使用抽象类而不是特征的优点是什么?

使用抽象类而不是特质(除了性能)有什么优势? 在大多数情况下,抽象类似乎可以被特征取代。


我可以想到两个区别

  • 抽象类可以有构造函数参数和类型参数。 特征只能有类型参数。 有一些讨论,即使在未来,特征也可以有构造参数
  • 抽象类与Java完全互操作。 你可以在没有任何包装的情况下从Java代码中调用它们。 只有当它们不包含任何实现代码时,特征才能完全互操作

  • 在Scala编程中有一个部分叫做“特质,还是不特性?” 这解决了这个问题。 由于第一版可以在线获得,我希望可以在这里引用整篇文章。 (任何认真的Scala程序员都应该购买这本书):

    每当你实现一个可重复使用的行为集合,你将不得不决定是否要使用特质或抽象类。 没有确定的规则,但本部分包含一些要考虑的准则。

    如果行为不会被重用,那么将其作为一个具体的类。 毕竟这不是可重用的行为。

    如果它可能在多个不相关的类中重用,则将其作为一个特征。 只有特征可以混合到类层次结构的不同部分。

    如果您想在Java代码中继承它,请使用抽象类。 由于具有代码的特征不具有密切的Java模拟,所以从Java类中的特征继承它往往是尴尬的。 与此同时,从Scala类继承,就像继承自Java类。 作为一个例外,只有抽象成员的Scala特征可以直接转换为Java接口,所以即使您希望Java代码继承它,也应该自由地定义这些特征。 有关使用Java和Scala的更多信息,请参见第29章。

    如果你打算以编译的形式发布它,并且你期望外部团体编写继承它的类,你可能会倾向于使用抽象类。 问题是,当一个特质获得或失去一个成员时,任何从它继承的类都必须重新编译,即使它们没有改变。 如果外部客户只会调用行为,而不是继承它,那么使用特质就没有问题。

    如果效率非常重要,则倾向于使用课堂。 大多数Java运行时对类成员的虚拟方法调用比接口方法调用更快。 特性被编译为接口,因此可能会支付一点性能开销。 然而,只有当你知道所讨论的特质构成了性能瓶颈并且有证据表明使用类来实际解决问题时,你才应该做出这种选择。

    如果你仍然不知道,在考虑了上述内容之后,开始把它作为一个特征。 您可以随时更改它,通常使用特质可以打开更多选项。

    正如@Mushtaq Ahmed所提到的,一个特征不能有任何参数传递给一个类的主构造函数。

    super另一个区别是治疗。

    类和特征之间的另一个区别在于,在类中, super调用是静态绑定的,在特征中,它们是动态绑定的。 如果你在类中写入super.toString ,你确切知道哪个方法实现将被调用。 然而,当你在trait中编写同样的东西时,当定义特征时,调用超级调用的方法实现是不确定的。

    有关更多细节,请参阅第12章的其余部分。

    编辑1(2013):

    抽象类的行为与特征相比存在细微的差异。 线性化规则之一是保留了类的继承层次结构,这往往会在链中稍后推送抽象类,同时可以愉快地混入特征。在某些情况下,实际上最好是处于类线性化的后一个位置,所以抽象类可以用于此。 请参阅在Scala中约束类线性化(mixin order)。

    编辑2(2018):

    从Scala 2.12开始,特征的二进制兼容性行为已经发生了变化。 在2.12之前,即使类没有改变,向特征添加或移除成员也需要重新编译所有继承特征的类。 这是由于特征在JVM中编码的方式。

    从Scala 2.12开始,特性被编译为Java接口,因此需求已经放宽了一些。 如果特征执行以下任何操作,则其子类仍然需要重新编译:

  • 定义字段( valvar ,但是常量是可以的 - final val不带结果类型)
  • 打电话超级
  • 正文中的初始化语句
  • 延长课程
  • 依靠线性化来找到右超级实现
  • 但是这个特性没有,你现在可以在不破坏二进制兼容性的情况下更新它。


    无论它值什么,Odersky等人的Scala编程建议,当你怀疑时,你使用特征。 如果需要,您可以随后将它们更改为抽象类。

    链接地址: http://www.djcxy.com/p/69373.html

    上一篇: What is the advantage of using abstract classes instead of traits?

    下一篇: How to unit test PHP traits