Search code examples
scalamultiple-inheritancetraitsdiamond-problem

How does "override" work when inherited traits are combined?


I'm experimenting with multiple inheritance in Scala. I get that there is a right-to-left resolution, but I don't understand the role of the override keyword. Let's consider the following snippet :

trait A           { def          method                }
class B extends A { def          method = println("B") }
trait C extends A { override def method = println("C") }
trait E           {                                    }
class D extends B with C with E

It compiles fine, but if I remove override from the rightmost trait defining method, it's broken. Why is this ? Isn't the method overridden even without the keyword ?


Solution

  • If a trait is supposed to replace (rather than provide a missing implementation of) a method, you need to use the override keyword. This is true whether the other method is provided in a trait or in some other way. For instance:

    trait X { def foo = "foo" }
    class Y { def foo = "bar" }
    class Z extends Y with X  // Does not compile
    

    So it's really the exact same rules as with abstract classes: you need to override the methods whose implementations you replace (rather than provide the first time).

    Note that with traits, as long as the last provided method is an override, it is enough:

    trait L { def m: String }
    trait M extends L { def m = "m" }
    trait N extends L { def m = "M" }
    trait O extends L { override def m = "?" }
    class P extends M with N with O  // All good
    class Q extends M with N { override def m = "." } // Also good
    

    Here, what counts as "already provided" is the earlier part of the extension, and all's well that ends well. So

    class R extends O with N
    

    does not work, because O provides an implementation of m (which may override if it needs to), but N then tries to apply another, and it is not allowed to override.

    On the other hand, P is fine because even though M and N both try to provide an implementation for m, O comes last and declares that its implementation may override. So it does, and all is well.