compile time and run time binding in method overloading and overriding

According to my understanding:

Private, final and static methods of a class follow compile time binding i.e. which method will be called is decided at compile time.

But, call to non-private instance method is decided at run-time.

This used to solve all my problems till now. But, I stuck in some problem where the above statements are not deriving the correct output.

Here is the code:

class Item
{
    Integer size;
    Item(Integer size)
    {
      this.size=size;
    }
    public boolean equals(Item item2) //not overriding the method of Object class
    {
        if(this==item2)
        return true;
        return this.size.equals(item2.size);
     }

  }

public class Test 
{
    public static void main(String[] args)
     {
        Item itemA= new Item(10);
        Item itemB= new Item(10);
        Object itemC=itemA;
        System.out.println("|"+ itemA.equals(itemB) + "|" + itemC.equals(itemB)+ "|");
     }

}

Output it is giving: |true|false|

Output I expected: |true|true|

Here, the equals method of class Item is not overriding the equals method of Object but overloading is taking place because of different method signature of equals method.

Question: In the call itemC.equals(itemB), why the equals method of Object class is getting called.

According to me: itemC is having object of class Item at run-time, therefore, equals of Item class should get called. At run-time there are two equals methods in Item class, one is its own and other one is inherited from Object class. So, equals(Item) should get called instead of equals(Object) because the call is for equals(Item).

What exactly am I missing conceptually?

Jon Skeet
people
quotationmark

Question: In the call itemC.equals(itemB), why the equals method of Object class is getting called.

Because the compile-time type of itemC is Object.

Overriding is performed at execution time based on the actual type of the target object, but overloading is performed at compile time based on the compile-time type of the target expression.

If you use javap -c -v Test you'll see the two method calls involved:

Constant pool:

...
#6 = Methodref #2.#32    //  Item.equals:(LItem;)Z
#7 = Methodref #16.#33   //  java/lang/Object.equals:(Ljava/lang/Object;)Z
...

Then the main method:

33: invokevirtual #6  // Method Item.equals:(LItem;)Z
...
40: invokevirtual #7  // Method java/lang/Object.equals:(Ljava/lang/Object;)Z

So that shows the signatures for the methods that are being called. Which implementation of that signature is executed depends on the execution-time type. So if you override equals(Object) in Item, then that override will be called for itemC.equals(itemA).

people

See more on this question at Stackoverflow