如何在 JAVA 中使用带过载的双分派?

How to use a Double Dispatch with overload in JAVA?

我正在尝试在 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 的重载方法 class 得到这样的东西:

Montage - Montage
Montage - Montage
Montage - Montage

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

一般的JVMs只使用single dispatch:运行时类型只考虑receiver对象;对于方法的参数,考虑静态类型。

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

如果您的目标是坚持使用普通的香草 java,请阅读其他设计模式,例如 Strategy、Visitor、Observer。这些通常可以解决与 multiple dispatch

相同的问题

你在你的代码中做的是一个分派(不是分派——下面的解释)因为只有一个具体的子在运行时评估的类型会影响结果(对象将自身传递为 this)。

此外ComposantOrdiMontage提供了不同的方法(查看参数:ComposantOrdi获取ComposantOrdiMontage获取Montage) .如果调用 superclass' equiv 方法,则无法获得像 Montage - Montage 这样的结果,因为每个传递的 Montage 都被隐式转换到 ComposantOrdi。就像调用 superclass' 方法一样,结果的第二部分不能是 ComposantOrdi.

此外,如果您调用 superclass' 方法,

中的关键字 this

Equivalence.equiv(this, c);

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

所以:调用superclass'方法只能产生输出ComposantOrdi - ComposantOrdi.

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

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

因为关键字 thisMontage 中触发,输出的第一部分必须是 Montage。第二部分也是 Montage 因为 Java 不会在不需要时隐式转换对象。这就是为什么选择以 Montages 作为参数的重载方法。

所以:调用sub class'方法只能产生输出Montage - Montage.

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


解释双重调度机制:

我认为双重派遣机制存在根本性的误解。所以让我解释一下。在介绍双分派机制之前,我将开始解释单分派。

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

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

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

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

如果根据下图修改class系统,客户端触发的绘制机制依赖于两个对象——Shape和一个Painter。客户端调用 paint 方法并注入它的 Painter。然后 ShapePainter.

的重载方法之一中将自身作为 this 传递

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

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

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