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.
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.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With