让我们从周期性依赖开始。
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
虽然,如果您覆盖自身类型的成员,则您将失去对原始成员的访问权限,该成员仍可以通过使用继承的super进行访问。因此,使用继承真正获得的是:
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 }
应该更加理解的一点是,特质可以扩展类。感谢David Maclver指出这一点。这是我自己的代码中的一个示例:
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
然后,可以通过多个正交特性从这些类继承它们,而这些正交特性又可以从中继承。使用多个类型参数(泛型)提供了模块化的另一种形式。