3
class A {
    public void doSomething(float f) {
        //print "in A"
    }
}

class B extends A {
    public void doSomething(int i) {
        // print "in B"
    }

    public static void main(String[] args) {
        A a = new B();
        a.doSomething(10);
    }
}

The compiler first checks if doSomething() is present in class A. Once confirmed, during runtime, the object type is checked and the corresponding method is executed that is doSomething() of class B should be executed but doSomething() of class A is executed instead.

If both the methods would be the same then only class B doSomething() would be executed. How does the compiler decide that a certain method is overridden and thus it will be taken care by the JVM?

ernest_k
  • 44,416
  • 5
  • 53
  • 99
  • 1
    related: [How does method overloading work in Java](https://stackoverflow.com/q/70898708/16320675) - the compiler does just *make* a virtual call to `A.doSomething(float)` (bytecode: `invokevirtual doSomething:(F)V` - it will execute `doSomething` of `A`, not of `B`- there is no overriding involved in posted code, `doSomething(int)` is not a subsignature of `doSomething(float)` – user16320675 Jan 29 '22 at 08:16

1 Answers1

6

To put it simply:

  1. The compiler picks the signature of the method that will be invoked
  2. The runtime picks which implementation of the compiler-selected signature will run

In step 1, the compiler looks at the declared type of the object on which the method is invoked, and also at the parameter types, which means:

  • the compiler only knows that a is A ("declared type", aka static type); it therefore can only search methods in A or methods inherited by A. This means that the compiler doesn't even search for doSomething in class B in your example.
  • it looks at the data types of arguments to resolve one method among overloads. In your example, this is not necessary as there is only one doSomething method (the one with a float parameter)

Based on the above, the compiler can only conclude that it's doSomething(float) that will run. You can look at it as if the compiler's selected method signature is written in stone, the runtime can't change that.

In step 2, the runtime knows which signature will run (doSomething(float)), the only thing it needs to do is pick which implementation of that signature will be invoked. For that, it looks at the actual object type (the object that was created), new B(), in your example. It will then run the implementation in B, if overridden, or search up the tree any overridden implementation of that exact signature until it gets to the inherited implementation in A. Because B does not override doSomething(float), the inherited implementation A.doSomething(float) from A runs.

ernest_k
  • 44,416
  • 5
  • 53
  • 99
  • Thank you so much for your help. Just to confirm it once: The compiler selects the method signature for every instance method and then it's up to the JVM to find a method of that signature in the class pointed by the object. However, the above statement only works for instance methods and not private, final and static as they cannot be overridden. – Karan Guleria Jan 29 '22 at 08:55
  • 1
    Instance methods can be private or final. This doesn't make a difference other than the respective effects of those modifiers. `private`: this answer applies assuming that the method is visible to the caller. If the method is private, it can't even be inherited, so that in itself prevents polymorphism. A `final` method simply can't be overridden, that changes nothing about polymorphism except that a final method prevents further overriding down the tree. Nothing else. As for static methods: the runtime makes no decision: the compiler picks signature and implementation based on declared type. – ernest_k Jan 29 '22 at 09:06