0

In lectures we were shown this code and told that it creates double dispatch but why does it not create an infinite loop?

If c3po.greet(c4po); calls the TranslationRobot method from TranslationRobot

Why does c5po.greet(c4po); call the AbstractRobot method in CarrierRobot and not the TranslationRobot method and then not call the AbstractRobot method in TranslationRobot which would then call the Abstract Method in CarrierRobot and so on?

What decides whether it calls an AbstractRobot method or not?

AbstractRobot.java

abstract class AbstractRobot extends Robot { 
abstract void greet(AbstractRobot other);
abstract void greet(TranslationRobot other);
abstract void greet(CarrierRobot other);
}

CarrierRobot.Java

class CarrierRobot extends AbstractRobot {
...
void greet(TranslationRobot other) {
    talk("'Hello from a TranslationRobot to a CarrierRobot.'"); }
void greet(CarrierRobot other) {
    talk("'Hello from a CarrierRobot to another.'"); }
void greet(AbstractRobot other) {
    other.greet(this); 
}}

TranslationRobot.Java

public class TranslationRobot extends AbstractRobot {
...
void greet(TranslationRobot other) {
    talk("'Hello from a TranslationRobot to another.'"); }
void greet(CarrierRobot other) {
    talk("'Hello from a CarrierRobot to a TranslationRobot.'"); }
void greet(AbstractRobot other) {
    other.greet(this);
} } 

DispatchWorld.Java

class DispatchWorld {
public static void main (String[] args) {
AbstractRobot c3po = new TranslationRobot();
AbstractRobot c4po = new TranslationRobot();
AbstractRobot c5po = new CarrierRobot();
AbstractRobot c6po = new CarrierRobot();
c3po.greet(c4po);
c5po.greet(c4po);
c4po.greet(c5po);
c5po.greet(c6po);
} }

This produces the output:

Standard Model says 'Hello from a TranslationRobot to another.'
Standard Model says 'Hello from a CarrierRobot to a TranslationRobot.'
Standard Model says 'Hello from a TranslationRobot to a CarrierRobot.'
Standard Model says 'Hello from a CarrierRobot to another.'
Tristan Warren
  • 435
  • 1
  • 7
  • 17
  • 2
    Possible duplicate of [Java method overloading + double dispatch](http://stackoverflow.com/questions/2794195/java-method-overloading-double-dispatch) – John Bollinger Feb 27 '17 at 15:02
  • The choice between overloads of a method name is made at compile time, based on the *static* types of the method arguments. This is perhaps the single most important thing to understand when it comes to implementing double dispatch in Java. – John Bollinger Feb 27 '17 at 15:05
  • I'd disagree that this is a duplicate question, the other has a parent class that the method is called from and asks why 'this' refers to the parent and no the child. Whereas here I am asking why one method is chosen over another in the same class and why the argument is abstract or concrete – Tristan Warren Feb 27 '17 at 15:32
  • Though the dupe target differs in some details, it is fundamentally the same question: how is a specific overloaded method bound to a method invocation in double dispatch? The accepted answer to the dupe target addresses that question very well, answering both that version of the question and this one. – John Bollinger Feb 27 '17 at 15:58
  • The method that runs is the method from the runtime type of the object whose method you call. `c3po.greet` is the method from `TranslationRobot` because that's the type of the object. ` c5po.greet(c4po);` does not "call the AbstractRobot method in CarrierRobot", it calls `CarrierRobot#greet`. Because that's the type of the object. How would it call a method from `TranslationRobot`? That's not the type of the object nor of its variable. So how could such a cycle as you describe even come to be? – Lew Bloch Feb 27 '17 at 16:02
  • But when c5po.greet(c4po) is called it prints to the console "Hello from a Carrier Robot to A Translation Robot" so it must run the method from the the TranslationRobot class. This is what is confusing me – Tristan Warren Feb 27 '17 at 16:53
  • I should probably put the console output in the question – Tristan Warren Feb 27 '17 at 16:53

1 Answers1

0

I think that the answer why this works and why there is no infinite recursion might be illustrated better if we refactor code a bit by removing all method overloading and putting explicit different names instead:

abstract class Robot
{
    void talk(String msg)
    {
        System.out.println(msg);
    }
}

abstract class AbstractRobot extends Robot
{
    abstract void greet(AbstractRobot other);

    abstract void greetFromTranslationRobot(TranslationRobot other);

    abstract void greetFromCarrierRobot(CarrierRobot other);
}

class CarrierRobot extends AbstractRobot
{
    void greetFromTranslationRobot(TranslationRobot other)
    {
        talk("'Hello from a TranslationRobot to a CarrierRobot.'");
    }

    void greetFromCarrierRobot(CarrierRobot other)
    {
        talk("'Hello from a CarrierRobot to another.'");
    }

    void greet(AbstractRobot other)
    {
        other.greetFromCarrierRobot(this);
    }
}


public class TranslationRobot extends AbstractRobot
{
    void greetFromTranslationRobot(TranslationRobot other)
    {
        talk("'Hello from a TranslationRobot to another.'");
    }

    void greetFromCarrierRobot(CarrierRobot other)
    {
        talk("'Hello from a CarrierRobot to a TranslationRobot.'");
    }

    void greet(AbstractRobot other)
    {
        other.greetFromTranslationRobot(this);
    }
}

From the compiler's point of view methods void greet(AbstractRobot other), void greet(TranslationRobot other) and void greet(CarrierRobot other) are 3 obviously different methods which my renaming just highlights.

So a call like c3po.greet(c4po) is actually a call to TranslationRobot.greet which forwards it to other.greetFromTranslationRobot(this) which is TranslationRobot.greetFromTranslationRobot and which obviously should not result in any infinite recursion.

SergGr
  • 23,570
  • 2
  • 30
  • 51