【问题标题】:How to use a Double Dispatch with overload in JAVA?如何在 JAVA 中使用具有重载的双重调度?
【发布时间】:2018-01-11 18:02:41
【问题描述】:

我正在尝试在 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

我可能不明白双重调度,但请你告诉我我做错了什么吗?

【问题讨论】:

  • 由于m3ComposantOrdi 类型,它只能调用protected void equiv(ComposantOrdi c) 方法。将其定义为Montage,您将获得不同的输出。

标签: java design-patterns double dispatch concept


【解决方案1】:

您在代码中所做的是 single 调度(不是 double 调度 - 解释如下),因为在运行时评估的只有一个具体子类型会影响结果(对象将自己传递为this)。

另外ComposantOrdiMontage提供不同的方法(检查参数:ComposantOrdi得到ComposantOrdiMontage得到Montage)。如果您调用超类的equiv 方法,则无法获得像 Montage - Montage 这样的结果,因为每个通过的Montage 都会隐式转换为ComposantOrdi。像这个调用超类的方法一样,结果的第二部分不能是 ComposantOrdi

此外,如果您将超类的方法称为关键字this in

等价.equiv(this, c);

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

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

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

  • 您需要将equiv 调用的接收者引用为Montage(请参阅您的主目录中的m2,而不是m3
  • 你必须传入一个Montage 对象

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

所以:调用子类的方法只能产生输出Montage - Montage

Equivalence 的 equiv 方法的混合版本永远不会被调用。


解释双重调度机制:

我认为对双重调度机制存在根本性的误解。所以让我解释一下。我会先解释一下单分派,然后再介绍双分派机制。

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

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

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

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

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

像这样Client 触发绘制机制,而不知道绘制什么样的形状以什么颜色。例如,如果它的画家是GreenPainter,而ShapeTriangle,那么屏幕上会出现一个绿色三角形。

在这个例子中,双重分派机制是由Shapes'paint方法的调用触发的。

你应该看看使用双重调度机制的访问者模式

【讨论】:

    【解决方案2】:

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

    如果您想为您的参数进行多次分派,并且想坚持使用 Java,请查看 MultiJava

    如果您的目标是坚持使用普通的 java,请阅读其他设计模式,例如 Strategy、Visitor、Observer。这些通常可以解决与多次调度相同的问题

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2023-03-10
      • 1970-01-01
      • 1970-01-01
      • 2021-08-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多