Answers:
我可以想到两个差异
Scala编程中有一节称为“是特质还是不特质?”。解决了这个问题。由于第一版可在线获得,我希望可以在这里引用整个内容。(任何认真的Scala程序员都应该买这本书):
每当实现行为的可重用集合时,都必须决定要使用特征还是抽象类。没有严格的规则,但是本节包含一些要考虑的准则。
如果该行为不会被重用,则将其设为一个具体的类。毕竟这不是可重用的行为。
如果可以在多个不相关的类中重用它,请使其成为特征。只有特征可以混入类层次结构的不同部分。
如果要在Java代码中从中继承,请使用抽象类。由于具有代码的特征没有紧密的Java类似物,因此从Java类中的特征继承往往很尴尬。同时,从Scala类继承就像从Java类继承一样。一个例外是,仅具有抽象成员的Scala特性直接转换为Java接口,因此即使您期望Java代码从其继承,您也可以随意定义此类特性。有关一起使用Java和Scala的更多信息,请参见第29章。
如果打算以编译形式分发它,并且希望外部团体编写从其继承的类,则您可能倾向于使用抽象类。问题是,当一个特性获得或失去一个成员时,任何从其继承的类都必须重新编译,即使它们没有更改。如果外部客户只会调用行为,而不是继承行为,那么使用特征就可以了。
如果效率非常重要,则倾向于使用课程。大多数Java运行时使对类成员的虚拟方法调用比接口方法调用更快。特性被编译到接口,因此可能会付出一点性能开销。但是,仅当您知道所讨论的特征构成性能瓶颈并有证据证明使用类实际上可以解决问题时,才应做出此选择。
如果您仍然不知道,请在考虑了上述内容之后,将其作为特征开始。您以后总是可以更改它,通常使用特征可以使更多选项保持打开状态。
正如@Mushtaq Ahmed所提到的,特征不能具有任何传递给类的主构造函数的参数。
另一个区别是治疗super
。
类和特性之间的另一个区别是,在类中,
super
调用是静态绑定的,而在特性中,它们是动态绑定的。如果您super.toString
在类中编写代码,那么您将确切知道将调用哪种方法实现。但是,当您在特征中编写相同内容时,定义特征时,用于超级调用的方法实现是不确定的。
有关更多详细信息,请参见第12章的其余部分。
编辑1(2013):
与特征相比,抽象类的行为方式存在细微差异。线性化规则之一是,它保留了类的继承层次结构,这倾向于将抽象类推到链的后面,而特征可以很容易地混合进来。在某些情况下,实际上最好放在类线性化的后一个位置,因此可以使用抽象类。请参阅Scala中的约束类线性化(混合顺序)。
编辑2(2018):
从Scala 2.12开始,特征的二进制兼容性行为已更改。在2.12之前,向特性添加或删除成员都需要重新编译所有继承特性的类,即使这些类未更改也是如此。这是由于在JVM中对特征进行编码的方式所致。
从Scala 2.12开始,特征可以编译到Java接口,因此要求有所放松。如果特征执行以下任何一项操作,则其子类仍需要重新编译:
- 定义字段(
val
或var
,但是常量可以-final val
没有结果类型)- 呼唤
super
- 主体中的初始化器语句
- 扩展课程
- 依靠线性化在正确的特征中找到实现
但是,如果特征不具备,您现在可以在不破坏二进制兼容性的情况下对其进行更新。
If outside clients will only call into the behavior, instead of inheriting from it, then using a trait is fine
-有人可以解释一下有什么区别吗?extends
与with
?
extends
和之间绝对没有语义差异with
。它纯粹是语法上的。如果您从多个模板继承,则第一个获取extend
,其他所有获取with
,就是这样。认为with
是逗号:class Foo extends Bar, Baz, Qux
。
无论值多少钱,Odersky等人的《Scala编程》都建议您在怀疑时使用特征。以后如有需要,您始终可以将它们更改为抽象类。
除了您不能直接扩展多个抽象类,而是可以将多个特征混入一个类之外,值得一提的是,特征是可堆叠的,因为特征中的超级调用是动态绑定的(它是指在混合之前混合了一个类或特征当前一个)。
从托马斯关于抽象类与特质的区别的回答:
trait A{
def a = 1
}
trait X extends A{
override def a = {
println("X")
super.a
}
}
trait Y extends A{
override def a = {
println("Y")
super.a
}
}
scala> val xy = new AnyRef with X with Y
xy: java.lang.Object with X with Y = $anon$1@6e9b6a
scala> xy.a
Y
X
res0: Int = 1
scala> val yx = new AnyRef with Y with X
yx: java.lang.Object with Y with X = $anon$1@188c838
scala> yx.a
X
Y
res1: Int = 1
作者在《Programming Scala》中说,抽象类建立了经典的面向对象的“ is-a”关系,而特征则是构成的一种scala方式。