Начнем с циклической зависимости.
trait A {
selfA: B =>
def fa: Int }
trait B {
selfB: A =>
def fb: String }
Однако модульность этого решения не так велика, как может показаться на первый взгляд, потому что вы можете переопределить собственные типы следующим образом:
trait A1 extends A {
selfA1: B =>
override def fb = "B's String" }
trait B1 extends B {
selfB1: A =>
override def fa = "A's String" }
val myObj = new A1 with B1
Хотя, если вы переопределяете член собственного типа, вы теряете доступ к исходному члену, к которому по-прежнему можно получить доступ через супер с помощью наследования. Итак, что действительно дает использование наследования:
trait AB {
def fa: String
def fb: String }
trait A1 extends AB
{ override def fa = "A's String" }
trait B1 extends AB
{ override def fb = "B's String" }
val myObj = new A1 with B1
Сейчас я не могу утверждать, что понимаю все тонкости паттерна торта, но меня поражает, что основным методом обеспечения модульности является композиция, а не наследование или самотипы.
Версия с наследованием короче, но основная причина, по которой я предпочитаю наследование самотипам, заключается в том, что мне гораздо сложнее получить правильный порядок инициализации с самотипами. Однако есть некоторые вещи, которые вы можете делать с самотипами, которые нельзя делать с наследованием. Самотипы могут использовать тип, в то время как для наследования требуется признак или класс, как в:
trait Outer
{ type T1 }
trait S1
{ selfS1: Outer#T1 => } //Not possible with inheritance.
Вы даже можете:
trait TypeBuster
{ this: Int with String => }
Хотя вы никогда не сможете его создать. Я не вижу абсолютных причин для невозможности наследования от типа, но я определенно считаю, что было бы полезно иметь классы и свойства конструктора пути, поскольку у нас есть черты / классы конструктора типа. Как к сожалению
trait InnerA extends Outer#Inner //Doesn't compile
У нас есть это:
trait Outer
{ trait Inner }
trait OuterA extends Outer
{ trait InnerA extends Inner }
trait OuterB extends Outer
{ trait InnerB extends Inner }
trait OuterFinal extends OuterA with OuterB
{ val myV = new InnerA with InnerB }
Или это:
trait Outer
{ trait Inner }
trait InnerA
{this: Outer#Inner =>}
trait InnerB
{this: Outer#Inner =>}
trait OuterFinal extends Outer
{ val myVal = new InnerA with InnerB with Inner }
Еще один момент, которому следует сопереживать, - это то, что черты могут расширять классы. Спасибо Дэвиду Маклверу за указание на это. Вот пример из моего собственного кода:
class ScnBase extends Frame
abstract class ScnVista[GT <: GeomBase[_ <: TypesD]](geomRI: GT) extends ScnBase with DescripHolder[GT] )
{ val geomR = geomRI }
trait EditScn[GT <: GeomBase[_ <: ScenTypes]] extends ScnVista[GT]
trait ScnVistaCyl[GT <: GeomBase[_ <: ScenTypes]] extends ScnVista[GT]
ScnBase
наследуется от класса Swing Frame, поэтому его можно использовать как самостоятельный тип а затем подмешивают в конце (при создании экземпляра). Однако val geomR
необходимо инициализировать, прежде чем он будет использоваться путем наследования признаков. Итак, нам нужен класс для принудительной предварительной инициализации geomR
. Затем класс ScnVista
может быть унаследован от нескольких ортогональных признаков, от которых могут быть унаследованы сами. Использование нескольких параметров типа (обобщенных) предлагает альтернативную форму модульности.
person
Rich Oliver
schedule
06.10.2012
trait A[Self] {this: Self => }
разрешено,trait A[Self] extends Self
- нет. - person Blaisorblade   schedule 20.01.2013