I am practicing for a test and I came across this exercise about overloading and static and dynamic binding. The output of the following code is asked:
class Moe {
public void print(Moe p) {
System.out.println("Moe 1");
}
}
class Larry extends Moe {
public void print(Moe p) {
System.out.println("Larry 1");
}
public void print(Larry l) {
System.out.println("Larry 2");
}
}
class Curly extends Larry {
public void print(Moe p) {
System.out.println("Curly 1");
}
public void print(Larry l) {
System.out.println("Curly 2");
}
public void print(Curly b) {
System.out.println("Curly 3");
}
}
class Overloading {
public static void main (String [] args) {
Larry stooge1 = new Curly();
Moe stooge2 = new Larry();
Moe stooge3 = new Curly();
Curly stooge4 = new Curly();
Larry stooge5 = new Larry();
stooge1.print(new Moe());
stooge1.print(new Curly());
stooge1.print(new Larry());
stooge2.print(new Curly());
stooge3.print(new Curly());
stooge3.print(new Larry());
stooge5.print(new Curly());
}
}
I think I get the first one but on the others I am completely lost. This is how I solved the first one:
at runtime the type of stooge1
is Curly
, so we're calling the print method of Curly. Because we pass an object of type Moe
to print, the corresponding print method with argument type Moe
is run in Curly
. The output of this method is Curly 1
, the correct answer.
However, when I apply this technique to the following lines I end up with the wrong answers. Can someone explain me how exactly this concept works in Java?
The correct output of the code is:
Curly 1
Curly 2
Curly 2
Larry 1
Curly 1
Curly 1
Larry 2
Static binding happens at compilation time and dynamic binding at runtime.
Static binding is responsible for selecting signature (name and argument types) of method which should be executed. It uses
Compiler selects signature from variable type on which method is invoked, so
Object o = "abc";
will not allow you to invokeo.substring(1,2);
because compiler will not be able to findsubstring(int, int)
signature inObject
class (which is type ofo
variable on whichsubstring
method was invoked).Dynamic binding is responsible for finding and invoking code of method selected by static binding at compilation time. It will try to find code of method in type of actual instance held by variable. In other words if you have
Animal a = new Cat(); a.makeSound();
you can expect to get as result"Mew"
because at runtime JVM will search and invoke code ofmakeSound
starting fromCat
class. If implementation will not be provided in that class JVM will search for it in ancestor(s) until it finds one from which it was inherited.I renamed classes and variables in your example a little to hopefully make it more readable:
(variable naming -> variable of type
X
holding instance of typeY
is namedxy
).So, when we execute
print
method signature available in classB
which can handle instance of typeA
. In this case it will beprint(A)
.C
(since this is type of instance held bybc
variable) which means we will seeC.print(A)
.Similarly in case of
bc.print(new C());
print
method forC
argument available inB
class, which forC
isprint(B)
(since there is noprint(C)
there and B is closest supertype).C
class (since this is instance whichbc
holds).So it will invoke
C.print(B)
.For #1, #2, #3
stooge1
is declared aLarry
, so only methods available toLarry
can be called.Passing a
Moe
will callprint(Moe)
. Since the actual class is aCurly
, it prints "Curly 1".Passing a
Larry
will callprint(Larry)
, since that is a better match thatprint(Moe)
. This will print "Curly 2".Passing a
Curly
will also callprint(Larry)
. Note thatprint(Curly)
is unknown tostooge1
, so it cannot be selected by the compiler. Therefore it also prints "Curly 2".Now try to figure out the rest.
Here is what's going on:
The remaining three cases can be solved by applying the same logic as above.
So this is a super confusing and awful example of something you should never do. The declared type of the variables matters for what signature methods have. So
Larry
doesn't have method that accepts aCurly
, so the compiler considers the argument aLarry
. But it gets dispatched toCurly
's version of the method.So yeah, never ever do this =\