Self-types are a way to declare that a trait must be mixed into another trait, even though it doesn't directly extend it. That makes the members of the dependency available without imports. A self-type is a way to narrow the type of this or another identifier that aliases this .
It lets you create dependencies among modules. For instance: class System extends Base with CompA with CompB with CompC. If CompA needs to use something from CompC , it can be defined as: trait CompA { self: CompC => ... } In this case: class System extends Base with CompA with CompB.
You can have a single self-type which is a compound type.
Try this:
trait A {
def aValue = 1
}
trait B {
def bValue = 1
}
trait C {
self: A with B =>
def total = aValue + bValue
}
class ABC extends A with B with C
With one trait you can do it with structural type:
trait C {
self: { def aValue: Int
def bValue: Int } =>
def total = aValue + bValue
}
class ABC extends C {
def aValue = 1
def bValue = 1
}
Reflection used.
But, firstly you should not overuse self-types because of principle of least power.
Methods from question can be added simply by extending other tait:
trait C extends A with B{
def total = aValue + bValue
}
or type both methods explicitly:
trait C {
def aValue: Int
def bValue: Int
def total = aValue + bValue
}
Where to use self-types?
Self-types are usually used with classes. This is a good way for trait to require being a subclass of a desired class.
There is also one good use of self-type with triats: when you want to manipulate order of class initialization with multi-inheritance.
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