Search code examples
scalamixinstraitssubtyping

How can I predict which implementation will be chosen when mixing in multiple traits with conflicting abstract overrides?


Consider this example:

abstract class Writer {
  def write(message: String): Unit
}

trait UpperCaseFilter extends Writer {
  abstract override def write(message: String) =
    super.write(message.toUpperCase)
}

trait LowerCaseFilter extends Writer {
  abstract override def write(message: String) =
    super.write(message.toLowerCase)
}

class StringWriter extends Writer {
  val sb = new StringBuilder

  override def write(message: String) =
    sb.append(message)

  override def toString = sb.toString
}

object Main extends App {
  val writer = new StringWriter with UpperCaseFilter with LowerCaseFilter
  writer.write("Hello, world!")
  println(writer)
}

I was surprised by the output “HELLO, WORLD!” Why is the output not “hello, world!” or a compilation error?


Solution

  • The logic that decides it is called linearization. You can find more information about it here:

    http://www.artima.com/pins1ed/traits.html#12.6

    In your case the whole class hierarchy would linearized like this:

    LowerCaseFilter > UpperCaseFilter > Writer > StringWriter > AnyRef > Any

    So, as you can see, UpperCaseFilter was the last transformation that went to the StringWriter.