2
votes

I'm a Scala beginner. I was told that "a field in a trait can be concrete or abstract".

trait T2 {
  val f1: String = "T2f1"
}

trait T3 {
  val f1: String = "T3f1"
}

class C2 extends T2 with T3{}

object Test2 extends App {
  val c2 = new C2
  println(c2.f1)
}

When I ran the code above, the compiler printed some error messages:

" class C2 inherits conflicting members: value f1 in trait T2 of type String and variable f1 in trait T3 of type String (Note: this can be resolved by declaring an override in class C2.) class C2 extends T2 with T3{} "

So what should be changed if C2 extend traits that have the same field name? Thank you for your help.

3
class C2 extends T2 with T3 {override val f1: String = ...}mfirry

3 Answers

1
votes

You can resolve ambiguity manually:

  trait T2 {
    val f1: String = "T2f1"
  }

  trait T3 {
    val f1: String = "T3f1"
  }

  class C2 extends T2 with T3 {
    override val f1: String = "T2f1"
  }

or

  trait T2 {
    val f1: String = "T2f1"
  }

  trait T3 {
    val f1: String = "T3f1"
  }

  class C2 extends T2 with T3 {
    override val f1: String = "T3f1"
  }

Compiler could do this automatically via linearization if it were

  trait T2 {
    val f1: String = "T2f1"
  }

  trait T3 extends T2 {
    override val f1: String = "T3f1"
  }

  class C2 extends T2 with T3

Conflicting fields in Scala Traits

2
votes

The accepted answer is correct, but bear in mind that the suggested pattern is strange and could lead to hard to understand bugs in non-trivial cases. In my experience, overriding non-abstract vals will only get you in trouble.

The problem is that the initialisation code is part of the constructor of the defined class/trait. This means that the code initialising both T2.f1 and T3.f1 will be executed when creating an instance of C2:

trait T2 {
    val f1: String = {
        println("T2")
        "T2f1"
    }
}

trait T3 {
    val f1: String = {
        println("T3")
        "T3f1"
    }
}

class C2 extends T2 with T3 {
    override val f1: String = {
        println("C2")
        "T3f1"
    }
}

new C2 // Will print "T2", then "T3", then "C2"

If the initialisation code has any important side-effect, this might result in hard-to-trackdown bugs! It also has the disadvantage of forcing you to repeat some of T3's code in C2.

If you don't absolutely need T2.f1 and T3.f1 to be vals, you might be better off using defs to avoid initialisation code in abstract vals:

trait T2 {
    def f1: String = "T2f1"
}

trait T3 {
    def f1: String = "T3f1"
}

class C2 extends T2 with T3 {
    override val f1: String = "C2f1" // You can keep this a def if you like
}

In case you really need f1 to be a val, e.g. if you need a stable value to use it in pattern matching statements, you can use the following:

trait T2 {
    val f1: String
    protected def computeF1: String = {
        println("T2")
        "T2f1"
    }
}

trait T3 {
    val f1: String
    protected def computeF1: String = {
        println("T3")
        "T3f1"
    }
}

class C2 extends T2 with T3 {
    override val f1: String = computeF1 // You can keep this a def if you like
    override protected def computeF1: String = super[T3].computeF1
}

new C2 // Only prints "T3" once

This last solution is a bit more verbose but it bypasses the problem altogether by avoiding the overriding of a non-abstract val.

0
votes

Suggested example is correct but try to avoid cyclic dependency it could lead to some serious problems too.

See the problem here is you have the same field in both of the traits and compiler is not able to resolve which property of which trait you gonna use. So you can solve the problem by make it explicit to the compiler.

Trait A{
val x:String=“ABC”}

Trait B extends A{
override val x: String=“DEF”}

class AB extends B{
override val f1: String = super[T3].x}