在 Scala 中混合多个特征

Posted

技术标签:

【中文标题】在 Scala 中混合多个特征【英文标题】:Mixing multiple traits in Scala 【发布时间】:2010-11-08 05:55:25 【问题描述】:

快速说明:教程中的示例Scala for Java Refugees Part 5: Traits and Types

假设我具有 Student、Worker、Underpaid 和 Young 的特质。

我如何声明一个具有所有这些特征的类(不是实例)CollegeStudent?

注意:我知道最简单的情况,例如具有一两个特征的 CollegeStudent:

class CollegeStudent extends Student with Worker

【问题讨论】:

【参考方案1】:

这很简单,在声明一个类时,您只需根据需要使用“with”关键字即可

class CollegeStudent extends Student with Worker with Underpaid with Young

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

另外,如果你不想让一个类总是使用相同的特征,你可以在以后使用它们:

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

【讨论】:

改变行为意味着调用一个 trait 方法,该方法在多个 trait 中实现并具有相同的名称。因此,它会根据 trait 的顺序调用该方法。【参考方案2】:

我认为不仅要解释语法,而且要解释特征的顺序所起的作用,这一点非常重要。我发现 Jason Swartz 的 Learning Scala(第 177 页)中的解释非常有启发性。

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

在这种情况下,扩展具有相同字段名称的多个特征将无法编译,完全相同“就像您正在扩展一个类并提供您自己的方法版本但未能添加覆盖一样关键字”。

由于它决定了继承树的形状,线性化顺序确实是一个非常重要的问题。例如,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

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

【讨论】:

以上是关于在 Scala 中混合多个特征的主要内容,如果未能解决你的问题,请参考以下文章

Scala 中的特征和抽象方法覆盖

Scala's Play 中具有多个案例类(总和类型)的特征的 Json 序列化

Scala中的MultiMap

如何将特征混合到实例中?

何时在 Scala 特征中使用 val 或 def?

Scala在通用特征方法中找不到案例类参数