Skip to content
Advertisement

java polymorphism aliasing issue

If there’s 3 classes. A, B and C. class B extends A and class C extends B.

class A has equals method:

public boolean equals(A other)
{...}

class B has equals method:

public boolean equals(B other)
{...}

and class C has euals method:

public boolean equals(Object other)
{...}

And the main has these code lines:

A a = new A();
C c = new C();
a=c;
System.out.println(a.equals(c));

I can’t understand why the equals method of class A is being executed.

I know that overloaded methods are bonded using static binding. But a points to the “C part of the object” after aliasing and there’s the method equals of class C. Why isn’t it the equals method of class C that will execute?

Advertisement

Answer

A method in a subclass overrides a method in the superclass only if the parameters have the same types.

The Object class defines an equals() method:

class Object {
    public boolean equals(Object obj) {...}
}

When you define class A, it inherits the equals routine from Object. You define a new equals, but the parameter type is different, so it doesn’t override the one in Object; instead, it becomes an overload. The result is that A has two overloaded equals methods:

class A {
    public boolean equals(Object obj) {...}  // inherited
    public boolean equals(A other) {...}     // the one you wrote
}

Similarly, the equals in B won’t override either equals, so the result is three overloaded equals methods:

class B {
    public boolean equals(Object obj) {...}  // inherited from Object
    public boolean equals(A other) {...}     // inherited from A
    public boolean equals(B other) {...}     // doesn't override anything
}

In class C, the new equals method does override the one in Object, so there are still three equals methods:

class C {
    public boolean equals(Object other) {...}  // overrides the one in Object
    public boolean equals(A other) {...}       // inherited from A
    public boolean equals(B other) {...}       // inherited from B
}

Now, here’s your code:

A a = new A();
C c = new C();
a=c;
System.out.println(a.equals(c));

When you say a.equals(c), the compiler sees that a has type A. Therefore it looks at the methods in A to see which one to execute. (The compiler doesn’t know that a will have type C at run time; therefore, it won’t look at the methods in C.)

There are two methods to choose from, as shown above:

    public boolean equals(Object obj) {...}  // inherited
    public boolean equals(A other) {...}     // the one you wrote

Both of them could be used on the parameter c, since c is an Object and it is an A. In that case, when one parameter is a subclass of the other, the compiler chooses the “closest” one, in essence. C is only two subclasses away from A, and it’s three subclasses away from Object, so it chooses the overload with parameter A, which is the one you defined in A. And note that this equals method was never overridden. So it executes the code that you wrote in class A.

But suppose you had written:

System.out.println(a.equals((Object)c));

By casting c to an Object, you’re forcing the compiler to look at it as an Object. Now when it chooses between the overloads, it must choose the one with the Object parameter, because an Object cannot automatically be converted to an A (because not every Object is an A). Thus, it would choose the inherited method. And since, at run time, the object actually is of class C, and since class C has an equals method that overrides the one in Object, in this case it would execute the code written in class C.

Your code is a nice example for demonstrating how overloading and overriding work. In real life, however, it’s a bad idea to write an equals() method whose parameter is anything other than Object, because it won’t override and it could lead to confusion. Also, it’s a good practice to put @Override on any method that you think will override a method in a superclass. That way, if you goof and use the wrong parameters, the compiler will catch it before you get a run-time bug that could be very difficult to track down.

Advertisement