Lets say I have two classes A
and B
. B
inherits from A
and B
has the following methods:
public boolean equals(Object other) {
System.out.print("Object");
return true;
}
public boolean equals(A other){
System.out.print("A object");
return true;
}
public boolean equals(B other) {
System.out.print("B object");
return true;
}
A a1 = new A();
A ab = new B();
B b1 = new B();
what is unclear to me is why
ab.equals(a1)
ab.equals(b1)
Returns Object
ab
is an instance of B
with pointer of A
. a1
is clearly both instance and pointer of A
. So Why does it use the Object other
instead of A other
method?? same goes for b1
which is an instance of B
with pointe B
yet the compiler chose to apply the equals method like I inserted regular object?? AM i that stupid or is this language incoherent?
BTW A
doesn't have any equals
methods at all.
BTW
A
doesn't have anyequals
methods at all.
But you do
ab.equals(a1)
ab.equals(b1)
And ab
is:
A ab = new B();
So while it is actually a B
, the variable is of type A
.
Javas rules for choosing methods in such situations will start looking for an equals
method in the A
class. And it actually has one, the default implementation inherited from Object
. It has the signature
public boolean equals(Object other)
So it decides for this signature. Now it asks the actual instance behind ab
, which is of type B
, for a method with that signature. It chooses the overriden implementation in B
:
public boolean equals(Object other) {
System.out.print("Object");
}
And consequentially prints Object
in both cases.
The details for this are defined in JLS§15.12. Method Invocation Expressions. It talks about finding the most specific match. The rules can get quite complex if you dive deep into it.
Some excerpts:
The first step in processing a method invocation at compile time is to figure out the name of the method to be invoked and which class or interface to search for definitions of methods of that name.
The second step searches the type determined in the previous step for member methods. This step uses the name of the method and the argument expressions to locate methods that are both accessible and applicable, that is, declarations that can be correctly invoked on the given arguments.
There may be more than one such method, in which case the most specific one is chosen. The descriptor (signature plus return type) of the most specific method is the one used at run time to perform the method dispatch.
The class or interface determined by compile-time step 1 (§15.12.1) is searched for all member methods that are potentially applicable to this method invocation; members inherited from superclasses and superinterfaces are included in this search.
If more than one member method is both accessible and applicable to a method invocation, it is necessary to choose one to provide the descriptor for the run-time method dispatch. The Java programming language uses the rule that the most specific method is chosen.