2

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.

bm1125
  • 223
  • 1
  • 10
  • https://stackoverflow.com/questions/9759141/overloading-in-java-and-multiple-dispatch – Dinari Feb 23 '20 at 12:40
  • 1
    Shouldn't that give a compiler error because there is no `return` in the methods? – dan1st Feb 23 '20 at 12:48
  • Prefer to post a **full** example. Your code does not compile and because it does not clearly show the class-setup people might interpret it wrong. See [mcve]. – Zabuzard Feb 23 '20 at 12:50

2 Answers2

3

Explanation

BTW A doesn't have any equals 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.


Details

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.

Zabuzard
  • 25,064
  • 8
  • 58
  • 82
2

The setup

Given a class A and a class B extends A, overriding and overloading equals(...) with different parameter types as follows:

class A {}

class B extends A {
    public boolean equals(Object other) {
        System.out.println("Object");
        return true;
    }
    
    public boolean equals(A other) {
        System.out.println("A object");
        return true;
    } 

    public boolean equals(B other) {
        System.out.println("B object");
        return true;
    }
}

The question

Given the following code

A a1 = new A();
A ab = new B();
B b1 = new B();

ab.equals(a1);
ab.equals(b1);

Why is "Object" printed out for both calls?

The explanation

The signature of method called is determined through the static type of the receiver and the static type of the parameters (as described in JLS, §15.12.3). Furthermore, through dynamic dispatch, a child-class override of a method may be executed.

If we analyze the type of ab (the receiver of the call), we find it to be A. There is only one method called equals(...) wihtin A, which is equals(Object) (inherites from Object). the parameters (a1 and b1 respectively) are assignable to Object, so this method is called.

Through dynamic dispatch, equals(Object) in B is actually executed and "Object" is printed out for both calls.

A full example is available on Ideone

Turing85
  • 18,217
  • 7
  • 33
  • 58