Search code examples
javapolymorphismruntimelate-binding

Java Polymorphism Exercise


Can someone please explain how the compiler/runtime runs the appropriate method in the example? There are 6 classes and a method void m(/* ... */) with different parameters. I know that the compiler analyses the declared types. The output is always "M" from the Middle class.

public class All {}
public class Most extends All {}
public class Special extends Most {}

public class Top {
    public void m( All p )     { System.out.println("A"); }
}
public class Middle extends Top {
    public void m( All p )     { System.out.println("M"); }
    public void m( Special p ) { System.out.println("L"); }
}
public class Bottom extends Middle {
    public void m( Most p ) { System.out.println("V"); }
    public void m( Special p ) { System.out.println("X"); }
}

public class Main {

  public static void main(String[] args) {
    All all = new All();
    Most most = new Most();
    Special special = new Special();

    Top x = new Middle();
    Top y = new Bottom();
    Middle z = new Bottom();

    x.m( most );     // Output is M
    x.m( special );  // Output is M
    y.m( all );      // Output is M
    y.m( special );  // Output is M
    z.m( all );      // Output is M
    z.m( most );     // Output is M
  }
}

Solution

  • Method overriding is resolved at runtime, by looking at what the runtime type of the object. The compiler also decides which method to call, but it can only decide based on the compile time type of the expression.

    For the two calls on x, they both resolve to Top.m(All) at compile time. x is of compile time type Top, so the compiler can only look for methods declared in Top and its superclasses. The compiler finds the only available method is m(All). At runtime, the method to call is resolved to Middle.m(All). This is because the runtime type of x is actually Middle, so the runtime will call the overridden m(All)in Middle. Why didn't it call Middle.m(Special)? The compiler has already decided that Top.m(All) should be called. The runtime will only check if the runtime type has overridden that. The compiler does not know there is a Middle.m(Special) because x is of compile time type Top.

    The two calls on y is similar. y's compile time type is still Top, so the compiler resolves the method to be Top.m(All). y is of runtime type Bottom. Since Bottom inherits from Middle, it also overrides Top.m(All). The implementation is the same as in Middle. The overridden method is thus called at runtime.

    The two calls on z is a bit different, but they still resolve to Middle.m(All) eventually. The compile time type of z is Middle, so both calls resolve to Middle.m(All). Note that there is no Middle.m(Most), so the call z.m(most) will still resolve to Middle.m(All). At runtime, the method is still resolved to Middle.m(All) because the runtime type Bottom does not override Middle.m(All).