30

关于钻石问题的维基百科:

“...菱形问题是当两个类 B 和 C 继承自 A,而类 D 继承自 B 和 C 时出现的歧义。如果 D 中的方法调用 A 中定义的方法(并且不覆盖该方法),并且 B 和 C 以不同的方式覆盖了该方法,那么它从哪个类继承:B 还是 C?”

所以钻石看起来像这样:

  A
 / \
B   C
 \ /
  D

我的问题是,如果没有这样的 A 类会发生什么,但 B 和 C 又声明了相同的方法,比如 foo()。这不是同样的问题吗?为什么又叫钻石问题?

例子:

class B {
    public void foo() {...}
}

class C {
    public void foo() {...}
}

class D extends B, C {
}

new D().foo();
4

4 回答 4

14

它不是同一个问题。

在原始问题中,可以从 A 调用覆盖的方法。在您的问题中,情况并非如此,因为它不存在。

在菱形问题中,如果类 A 调用方法 Foo,就会发生冲突。通常这没有问题。但是在 D 类中,您永远无法知道需要调用 Foo 的哪个实例:

         +--------+
         |   A    |
         | Foo    |
         | Bar    |
         +--------+
            /  \
           /    \
          /      \
+--------+        +--------+
|   B    |        |   C    |
| Foo    |        | Foo    |
+--------+        +--------+
          \      /
           \    /
            \  /
         +--------+
         |   D    |
         |        |
         +--------+

在您的问题中,没有可以调用该方法的共同祖先。在 D 类中,您可以选择两种口味的 Foo,但至少您知道有两种。您可以在两者之间做出选择。

+--------+        +--------+
|   B    |        |   C    |
| Foo    |        | Foo    |
+--------+        +--------+
          \      /
           \    /
            \  /
         +--------+
         |   D    |
         |        |
         +--------+

但是,一如既往,您不需要多重继承。您可以使用聚合和接口来解决所有这些问题。

于 2010-01-14T14:49:53.597 回答
11

在菱形问题中,D 类隐式继承了 A 类的虚方法。要调用它,D 类会调用:

A::foo()

如果 B 类和 C 类都覆盖了这个方法,那么问题就出在哪个实际被调用了。

但是,在您的第二个示例中,情况并非如此,因为 D 类需要明确说明被调用的内容:

B::foo()
C::foo()

所以问题实际上并不相同。在菱形问题中,您不是引用派生类,而是它们的基类,因此存在歧义。

反正我是这么理解的。

请注意,我来自 C++ 背景。

于 2010-01-14T14:57:18.523 回答
0

您的第二个示例与钻石问题相去甚远,因为编译器能够检测到继承的上一级可用函数。

一旦编译器知道你在两个基类中使用了同名函数,它就会抛出error: member 'foo' found in multiple base classes of different types.

于 2019-10-07T04:11:46.313 回答
0

钻石问题仅由不正确的类型/本体建模引起。它不存在于正确建模的系统中。

问题的出现通常是因为开发人员根据实体的角色而不是类型为实体定义类。他们将它可以做的事情提升到实际水平之上。实体对象带有角色而不是类型。

例如,一个人是一个人,从出生到死亡,无论他是否结婚(Husband)、探望妈妈(Son)或找到一份好工作(Employee)。的角色Person可以使用接口或组件或 mixins 来实现——参数中的燃料更倾向于组合而不是继承

允许通过某种状态机转换来替换实体的类型是合理的。因此,作为状态转换的结果,aPerson可以替换为Werewolf(满月)或(颈部咬合)。无论转换如何,Vampire实体(一些固定的)都会保持不变。GUID但即使这样也可以用不同的方式建模——一个PersonwithLycanthropy和/或Vampirism. 当然,建模以适应许多方面可能是一个挑战,但这与类可能参与单个继承链的典型限制无关。

An UnverifiedEmailAddress could transition to a VerifiedEmailAddress, but this is not a great example because it probably ought use a value object rather than a reference object. A better example could involve a WorkingPurchaseOrder transitioning to a SubmittedPurchaseOrder via a submit function. The function would likely return an immutable replacement (such as a Persistent Object) and be swapped into some observable state container.

于 2021-08-27T20:59:18.553 回答