在Scala中混合多个特质

67

快速提示: 本例子出自 Scala for Java Refugees Part 5: Traits and Types 教程。

假设我有下列特质:Student,Worker,Underpaid和Young。

如何声明一个类(不是实例),名为CollegeStudent,并拥有所有这些特质?

注意: 我知道简单的情况,例如只有一两个Trait的CollegeStudent:

class CollegeStudent extends Student with Worker
2个回答

124

声明一个类时,只需在需要的地方使用“with”关键字即可。

class CollegeStudent extends Student with Worker with Underpaid with Young

如果一个特征改变了类的行为,那么特征的顺序可能很重要,这完全取决于你使用的特征。

此外,如果您不想始终使用相同的特征来创建类,可以稍后再使用它们:

class CollegeStudent extends Student
new CollegeStudent with Worker with Underpaid with NotSoYoungAnymore

1
改变行为意味着调用实现了相同名称的多个 trait 中的一个 trait 方法。因此,根据 trait 的顺序,它将调用该方法。 - Ajay Gupta

19

我认为解释语法和解释特质的排序都是非常重要的。在Jason Swartz的《Learning Scala》(第177页)中,我发现了一个相当启发性的解释。

  • Scala类可以同时扩展多个特质,但JVM类只能扩展一个父类。Scala编译器通过创建“每个特质的副本以形成类和特质的高大、单列层次结构”,这个过程称为线性化来解决这个问题。

  • 在这种情况下,具有相同字段名的多个特质的扩展将无法编译,这与“如果您扩展了一个类并提供了自己的版本的方法,但未添加override关键字”完全相同。

由于它确定了继承树的形状,因此线性化顺序确实是一个非常重要的问题。例如,class D extends A with B with C(其中A是一个类,B和C是特质)将变成class D extends C extends B extends A。下面几行也来自于本书,完美地说明了这一点:

trait Base { override def toString = "Base" }
class A extends Base { override def toString = "A->" + super.toString }
trait B extends Base { override def toString = "B->" + super.toString }
trait C extends Base { override def toString = "C->" + super.toString }
class D extends A with B with C { override def toString = "D->" + super.toString }

调用 new D() 将会在 REPL 中打印出以下内容:

 D->C->B->A->Base

这完美地体现了线性化继承图的结构。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接