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.