Parametric covariance in class hierarchies

M

Matthias Kaeppler

Hello,

can someone explain why in this code fragment always the base class
method is called?

class Base {
void m(Object o) {...}
}

class Derived extends Base {
void m(String s) {...}
}

....

public static void main(String[] argv) {
Base b = new Derived();
b.m("hello"); // calls Base::m
}

Of course, Base::m qualifies for the invocation with a string, but
Derived::m does as well (maybe even more). Is this some sort of "call
first match" principle, or is there more to it? Shouldn't the compiler
come to the conclusion that Derived::m is the better match for the
invocation of m()?

Thanks,
Matthias
 
R

Roedy Green

can someone explain why in this code fragment always the base class
method is called?

Let me start by first converting your snippet to as SSCCE so that
anyone skeptical of your claim can run the code:


public class Base
{
void m( Object o )
{
System.out.println( "base:" + o.toString ());
}
public static void main(String[] argv)
{
Base b = new Derived();
b.m( "hello" ) ; // calls Base::m
}
}

class Derived extends Base
{
void m( String s )
{
System.out.println( "derived:" + s );
}
}

It indeed runs the base class version.. Why?

Java has to make its decision which method to use at COMPILE time.
This is not Nice that delays the decision to run time. What
information does JavaC have? Only that b is of type Base, and hence
has only ONE method m( Object). It has no choice. b is a Base, Even
though for NOW you have set it to a Derived, there is no guarantee you
won't later set it to a pure Base.

If you changed your code to read:

Derived b = new Derived();

Then you will run the better fit derived version.
 
T

Torkel Franzen

Roedy Green said:
Java has to make its decision which method to use at COMPILE time.

More precisely, the compiler decides on the signature of the
method. If the class Derived had a method overriding m in Base, that
method would be used at runtime.
 
M

Matthias Kaeppler

Roedy said:
Java has to make its decision which method to use at COMPILE time.
This is not Nice that delays the decision to run time. What
information does JavaC have? Only that b is of type Base, and hence
has only ONE method m( Object). It has no choice. b is a Base, Even
though for NOW you have set it to a Derived, there is no guarantee you
won't later set it to a pure Base.

Why? If I was to override m() in Derived, the decision would be made at
run-time, so why not decide at run-time here, too?

Regards,
Matthias
 
C

Chris Uppal

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
 
S

Stefan Schulz

Matthias said:
Why? If I was to override m() in Derived, the decision would be made at
run-time, so why not decide at run-time here, too?

It does not make any decision which method to call at run-time. At
least not as far as the bytecode language is concerned. It will always
call void m(Object). However, what method is actually hiding behind the
entry for m(Object) in that objects method table may be different.
 
R

Roedy Green

More precisely, the compiler decides on the signature of the
method. If the class Derived had a method overriding m in Base, that
method would be used at runtime.
exactly. Java has compile time matching of methods with slightly
different signatures, but the same name. It has run time selecting of
methods with identical signature in subclasses.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,766
Messages
2,569,569
Members
45,043
Latest member
CannalabsCBDReview

Latest Threads

Top