Matthias said:
class Base {
void m(Object o) {...}
}
class Derived extends Base {
void m(String s) {...}
}
It may help to think of it this way. The signature (types of the method
parameters) is part of the name of the method. In the above code there is /no/
connection between the two methods called "m". From the point of view of both
the compiler and the JVM the two methods are unrelated; exactly as unrelated as
if they were called m() and somethingElseEntirely().
public static void main(String[] argv) {
Base b = new Derived();
b.m("hello"); // calls Base::m
}
So when you call m() of an object statically declared to be of type Base, the
compiler generates bytecode which calls the method whose full name is:
void m(Object)
When the JVM executes that code, it looks for a method with full name:
void m(Object)
Since the only definition of a method with that full name is in Base, that's
the code that is called. If Derived defined a method with that full name, then
the JVM would call that code instead.
The method with full name
void m(String)
is simply irrelevant to all of this. It has a different name so the JVM
doesn't even /consider/ it as a potential override of Base.m(Object).
Perhaps you think, or would prefer, that the JVM should take the dynamic type
of the parameter into account. That's not unreasonable, and there are
languages which are defined to work like that (Lisp+CLOS for instance), but
that isn't how Java is defined. In Java, the types of parameters are
considered only at compile time (in order to discover the full name of the
method to call), and the only type that is considered at runtime is the type of
"this".
Now, /why/ is that ? The simple answer is "because that's how Java is
defined". And -- at bottom -- there is no better reason. As to why it was
defined like that, I don't know since I wasn't one of the designers (and I
haven't seen any comments on this issue by the designers). If /I/ had been
designing an OO language, then I would do the same as the Java people did,
since that would minimise the runtime complexity of the semantics. Java is a
dynamic language -- code can be loaded at runtime -- so I'd want to minimise
the interaction between loading new code (new classes) and the semantics of
/other/ code. That would both minimise the potential for confusing the
programmer and allow reasonably efficient implementations. (As far as I know,
implementation techniques for method calls which resolve on parameter types,
and which do not require complicated lookup at runtime, all rely on of
whole-program analysis -- which is not feasible, or at least extremely
difficult, for Java).
-- chris