Scala 自我类型和特质亚类之间有什么区别?

Scala 自我类型和特质亚类之间有什么区别?,scala,traits,self-type,Scala,Traits,Self Type,特征的自我类型A: trait B trait A { this: B => } 表示“A不能混合到不扩展B的具体类中” 另一方面,以下方面: trait B trait A extends B 表示“在A中混合的任何(具体或抽象)类也将在B中混合” 这两种说法的意思不一样吗?self类型似乎只用于创建简单编译时错误的可能性 我缺少什么?在第一种情况下,B的子特征或子类可以混合到使用a的任何东西中。因此B可以是抽象特征。自我类型允许您指定允许混合特征的类型。例如,如果您有一个自我类型为

特征的自我类型
A

trait B
trait A { this: B => }
表示“
A
不能混合到不扩展
B
的具体类中”

另一方面,以下方面:

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

这两种说法的意思不一样吗?self类型似乎只用于创建简单编译时错误的可能性


我缺少什么?

在第一种情况下,B的子特征或子类可以混合到使用a的任何东西中。因此B可以是抽象特征。

自我类型允许您指定允许混合特征的类型。例如,如果您有一个自我类型为“可关闭”的特征,那么该特征知道,只有允许将其混合在一起的东西才能实现“可关闭”接口。

自我类型允许您定义周期性依赖关系。例如,您可以实现以下目标:

trait A { self: B => }
trait B { self: A => }
使用
扩展的继承不允许这样做。尝试:

trait A extends B
trait B extends A
error:  illegal cyclic reference involving trait A
在奥德斯基的书中,请参阅第33.5节(创建电子表格UI章节),其中提到:

在电子表格示例中,类模型继承自Evaluator和 从而获得其评价方法。走另一条路,同学们 Evaluator将其自身类型定义为模型,如下所示:


希望这有帮助。

它主要用于制作蛋糕图案。Scala中存在许多不同形式的依赖注入,包括Cake模式。如果你在谷歌上搜索“蛋糕模式和Scala”,你会得到很多链接,包括演示文稿和视频。现在,这里有一个链接

现在,关于自我类型和延伸特质之间的区别,很简单。如果你说
B扩展了A
,那么
B
就是
A
。使用self类型时,
B
需要
A
。使用self类型创建两个特定需求:

  • 如果扩展了
    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
                           ^
    
    有了
    权限
    ,就满足了混入
    用户
    的要求。然而,上面提到的第二个要求没有得到满足:实现
    User
    的负担仍然存在于扩展
    权限的类/特征上

    再次使用
    右侧
    这两个要求都得到满足。提供了
    用户
    用户
    的实现


    有关更多实际用例,请参阅此答案开头的链接!但是,希望你现在明白了

    另一个区别是self类型可以指定非类类型。比如说

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

    这里的self类型是一种结构类型。其效果是,任何混合在Foo中的东西都必须实现一个no-arg“close”方法返回单元。这允许鸭子类型的安全混合。

    让我们从循环依赖性开始

    trait A {
      selfA: B =>
      def fa: Int }
    
    trait B {
      selfB: A =>
      def fb: String }
    
    但是,此解决方案的模块化程度并不像最初出现的那样高,因为您可以覆盖self类型,如下所示:

    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
    
    但是,如果重写自类型的成员,则会失去对原始成员的访问权限,而原始成员仍然可以通过超级继承进行访问。因此,使用继承的真正好处是:

    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
    
    现在我不能说我理解了cake模式的所有微妙之处,但让我印象深刻的是,强制模块化的主要方法是通过组合,而不是继承或自类型

    继承版本较短,但我更喜欢继承而不是自类型的主要原因是,我发现用自类型获得正确的初始化顺序要困难得多。但是,有些事情可以用自类型来完成,而继承则不能。自类型可以使用类型,而继承需要trait或类,如中所示:

    trait Outer
    { type T1 }     
    trait S1
    { selfS1: Outer#T1 => } //Not possible with inheritance.
    
    你甚至可以:

    trait TypeBuster
    { this: Int with String => }
    
    尽管你永远无法实例化它。我不认为有任何绝对的理由不能从类型继承,但我确实觉得拥有路径构造函数类和traits会很有用,因为我们拥有类型构造函数traits/类。不幸的是

    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扩展帧
    
    抽象类ScnVista[GT更新:一个主要区别是self类型可以依赖于多个类(我承认这有点偏僻)

    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
    的子类中。当然,这只有在
    Expense
    扩展
    Person
    时才有意义,反之亦然。关键是使用自类型
    Employee
    可以独立于类的层次结构这取决于。它不关心什么扩展了什么-如果您切换
    费用
    人员
    的层次结构,您不必修改
    员工

    特征A{def x=1}
    
    trait A { def x = 1 }
    trait B extends A { override def x = super.x * 5 }
    trait C1 extends B { override def x = 2 }
    trait C2 extends A { this: B => override def x = 2}
    
    // 1.
    println((new C1 with B).x) // 2
    println((new C2 with B).x) // 10
    
    // 2.
    trait X {
      type SomeA <: A
      trait Inner1 { this: SomeA => } // compiles ok
      trait Inner2 extends SomeA {} // doesn't compile
    }
    
    特征B扩展了{override def x=super.x*5} 特征C1扩展了B{override def x=2} 特征C2扩展了A{this:B=>override def x=2} // 1. println((带B.x的新C1)//2 println((带B.x的新C2)//10 // 2. 性状X{ 键入SomeA}//编译ok trait Inner2扩展了一些{}//无法编译的对象 }
    Martin Odersky最初的Scala论文的第2.3节“Selftype注释”实际上很好地解释了Selftype超越mixin组合的目的:提供一种将类与抽象类型关联的替代方法

    文中给出的例子是l
    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;
    }
    
    trait A { def x = 1 }
    trait B extends A { override def x = super.x * 5 }
    trait C1 extends B { override def x = 2 }
    trait C2 extends A { this: B => override def x = 2}
    
    // 1.
    println((new C1 with B).x) // 2
    println((new C2 with B).x) // 10
    
    // 2.
    trait X {
      type SomeA <: A
      trait Inner1 { this: SomeA => } // compiles ok
      trait Inner2 extends SomeA {} // doesn't compile
    }
    
    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;
      }
    }
    
    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