For the code below as I understand it linearization of expression
new E with D with C with B
is E -> C -> B -> D. So then shouldnt the expression d.foo() in the code
below evaluate to ECBD instead of CBDE. What am I missing?
trait A {
def foo(): String = ""
}
trait B extends A {
abstract override def foo() = "B" + super.foo()
}
trait C extends B {
abstract override def foo() = "C" + super.foo()
}
trait D extends A {
abstract override def foo() = "D" + super.foo()
}
class E extends A{
override def foo() = "E"
}
var d = new E with D with C with B;
d.foo() //prints CBDE
I have noticed that if I have a class F like below
class F extends A with D with C with B{
override def foo() = "F" + super.foo()
}
and do
new F().foo
it prints "FCBD"
It seems a bit inconsistent to me because class F is mixed in the same way as the expression but has a different print order
The stackable traits design pattern is based on mixin composition—something we became familiar with in the early chapters of this book. We usually have an abstract class or a trait that defines an interface, a base implementation, and traits that extend the abstract class to stack modifications on it.
In scala, trait is a collection of abstract and non-abstract methods. You can create trait that can have all abstract methods or some abstract and some non-abstract methods. A variable that is declared either by using val or var keyword in a trait get internally implemented in the class that implements the trait.
Unlike a class, Scala traits cannot be instantiated and have no arguments or parameters. However, you can inherit (extend) them using classes and objects.
In scala, trait mixins means you can extend any number of traits with a class or abstract class. You can extend only traits or combination of traits and class or traits and abstract class. It is necessary to maintain order of mixins otherwise compiler throws an error.
The first case with new E with D with C with B
is perfectly explained here. Its linearization is EDBC
, so when you call d.foo()
, it
C#foo()
, B#foo()
, D#foo()
E#foo()
. If you make E
a trait and mix it in the end: val d = new D with C with B with E
, then d.foo()
will return just "E"
, because trait E
is the "last" in the linearization and just overridesfoo
.
The case of F
is different, because you define foo
as "F" + super.foo()
, and super
in this case is A with D with C with B
whose linearization is ADBC
, so new F().foo()
- first prints "F"
,
- then its super.foo()
which is "CBD"
.
By the way, try changing A#foo()
to return "A"
, then you will see that in E
you override A's foo
so "A"
doesn't appear in the result, and in F
it is "FCBDA"
.
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