A特质的自我类型:
trait B
trait A { this: B => }
他说:“A不能被混合到一个具体的类中,这个类不能同时扩展B。”
另一方面,以下几点:
trait B
trait A extends B
表示“任何(具体或抽象的)类在A中混合也会在B中混合”。
这两句话的意思难道不是一样的吗?自我类型似乎只用于产生简单的编译时错误的可能性。
我错过了什么?
A特质的自我类型:
trait B
trait A { this: B => }
他说:“A不能被混合到一个具体的类中,这个类不能同时扩展B。”
另一方面,以下几点:
trait B
trait A extends B
表示“任何(具体或抽象的)类在A中混合也会在B中混合”。
这两句话的意思难道不是一样的吗?自我类型似乎只用于产生简单的编译时错误的可能性。
我错过了什么?
当前回答
其他答案总结:
扩展的类型公开给继承的类型,但自类型不是 class Cow {this: FourStomachs}允许你使用只适用于反刍动物的方法,如digestGrass。扩展牛的特性将没有这样的特权。另一方面,类Cow扩展four胃部会将digestGrass暴露给任何扩展Cow的人。 自类型允许循环依赖,而扩展其他类型则不允许
其他回答
在第一种情况下,B的一个子特征或子类可以混合到任何使用a的地方,所以B可以是一个抽象的特征。
另一件没有提到的事情是:因为自类型不是所需类的层次结构的一部分,它们可以从模式匹配中排除,特别是当您根据密封的层次结构进行穷尽匹配时。当你想要建模正交行为时,这很方便,比如:
sealed trait Person
trait Student extends Person
trait Teacher extends Person
trait Adult { this : Person => } // orthogonal to its condition
val p : Person = new Student {}
p match {
case s : Student => println("a student")
case t : Teacher => println("a teacher")
} // that's it we're exhaustive
它主要用于依赖注入,例如在饼模式中。有一篇很棒的文章介绍了Scala中许多不同形式的依赖注入,包括饼模式。如果你谷歌“蛋糕模式和Scala”,你会得到很多链接,包括演示文稿和视频。现在,这里是另一个问题的链接。
现在,关于自我类型和扩展特质之间的区别,这很简单。如果你说B扩展了A,那么B就是A。当你使用自类型时,B需要一个A。用自类型创建了两个特定的需求:
如果B被扩展了,那么你就需要加入A。 当一个具体的类最终扩展/混合这些特征时,某些类/特征必须实现a。
考虑以下例子:
scala> trait User { def name: String }
defined trait User
scala> trait Tweeter {
| user: User =>
| def tweet(msg: String) = println(s"$name: $msg")
| }
defined trait Tweeter
scala> trait Wrong extends Tweeter {
| def noCanDo = name
| }
<console>:9: error: illegal inheritance;
self-type Wrong does not conform to Tweeter's selftype Tweeter with User
trait Wrong extends Tweeter {
^
<console>:10: error: not found: value name
def noCanDo = name
^
如果Tweeter是User的子类,就不会出现错误。在上面的代码中,无论何时使用Tweeter,我们都需要一个User,但是User没有提供给Wrong,所以我们得到了一个错误。现在,上面的代码仍然在范围内,考虑:
scala> trait DummyUser extends User {
| override def name: String = "foo"
| }
defined trait DummyUser
scala> trait Right extends Tweeter with User {
| val canDo = name
| }
defined trait Right
scala> trait RightAgain extends Tweeter with DummyUser {
| val canDo = name
| }
defined trait RightAgain
使用Right,可以满足混合用户的需求。然而,上面提到的第二个需求没有得到满足:实现User的负担仍然留给了扩展Right的类/特征。
使用RightAgain,这两个需求都得到了满足。提供了一个User和一个User的实现。
要了解更多实际用例,请参阅本回答开头的链接!希望你们现在明白了。
另一个区别是自类型可以指定非类类型。例如
trait Foo{
this: { def close:Unit} =>
...
}
这里的self类型是一个结构类型。其效果是说,任何在Foo中混合的东西都必须实现一个无参数的“close”方法返回单元。这为duck类型提供了安全的mixin。
self类型允许您指定允许在trait中混合哪些类型。例如,如果你有一个带有self类型Closeable的trait,那么这个trait知道唯一允许混合它的东西必须实现Closeable接口。