Confusion about upcasting and overloaded methods

804 Views Asked by At

Let's say, we have these two classes and one main method:

public class Super {
    public void f(double d){
        System.out.println("Super: f(double d)");
    }
    public void f(int i){
        System.out.println("Super: f(int i)");
    }
}

public class Sub extends Super {
    public void f(double d){
        System.out.println("Sub: f(double d)");
    }

    public void f(float f){
        System.out.println("Sub: f(float f)");
    }
}

public class M {
    public static void main(String[] args){
        Sub a = new Sub();
        a.f(1.5f); //output: "Sub: f(float f)"

        Super b = new Sub();
        b.f(1.5f); //output: "Sub: f(double d)"
    }
}

Why does the second call results in Sub: f(double d) and not in Sub: f(float f) like the first one?

When I add

public void f(float f) {
    System.out.println("Super: f(float f)");
}

to the Super class, the output changes to Sub: f(float f).

Given this behavior, I expect the workflow looks like this:

  1. As a is upcasted to Super, the class Super is checked for a matching method
  2. Only public void f(double d) is found, so the float is casted to a double
  3. Now the method public void f(double d) in the more specific class Sub is seen and executed

Is this correct?

2

There are 2 best solutions below

0
On BEST ANSWER

To understand this behavior, note that choosing which method to call out of a series of overloaded methods is always performed at compile-time. However, method dispatch is performed at runtime.

Therefore, at compile-time, only the double version is visible, but it will get dispatched to the subclass's overridden version at runtime.

0
On

The method b.f(1.5f) is looked up at compile time. Its implementation will be the more specific one (if present) at runtime but which overloaded method is chosen is bound at compile time.

Since b is at least a Super the only conclusion that the compiler can infer is that the method will be void f(double v) (since Super doesn't have a float overload) so that one is chosen.

Then at runtime the implementation is the one of Sub class, thanks to dynamic binding.