除了性能方面,使用抽象类的优点是什么?似乎大多数情况下抽象类都可以被 trait 取代。
我能想到两个区别
《Scala编程》一书中有一个章节叫做“使用Trait还是不使用Trait?”"(To trait, or not to trait?)", 专门讨论这个问题。由于第一版可以在线获得,我希望引用整篇文章不会有问题(任何严肃的Scala程序员都应该购买这本书):
无论何时你实现一个可重用的行为集合,你都需要决定是要使用trait还是抽象类。没有确定的规则,但本节包含了几个考虑因素。super
的处理。super
调用是静态绑定的;而在特质中,它们是动态绑定的。如果你在类中写了super.toString
,你就知道将调用哪个方法实现。然而,当你在特质中编写相同的代码时,为超级调用调用的方法实现在定义特质时是未定义的。
- 定义字段(
val
或var
,但常量也可以-使用不带结果类型的final val
)- 调用
super
- 在主体中使用初始化语句
- 扩展一个类
- 依赖线性化来找到正确超特质中的实现
但是如果特质没有这样做,您现在可以更新它而不会破坏二进制兼容性。
extends
和 with
有什么区别? - 0fntextends
和with
之间绝对没有语义上的区别,它们纯粹是语法上的。如果你从多个模板中继承,第一个使用extend
,其余的都使用with
,就是这样。把with
看作逗号:class Foo extends Bar, Baz, Qux
。 - Jörg W Mittag无论价值多少,Odersky等人的《Scala程序设计》(Programming in Scala)建议,在怀疑时使用特质(trait)。如果需要,您随时可以将它们更改为抽象类。
除了你不能直接扩展多个抽象类之外,但你可以将多个traits混合到一个类中。值得一提的是,traits是可堆叠的,因为trait中的super调用是动态绑定的(它是指向在当前trait之前被混合的类或trait)。
来自Thomas在Difference between Abstract Class and Trait中的回答:
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
在扩展抽象类时,这表明子类是类似类型的。但使用特性并非必然如此。
抽象类可以包含行为 - 它们可以使用构造函数参数化(特质无法),并表示一个可工作的实体。而特质只代表单个功能的接口。
trait Enumerable
定义了许多帮助函数时,我不会称它们为行为,而只是与一个功能相关的功能。 - Dario