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中混合”。
这两句话的意思难道不是一样的吗?自我类型似乎只用于产生简单的编译时错误的可能性。
我错过了什么?
当前回答
在第一种情况下,B的一个子特征或子类可以混合到任何使用a的地方,所以B可以是一个抽象的特征。
其他回答
它主要用于依赖注入,例如在饼模式中。有一篇很棒的文章介绍了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的实现。
要了解更多实际用例,请参阅本回答开头的链接!希望你们现在明白了。
Self类型允许您定义循环依赖关系。例如,你可以这样做:
trait A { self: B => }
trait B { self: A => }
使用extends的继承不允许这样。试一试:
trait A extends B
trait B extends A
error: illegal cyclic reference involving trait A
在Odersky的书中,第33.5节(创建电子表格UI章节)提到:
在电子表格示例中,类Model继承自Evaluator和 从而获得其评价方法。同学们,走另一条路 Evaluator将自身类型定义为Model,如下所示:
package org.stairwaybook.scells
trait Evaluator { this: Model => ...
Martin Odersky最初的Scala论文《可伸缩组件抽象》中的2.3节“自类型注释”实际上很好地解释了自类型在mixin组合之外的用途:提供了一种将类与抽象类型关联起来的替代方法。
文中给出的例子如下所示,它似乎没有一个优雅的子类对应:
abstract class Graph {
type Node <: BaseNode;
class BaseNode {
self: Node =>
def connectWith(n: Node): Edge =
new Edge(self, n);
}
class Edge(from: Node, to: Node) {
def source() = from;
def target() = to;
}
}
class LabeledGraph extends Graph {
class Node(label: String) extends BaseNode {
def getLabel: String = label;
def self: Node = this;
}
}
更新:一个主要的区别是自我类型可以依赖于多个类(我承认这有点极端)。例如,你可以有
class Person {
//...
def name: String = "...";
}
class Expense {
def cost: Int = 123;
}
trait Employee {
this: Person with Expense =>
// ...
def roomNo: Int;
def officeLabel: String = name + "/" + roomNo;
}
这允许将Employee mixin添加到Person和Expense的子类中。当然,只有当费用扩展了人员,或者反之,这才有意义。关键是使用自类型Employee可以独立于它所依赖的类的层次结构。它不关心什么扩展什么-如果你切换了费用和人员的层次结构,你不需要修改雇员。
另一件没有提到的事情是:因为自类型不是所需类的层次结构的一部分,它们可以从模式匹配中排除,特别是当您根据密封的层次结构进行穷尽匹配时。当你想要建模正交行为时,这很方便,比如:
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