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:
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.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…