A特质的自我类型:

trait B
trait A { this: B => }

他说:“A不能被混合到一个具体的类中,这个类不能同时扩展B。”

另一方面,以下几点:

trait B
trait A extends B

表示“任何(具体或抽象的)类在A中混合也会在B中混合”。

这两句话的意思难道不是一样的吗?自我类型似乎只用于产生简单的编译时错误的可能性。

我错过了什么?


当前回答

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可以独立于它所依赖的类的层次结构。它不关心什么扩展什么-如果你切换了费用和人员的层次结构,你不需要修改雇员。

它主要用于依赖注入,例如在饼模式中。有一篇很棒的文章介绍了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的实现。

要了解更多实际用例,请参阅本回答开头的链接!希望你们现在明白了。

其他答案总结:

扩展的类型公开给继承的类型,但自类型不是 class Cow {this: FourStomachs}允许你使用只适用于反刍动物的方法,如digestGrass。扩展牛的特性将没有这样的特权。另一方面,类Cow扩展four胃部会将digestGrass暴露给任何扩展Cow的人。 自类型允许循环依赖,而扩展其他类型则不允许

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 => ...

另一个区别是自类型可以指定非类类型。例如

trait Foo{
   this: { def close:Unit} => 
   ...
}

这里的self类型是一个结构类型。其效果是说,任何在Foo中混合的东西都必须实现一个无参数的“close”方法返回单元。这为duck类型提供了安全的mixin。