views:

457

answers:

3

I am practicing for an exam, and found a sample problem that gets me totally lost. For the following code, find what the output is:

class Moe {
    public void print(Moe p) {
        System.out.println("Moe 1\n");
    }
}
class Larry extends Moe {
    public void print(Moe p) {
        System.out.println("Larry 1\n");
    }
    public void print(Larry l) {
        System.out.println("Larry 2\n");
    }
}
class Curly extends Larry {
    public void print(Moe p) {
        System.out.println("Curly 1\n");
    }
    public void print(Larry l) {
        System.out.println("Curly 2\n");
    }
    public void print(Curly b) {
        System.out.println("Curly 3\n");
    }
}
public class Overloading_Final_Exam {
    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()); 
        ((Curly)stooge1).print(new Larry()); 
        ((Larry)stooge2).print(new Moe()); 
        stooge2.print(new Curly()); 
        stooge3.print(new Curly()); 
        stooge3.print(new Moe()); 
        stooge3.print(new Larry()); 
        ((Curly)stooge3).print(new Larry()); 
        ((Curly)stooge3).print(new Curly()); 
        stooge4.print(new Curly()); 
        stooge4.print(new Moe()); 
        stooge4.print(new Larry()); 
        stooge5.print(new Curly()); 
        stooge5.print(new Larry()); 
        stooge5.print(new Moe()); 
    }
}

I had my ideas in mind, but then when I ran the java, I got something totally different:

Curly 1
Curly 2
Larry 1
Larry 1
Curly 1
Curly 1
Curly 1
Curly 2
Curly 3
Curly 3
Curly 1
Curly 2
Larry 2
Larry 2
Larry 1

The first few ones are OK, but then I really don't understand. Anyone has a good explanation for this problem?

Thanks

A: 

A hint is to disregard the value on the left when looking at objects. Instead, look at the value of the right during the declaration, this is the actual value of the object.

samoz
+4  A: 

I would start by drawing a picture...

Moe - print(Moe)
 |
Larry - print(Moe), print(Larry)
 |
Curly - print(Moe), print(Larry), print(Curly)

Then I would keep track of the variables:

  • Larry - stooge1 -> Curly
  • Moe - stooge2 -> Larry
  • Moe - stooge3 -> Curly
  • Curly - stooge4 -> Curly
  • Larry - stooge5 -> Larry

  • stooge1.print(new Moe())

    • stooge1 -> Curly so calls Curly.print(Moe)
  • ((Curly)stooge1).print(new Larry());

    • stooge1 -> Curly so calls Curly.print(new Larry())
  • ((Larry)stooge2).print(new Moe());

    • stooge2 -> Larry so calls Larry.print(new Moe());
  • stooge2.print(new Curly());
    Ok, this is where it gets a bit trickier (sorry I stopped one before here)

    • stooge2 is declared to be a Moe. So when the compiler is looking at what to call it is going to call the print(Moe) method. Then at runtime it knows that stooge2 is a Larry so it calls the Larry.print(Moe) method.

etc...

Let me know if following that all the way through doesn't work out for you.

(updated to clarify the next one)

So the general rule is:

  • the compiler looks at the variable type to decide what method to call.
  • the runtime looks at the actual class that the variable is point at to decide where to get the method from.

So when you have:

Moe stooge2 = new Larry();
stooge2.print(new Moe());

the compiler says:

  • can Larry be assigned to stooge2? (yes as Larry is a subclass of Moe)
  • does Moe have a print(Moe) method? (yes)

the runtime says:

  • I am supposed to call the print(Moe) method on this here object... stooge2
  • stooge2 is point at a Larry.
  • I'll call the print(Moe) method in the Larry class.

Once you have worked all that out try getting rid of some of the methods and see how that changes things.

TofuBeer
the first 3 are straight-forward. If you could go on with the next 3 that would help. Thanks!
nute
the above changes should help.
TofuBeer
@TofuBeer - Not exactly correct. In a pure dynamic binding case stooge2.print(new Curly()) would print "Larry 2" not "Larry 1", since Curly extends Larry. (Actually stooge2.print(new Larry()) would have been a better example, since it would also print "Larry 1" instead of "Larry 2".
Robin
Which would make the error more obvious. The fact that overloaded methods are statically bound is why it is NOT called. Since in this case the method is dynamically bound, which only occurs with overridden methods.
Robin
Yes, stooge2.print(new Larry()) would be the same as stooge2.print(new Moe()) since Moe doesn't have a print(Larry) method. In Java there is no way, in the language, for stooge2.print(new Larry()) to call the Larry.print(Larry) method.
TofuBeer
+1  A: 

Actually, this problem is not as simple as it seems, since Java is both static and dynamically bound. You have to understand where each is applied before you will understand all the results you are getting from this exercise.

The General rule mentioned by TofuBeer is only correct in the dynamic binding case. In static binding, decisions are only made at compile time.

Your example mixes the dynamic binding (when methods are overridden) and static binding (when methods are overloaded).

Take a look at this question for more details.

Robin