Behaviour of super in chained Scala traits
Why does x.func
below return "B extends B extends B"
?
How to arrange this code so that it returns "B extends A extends Base"
?
trait Base {
def name = "Base"
def func = name
}
trait A extends Base {
override def name = "A"
override def func = name + " extends " + super.func
}
trait B extends Base {
override def name = "B"
override def func = name + " extends " + super.func
}
val x = new Base with A with B
println(x.func)
Update: One arrangement could be as follows. It now has iden开发者_开发百科tical definitions of func1
in A
and B
. It does not work if I try to move it to the Derived
class. Any ideas how to remove the repetition of func1
?
trait Base {
def name = "Base"
def func1(s: String) = s
}
trait Derived extends Base {
def func = func1(name)
}
trait A extends Derived {
override def func1(s: String) = s + " extends " + super.func1(super.name)
override def name = "A"
}
trait B extends Derived {
override def func1(s: String) = s + " extends " + super.func1(super.name)
override def name = "B"
}
val x = new Base with A with B
println(x.func)
I think the inheritance order may actually be the one your are seeking. If you replace the " extends "
with one that shows which method of which trait is called:
trait Base {
def name = "Base"
def func = "Base." + name
}
trait A extends Base {
override def name = "A"
override def func = name + " A.extends " + super.func
}
trait B extends Base {
override def name = "B"
override def func = name + " B.extends " + super.func
}
val x = new Base with A with B
println(x.func)
// B B.extends B A.extends Base.B
It's just that name
is always "B"
. In other words:
trait Base { def func = "Base" }
trait A extends Base { override def func = "A extends " + super.func }
trait B extends Base { override def func = "B extends " + super.func }
val x = new Base with A with B
println(x.func)
// B extends A extends Base
which is what you want...
The full linearization of your example is:
Object, B, A, Base, ScalaObject, AnyRef, Any
(see http://ofps.oreilly.com/titles/9780596155957/ScalaObjectSystem.html#Linearization for a practical explanation on how to figure out the linearization)
Edit to answer comment: why is the name
always returns "B"
? That's because the def name
method is overridden by the trait B
to return "B"
. That's the whole point of inheritance, to be able to use in superclasses behavior that is refined in subclasses:
trait Legs {
def legs: Int
def printLegs() { println("I have " + legs + " legs") }
}
class Dog extends Legs { def legs = 4 }
class Chicken extends Legs { def legs = 2 }
new Dog printLegs
// I have 4 legs
new Chicken printLegs
// I have 2 legs
legs
in trait Legs
is not a separate one than legs
in Dog
depending on if you refer to it in Legs
or Dog
... Similarly, your def name
will always return "B"
if your object is a B
.
It looks like you want to use name as a private method:
trait Base {
private def _name() = "Base"
def func = _name
}
trait A extends Base {
private def _name() = "A"
override def func = _name + " extends " + super.func
}
trait B extends Base {
private def _name() = "B"
override def func = _name + " extends " + super.func
}
val x = new Base with A with B
println(x.func)
// B extends A extends Base
I find that if there isn't a clear object model, using trait and inheritance quickly gets complicated. I assume you sanitized/simplified the example to use generic names like A, B, Base, func
so that you get to the core of the issue, but on the other hand it doesn't give me any insights on what changes you could do to make it work for you. As you asked I've arrange the code so that it prints "B extends A extends Base"
. I'm sure there are a bunch of other constraints that aren't in the question why it won't work for you.
精彩评论