Suppose I have the following code:
trait Trait1 {
trait Inner {
val name = "Inner1"
}
}
trait Trait2 {
trait Inner {
val name = "Inner2"
}
}
class Foo extends Trait1 with Trait2 {
// I want Concrete1 to be a Trait1.Inner not a Trait2.Inner
class Concrete1 extends Inner
val c = new Concrete1
}
object Obj {
def main(args: Array[String]): Unit = {
val foo = new Foo
println(foo.c.name)
}
}
When I mix in Trait1
and Trait2
, referring to Inner
seems to default to the Inner
type of whichever trait I mixin second; so when I call Obj
's main
method it prints Inner2
. How can I refer to Trait1.Inner
in Foo
? All three of the following give compiler errors:
class Concrete1 extends Trait1.Inner
class Concrete1 extends Trait1$Inner
class Concrete1 extends Trait1#Inner
Instead of
class Concrete1 extends Inner
Use this
class Concrete1 extends super[Trait1].Inner
That should get you what you want
There are two namespaces within a template (template being the body of a class, object, or trait.)
When inheriting from multiple parent templates, conflicts in these namespaces are resolved through class linearization.
You could re-order your inheritance to bring the desired parent Inner into your class, or find an alternative design.
One option (if you can be invasive to the traits) is to define each Inner trait as a type member that has a non-conflicting name.
trait Trait1 {
type Inner1 = Inner
trait Inner {
val name = "Inner1"
}
}
trait Trait2 {
type Inner2 = Inner
trait Inner {
val name = "Inner2"
}
}
class Foo extends Trait1 with Trait2 {
class Concrete1 extends Inner1
class Concrete2 extends Inner2
val c1 = new Concrete1
val c2 = new Concrete2
}
object App extends Application {
val foo = new Foo
println(foo.c1.name) // Inner1
println(foo.c2.name) // Inner2
}
If you cannot be invasive to the original traits (Trait1 and Trait2), you can extend them to define the type member.
trait Trait1 {
trait Inner {
val name = "Inner1"
}
}
trait Trait2 {
trait Inner {
val name = "Inner2"
}
}
trait Trait1a extends Trait1 {
type Inner1 = Inner
}
trait Trait2a extends Trait2 {
type Inner2 = Inner
}
class Foo extends Trait1a with Trait2a {
class Concrete1 extends Inner1
class Concrete2 extends Inner2
val c1 = new Concrete1
val c2 = new Concrete2
}
Another approach would be to use an intermediate trait to define your first concrete class:
trait Trait1 {
trait Inner {
val name = "Inner1"
}
}
trait Trait2 {
trait Inner {
val name = "Inner2"
}
}
trait FooIntermediate extends Trait1 {
class Concrete1 extends Inner
}
class Foo extends FooIntermediate with Trait2 {
class Concrete2 extends Inner
val c1 = new Concrete1
val c2 = new Concrete2
}
Why not order the traits in the order you expect them to have precedence? The linearization of traits is not arbitrary, but specified.
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