如何在JAVA中使用带有过载的Double Dispatch?

时间:2018-01-11 18:02:42

标签: java design-patterns double dispatch concept

我正在尝试在JAVA中创建双重调度以使用重载方法。

public abstract class ComposantOrdi {
    protected void equiv(ComposantOrdi c){
        Equivalence.equiv(this, c);
    }
}

public class Montage extends ComposantOrdi{
    protected void equiv(Montage montage){
        Equivalence.equiv(this, montage);
    }
}

public class Equivalence {
    public static void equiv(Montage m, ComposantOrdi c){
        System.out.println("Montage - ComposantOrdi");
    }

    public static void equiv(Montage m, Montage c){
        System.out.println("Montage - Montage");
    }

    public static void equiv(ComposantOrdi m, ComposantOrdi c){
        System.out.println("ComposantOrdi - ComposantOrdi");
    }
}

对于示例,我创建了两个对象

Montage m2 = new Montage();
ComposantOrdi m3 = new Montage();

m3.equiv(m2);
m3.equiv(m3);
m3.equiv((Montage)m3);

结果是:

ComposantOrdi - ComposantOrdi
ComposantOrdi - ComposantOrdi
ComposantOrdi - ComposantOrdi

但我想使用Montage类的重载方法 得到这样的东西:

Montage - Montage
Montage - Montage
Montage - Montage

我可能不理解双重发送,但你能告诉我我做错了吗?

2 个答案:

答案 0 :(得分:4)

您在代码中执行的操作是单个调度(不是调度 - 下面的说明),因为在运行时只评估一个具体的子类型会影响结果(对象将自己传递为this)。

此外,ComposantOrdiMontage提供了不同的方法(检查参数:ComposantOrdi获取ComposantOrdi,而Montage获取Montage)。如果您调用超类“equiv方法,则无法获得 Montage - Montage 等结果,因为每个传递的Montage都会隐式投放到ComposantOrdi。就像调用超类'方法一样,结果的第二部分不能是 ComposantOrdi

此外,如果您在

中调用超类'方法关键字this
  

Equivalence.equiv(this,c);

由超类触发,这意味着即使对象是Montage,它也会将自身传递为ComposantOrdi

所以:调用超类'方法只能生成输出 ComposantOrdi - ComposantOrdi

对于像 Montage - Montage 这样的输出,您需要调用子类'方法equiv,这意味着:

  • 您需要将equiv来电的接收方称为Montage(请参阅主要内容中的m2,而不是m3
  • 您必须传递Montage对象

因为在this中触发了关键字Montage,所以输出的第一部分必须是 Montage 。第二部分也是 Montage ,因为如果不是必要的话,Java不会隐式地转换对象。这就是为什么选择带有Montages作为参数的重载方法的原因。

所以:调用子类'方法只能生成输出蒙太奇 - 蒙太奇

永远不会调用等价的equiv方法的混合版本。

解释双重调度机制:

我认为双重调度机制存在根本性的误解。所以让我解释一下。在继续使用双重调度机制之前,我将开始解释单个调度。

单一调度: 单个调度意味着您调用对象的方法,并且执行的行为(方法调用的结果)取决于对象的具体类型。通常这称为多态,并使用抽象方法或方法覆盖来实现。

假设你有一个类系统,如下图所示。 Client包含Shapes列表。要绘制所有这些内容,它会遍历列表并在每个paint上调用Shape。当班级Triangle在屏幕上绘制一个三角形时,Square描绘一个正方形。通过不考虑Shapes是哪个具体子类型,执行行为是在运行时确定的。

换句话说,paint方法调用的结果取决于Shape接口的派生的具体子类型。 paint方法的调用是单个分派,因为只有一个具体子类型确定在运行时执行的行为。

enter image description here

DOUBLE DISPATCH:使用 double 分派机制,执行的行为在运行时受两个具体子类型的影响。像这样,你跨越一个动态评估的行为矩阵。

如果根据下图修改类系统,客户端触发的绘制机制依赖于两个对象 - ShapePainter。客户端调用paint方法并注入其Painter。然后,Shape将自己作为this的一个重载方法中的Painter传递。

像这样,Client会触发绘制机制而不知道绘制什么颜色的形状。例如,如果其画家为GreenPainterShapeTriangle,则屏幕上会显示绿色三角形。

在此示例中,双调度机制由Shapes'paint方法的调用触发。

您应该查看使用双重调度机制的访客模式

enter image description here enter image description here

答案 1 :(得分:1)

一般JVM只使用单一调度:运行时类型仅考虑接收者对象;对于方法的参数,考虑静态类型。

如果您希望多次调度参数,并希望坚持使用Java,请查看MultiJava

如果您的目标是坚持普通的,普通的java,请阅读其他设计模式,例如,策略,访客,观察者。这些通常可以解决与多次调度相同的问题