0

I have a scenario with multiple traits inheriting from a fixed base trait. The base trait has an abstract method that each needs to implement. The class using these traits also needs to implement this method:

trait A {
    def f: Any
}

trait B1 extends A {
    val i: Int
    override def f: Any = println("B1: " + i)        
}

trait B2 extends A {
    val j: Int
    override def f: Any = println("B2: " + j)
}

class C extends A with B1 with B2 {
    val i = 1
    val j = 2

    override def f: Any = {
        super.f
        println("C::f")
    }
}

Then when I do a

new C().f

It only outputs "B1: 1" and not "B2: 2"

Is there a way I can define B1, B2 and C so that all implementations of f can be called?

Mario Galic
  • 47,285
  • 6
  • 56
  • 98
user79074
  • 4,937
  • 5
  • 29
  • 57

2 Answers2

4

This is an example of the diamond problem which Scala addresses by the process of linearisation:

Scala resolves method names using a right-first depth-first search of extended 'traits', before eliminating all but the last occurrence of each module in the resulting list. So, the resolution order is: [D, C, A, B, A], which reduces down to [D, C, B, A].

so the output should be in your case

B2: 2
C::f

so it seems it is not possible to call all the f overrides in both B1 and B2, instead only B2.f is resolved.

Mario Galic
  • 47,285
  • 6
  • 56
  • 98
4

Well as you know which two diamond traits you mix in, you can call both super implementations and specify the trait from which you want to call each super implementation.

So, you could implement C like this:

class C extends A with B1 with B2 {
  val i = 1
  val j = 2

  override def f: Any = {
    super[B1].f
    super[B2].f
    println("C::f")
  }
}

And would get the output:

B1: 1
B2: 2
C::f
Sascha Kolberg
  • 7,092
  • 1
  • 31
  • 37