Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there any reason that Java uses late/static binding for overloaded methods in the same class?

Is there any specific reason why Java uses early binding for overloaded methods? Wouldn't it be possible to use late binding for this?

Example:

public class SomeClass {

    public void doSomething(Integer i) {
        System.out.println("INTEGER");
    }

    public void doSomething(Object o) {
        System.out.println("OBJECT");
    }

    public static void main (String[] args) {
        Object i = new Integer(2);
        Object o = new Object(); 
        SomeClass sc = new SomeClass();
        sc.doSomething(i);
        sc.doSomething(o); 
    } 
}

Prints: OBJECT OBJECT

I would rather expect: INTEGER OBJECT

like image 707
Korgen Avatar asked Oct 07 '10 15:10

Korgen


1 Answers

Korgen, I agree with you that such feature could potentially be quite useful, which is why I found this question very interesting.

To be clear; When executing this snippet of code:

Object i = new Integer(2);
SomeClass sc = new SomeClass();
sc.doSomething(i);

the JVM would be supposed to see that i is of runtime type Integer and execute the code as if it had executed the following:

Integer i = new Integer(2);         // Notice the different static type.
SomeClass sc = new SomeClass();
sc.doSomething(i);

(Note that I'm not going into how this should be done internally, I'm just pinning down the hypothetical semantics desired in this particular situation.)

Background: (Korgen, you can skip this) Selecting which function to call is sometimes referred to as "dispatch". When taking a single type into account (the type of o in o.m()) when deciding which function to call, it is called single dispatch. What we're after here is called multiple dispatch since we would decide which function to call based on multiple types (i.e. both the runtime type of the callee and the runtime type of the arguments).

Possible reason for not including support for multiple dispatch in Java:

  1. 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.

  2. 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.

  3. 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.

like image 98
aioobe Avatar answered Nov 07 '22 03:11

aioobe