似乎Scala的explicitly-typed self references最常见的用法是“Cake pattern”,其中模块的依赖关系被声明为:
class Foo { this: A with B with C =>
// ...
}
通常,暂时忽略蛋糕模式,A
,B
和C
可以引用任何类型级别的内容,例如类型参数:
class Outer[A, B, C] {
class Inner { this: A with B with C =>
// ...
}
}
...或抽象类型成员:
class Outer {
type A
type B
type C
class Inner { this: A with B with C =>
// ...
}
}
在这两种情况下,我们都不能写abstract class Inner extends A with B with C
,因为A
,B
和C
不是特征。这里需要明确键入的自引用。但是,我只见过用特征完成的蛋糕模式:
trait A { def a }
trait B { def b }
trait C { def c }
class Foo { this: A with B with C =>
// ...
}
在这种情况下,我们可以直接写abstract class Foo extends A with B with C
,如果我没有弄错,它具有相同的含义。我对么?如果没有,那么他们如何区别;如果是这样,为什么每个人似乎都使用明确类型的自引用?
答案 0 :(得分:6)
似乎我忽略了两个主要的区别:
虽然明确的自我类型注释和简单的extends
关键字都描述了" is-a"两种类型之间的关系,在前一种情况下,这种关系在外部是不可见的:
scala> trait T
defined trait T
scala> class C { this: T => }
defined class C
scala> implicitly[C <:< T]
<console>:10: error: Cannot prove that C <:< T.
这是一件好事,因为在蛋糕模式中你不想要你的&#34;模块&#34;对象无意中,多态地用作它所依赖的特征之一。
As noted explicitly by Mushtaq和indirectly by Daniel,在使用自我类型注释时,依赖关系可以是循环的。循环依赖是非常常见的,并且不一定是坏的(假设相互依赖的组件不需要彼此进行初始化,或者我们可以在它们之间以某种方式tie the knot),所以这是自我类型的另一个明显好处继承注释。
答案 1 :(得分:1)
使用继承时,可以决定初始化顺序。当您使用自我类型时,请将其保持打开状态。
还有其他差异,但我认为其中大多数都是实施细节,可能会消失。我知道其中一些是。