34

Wikipedia on the diamond problem:

"... the diamond problem is an ambiguity that arises when two classes B and C inherit from A, and class D inherits from both B and C. If a method in D calls a method defined in A (and does not override the method), and B and C have overridden that method differently, then from which class does it inherit: B, or C?"

So the diamond looks like this:

  A
 / \
B   C
 \ /
  D

My question is, what happens if there is no such class A, but again B and C declare the same method, say foo(). Isn't this the same problem? Why is it then called diamond problem?

Example:

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

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

class D extends B, C {
}

new D().foo();
Valentin Rocher
  • 11,667
  • 45
  • 59
cretzel
  • 19,864
  • 19
  • 58
  • 71
  • what language are you asking about? –  Jan 14 '10 at 14:49
  • 10
    @Neil Butterworth language should not matter in this as this is more a concept issue. Languages like C++ allow this but Java and C# does not. – Vincent Ramdhanie Jan 14 '10 at 14:50
  • And this is why "multiple inheritance" is a dirty word... – Danail Jan 14 '10 at 15:01
  • 3
    In short: The "triangle" can only fail at compile-time (and can be fixed by being more explicit at the call-site), but the "diamond" can fail at run-time because of `A* a = new D();` followed by `a->foo();`. – AnorZaken Jul 31 '16 at 04:05

4 Answers4

14

Its not the same problem.

In the original problem, the overriden method can be called from A. In your problem this can't be the case because it does not exist.

In the diamond problem, the clash happens if class A calls the method Foo. Normally this is no problem. But in class D you can never know which instance of Foo needs to be called:

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

In your problem, there is no common ancestor that can call the method. On class D there are two flavors of Foo you can chose from, but at least you know that there are two. And you can make a choice between the two.

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

But, as always, you do not need multiple inheritance. You can use aggegration and interfaces to solve all these problems.

whoan
  • 8,143
  • 4
  • 39
  • 48
Toon Krijthe
  • 52,876
  • 38
  • 145
  • 202
  • 4
    The Wikipedia article talks about calling foo() from D, though. And what about calling foo on a D from outside D like new D().foo() (also in my example)? – cretzel Jan 14 '10 at 15:16
11

In the diamond problem, class D implicitly inherits the virtual method from class A. To call it, class D would call:

A::foo()

If both classes B and C override this method, then the problem comes of which actually gets called.

In your second example however, this isn't the case as class D would need to explicitly state which was being called:

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

So the problems are not actually the same. In the diamond problem you aren't referencing the derived classes, but their base class, hence the ambiguity.

That's how I understand it, anyway.

Note that I'm coming from a C++ background.

icabod
  • 6,992
  • 25
  • 41
  • I don't know C++, but wouldn't it be the same problem if you'd call foo from outside D, say new D().foo()? Then the example without A would also be problematic, right? – cretzel Jan 14 '10 at 15:01
  • @cretzel, yes, in that case you also have a name clash problem. – Toon Krijthe Jan 14 '10 at 15:10
  • I think in most cases the compiler (for C++ at least) will give an error due to the ambiguity. It has on the compilers I've used, I'm not sure what the C++ standard says about it. – icabod Jan 14 '10 at 15:17
  • @icabod So, when you write `A::foo()`, what is called is not the method in class A, but one of the methods in A's children, which causes the ambiguity. But what if you want to call class A's `foo()`? How do you do that? – Utku Jul 15 '16 at 15:30
0

Your second example is nowhere near the diamond problem because compiler has the ability to detect the available functions one level up of inheritance.

Once the compiler gets to know that you are using same-named functions in two base classes, it will throw error: member 'foo' found in multiple base classes of different types.

Adarsh Kumar
  • 467
  • 6
  • 11
  • There are other answers that provide the OP's question, and they were posted some time ago. When posting an answer [see: How do I write a good answer?](https://stackoverflow.com/help/how-to-answer), please make sure you add either a new solution, or a substantially better explanation, especially when answering older questions. You can comment on old answers instead. – help-info.de Oct 07 '19 at 13:12
0

The diamond problem only arises from incorrect type/ontology modeling. It doesn't exist in properly-modeled systems.

The problem arises usually because developers define classes for entities based on their roles not their types. They elevate what it can do above what it actually is. The entity object carries the role not the type.

A person is a person, for example, from birth to death regardless of whether he gets married (Husband), visits mom (Son), or lands a great job (Employee). The roles for Person can be implemented with interfaces or components or mixins—fuel in the argument to prefer composition to inhertiance.

It would be reasonable to allow an entity's type to be replaced via some state machine transition. Thus, a Person could be replaced with Werewolf (full moon) or Vampire (neck bite) as the result of a state transition. The entity (some fixed GUID) remains regardless of the transition. But even this can be modeled differently—a Person with Lycanthropy and/or Vampirism. Of course, modeling to accommodate many facets can be a challenge, but it it has nothing to do with the typical limitation that classes may participate in a single inheritance chain.

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.

Mario
  • 6,572
  • 3
  • 42
  • 74