views:

632

answers:

3

This is a purely theoretical question.

Given three simple classes:

class Base {
}

class Sub extends Base {
}    

class SubSub extends Sub {
}

And a function meant to operate on these classes:

public static void doSomething(Base b) {
  System.out.println("BASE CALLED");
}
public static void doSomething(Sub b) {
  System.out.println("SUB CALLED");
}

It seems that the followign code:

SubSub ss = new SubSub();
doSomething(ss);

could legitimately result in printing either BASE CALLED, or SUB CALLED, since SubSub can be casted to both of those. In fact, removing the Sub version of the function causes BASE CALLED to be printed. What actually happens is that "SUB CALLED" is printed. This seems to mean that which function is called doesn't depend on the order the functions are defined in, as the Base version was called first.

Does Java just look at all the different versions of the function and pick the one which requires the smallest traversal up the inheritance stack? Is this standardized? Is it written out in any documentation?

A: 

As far as I know, Java and C++ make this decision at compilation time (since these are static functions that are not dynamically dispatchable) based on the most specific matching that they can make. If your static type is SubSub and you have an overload that takes SubSub, this is the one that will be invoked. I'm fairly sure it's in both standards.

If you have a reference or pointer to Base, even if it contains a Sub or a SubSub, you will match the version that takes a Base because at compile time, that is the only assurance that the compiler has.

Uri
This is not dependent on the functions being static. Java (and C++) only do dynamic dispatch on the object itself (on the left of the .), not the other parameters.
Rolf Rander
I'm aware of that, but I think that a method being static is a more general rule than even bothering to look at the rest of the signature so I went with that one first.
Uri
A: 

When you have overloaded static methods then it calls that method that is defined immediately in the class which is invoking the method. If however, no method is defined in the calling class then it will invoke the method inherited from its immediate parent class.

In your case there are two overloaded methods both of which can accept SubSub as parameter. the compiler checks for the most specific match and goes for it. But the most specific match is generally the lowest in the type hierarchy.

EDITED

Removed the conflicting statement. Two methods in classes that are at the same type hierarchy level can't be in ambiguous state for the compiler to choose. This ambiguity is possible only in the case of multiple inheritance.

Tushu
Could you provide an example of your last sentence?
Claudiu
In your example if you make SubSub a subclass of Base then it will give doSomething() as an ambiguos method because then they would be at the same level.
Tushu
No, then it will call the Base version. For your first statement, "call to static method must be made in a static way".
Adeel Ansari
Its not about over-riding here. The question is not well phrased, I guess.
Adeel Ansari
+5  A: 

The formal specification can be found in part 15.12.2.5 of the Java Language Specification (JLS). Thanks to generics this is pretty complicated, so you might want to look at same section of the first edition of the JLS.

It basically says that the compiler tries to find a version of the method where all parameters including the object the method is called upon are most specific. If no such method exists (e.g. since you have method(Base, Sub) and method(Sub, Base) but not method(Sub, Sub)), then the compilation fails.

Note that the actual choice of method depends on the dynamic type of the target object for instance methods, but not for the parameters. Your example would still work the same on the instance level.

You should be able to give the compiler a helping hand by casting or redeclaring the type of ss. If the declared type of the variable matches a signature exactly then everything is clear for the compiler and maintenance programmers as well. It doesn't matter if you then assign a more specific type as long as the declared type matches.

Peter Becker
If I had a method of Base which took a Sub, and a method in Sub which took a Base, wouldn't I be able to call (new Sub()).method(new Sub()) ? I'm trying to understand the last sentence in your 2nd paragraph
Claudiu
You would be able to call it. Actually, here you messed up two different things. You should go one by one, first understand over-loading without entering into over-riding. And then understand how over-riding works. Don't mess both things together.
Adeel Ansari
oh, sorry, i thought you were turning this.method(Sub) into method(this, Sub) explicitly.
Claudiu