Is there any reason that Java uses late/static binding for overloaded methods in the same class?-Collection of common programming errors

  • Efficiency. Single dispatch can be much more efficiently implemented by means of a virtual method table. To quote the Wikipedia article on Double dispatch:

    In a language supporting double dispatch, this is slightly more costly, because the compiler must generate code to calculate the method’s offset in the method table at runtime, thereby increasing the overall instruction path length.

  • Legacy. This is the behavior in languages that have inspired Java, such as C++, Smalltalk and Eiffel. At the very least, single dispatch follows the principle of least astonishment.

  • Complexity. The specification on how to determine which method to call is a quite interesting read. It is amazingly complex, and how to push the complexity over from the compiler to the JVM is by no means obvious. Consider for example the following snippet:

    class A {                          .--------------.
        void foo(Object o) {}          |      A       |
    }                                  '--------------'
                                              |
    class B extends A {                .--------------.
        void foo(Integer i) {}         |      B       |
    }                                  '--------------'
                                              |
    class C extends B {                .--------------.
        void foo(Number n) {}          |      C       |
    }                                  '--------------'
    

    now which method should be called here:

    A c = new C();
    Object i = new Integer(0);
    c.foo(i);
    

    According to the runtime type of the callee C.foo should be called while according to the runtime type of the argument B.foo should be called.

    One option would be to resolve this the same way as a call staticMethod(c, i) would be resolved in the presence of staticMethod(A, Object), staticMethod(B, Integer) and staticMethod(C, Number). (Note however that in this case neighter B.foo nor C.foo would be called, as suggested above.)

    Another option would be to choose the method primarily based on the type of the callee and secondarily based on the types of the arguments, in which case C.foo would be called.

    I’m not saying that it is impossible to pin down a well defined semantics, but I it would arguably make the rules even more complex, and possibly even counter-intuitive in some aspects. In the case of early binding, at least the compiler and/or IDE can aid the developer by giving guarantees of what will actually happen in runtime.