Эта A#XLike
черта не могут быть смешаны в любом месте внутри A
:
val a = new A {}; import a._
scala> class KK extends XLike
<console>:21: error: illegal inheritance;
self-type KK does not conform to a.XLike's selftype a.X
class KK extends XLike
^
можно смешивать внутри A
сек реализации:
trait B extends A {
type X = XImpl
trait XImpl extends XLike { this: X => }
}
val b = new B {}; import b._
scala> class KK extends XImpl
defined class KK
Или даже:
trait B extends A {type X = XLike}
val b = new B {}; import b._
scala> class KK extends XLike
defined class KK
Так что позволяет вы можете выбрать, какая черта обязательна для смешивания:
trait B extends A {
type X = XImpl1
trait XImpl1 extends XLike { this: X => }
trait XImpl2 extends XLike { this: X => }
}
val b = new B {}; import b._
scala> class ZZ extends XImpl1
defined class ZZ
scala> class ZZ extends XImpl2
<console>:40: error: illegal inheritance;
self-type ZZ does not conform to b.XImpl2's selftype b.XImpl2 with b.X
class ZZ extends XImpl2
^
scala> class ZZ extends XImpl1 with XImpl2 // XImpl2 still can be mixed but only if XImpl1 present in linearization
defined class ZZ
Даже несколько из них:
trait A {
type X1 <: XLike
type X2 <: XLike
trait XLike { this: X1 with X2 => }
}
trait B extends A {
type X1 = XImpl1
type X2 = XImpl2
trait XImpl1 extends XLike { this: X1 with X2 => }
trait XImpl2 extends XLike { this: X1 with X2 => }
trait XImpl3 extends XLike { this: X1 with X2 => }
}
На практике X1
и X2
может иметь некоторые важные роли. Например, если вы выполняете цепочку resposibilty (stackable traits) - вы можете смешивать многие черты (и не все из них явно - могут быть группы признаков) в любом порядке, поэтому хорошо иметь возможность указывать одну или несколько обязательные обработчики (только не забывать их), как:
val a = new Logic with Hanler with Group0
trait Group0 extends MandatoryHandler1 with Group1 with H2
trait Group1 extends H3 with H4 with MandatoryHandler2
trait Group2 extends H2 with H5
Если изменить Group0
к Group2
вы можете потерять ваш MandatoryHandler2
(это может быть какой-то репортер, например).
Еще один: разработчик библиотеки может предоставить множество черт, чтобы быть лаконичным с принципом Single Resposibilty/Interface Segregation, но некоторые из них всегда должны быть смешаны (вместе) пользователями, которые будут играть с этим LEGO.
Так вот классический OOP-composition над агрегирования способом (требуется экземпляров):
abstract class B {
val helper1: H1 //mandatory, exactly H1, not some H
val helper2: H2
}
class C extends B {
val helper1 = new H1 //mandatory, exactly H1, not some H
val helper2 = new H2
}
class H1 extends H {...}
class H2 extends H {...}
против mix-in образом:
trait B extends A {
type helper1 = H1
type helper2 = H2
trait H1 extends H // no abstract members here just mix-in
trait H2 extends H
}
trait C extends H1 with H2
Что может быть возможные случаи использования? Зачем мне все это делать? – sparkr
«Я наткнулся на код, который выглядит так» - как насчет этого. Не беспокойтесь, никакой драмы здесь, поскольку код очень прост - вы просто говорите, что одна черта всегда должна быть смешана с другой - это просто дополнительная проверка времени компиляции. – dk14
Например, когда вы выполняете цепочку resposibilty - вы можете смешивать более 9000 признаков, поэтому хорошо иметь возможность указывать один или несколько обязательных обработчиков, например 'val a = new Logic with Hanler with OtherHandlerList; trait OtherHandlerList расширяет MandatoryHandler с H1 с H2' - и это только первое, что пришло в голову. – dk14