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