Scala中抽象类和特征之间的实际差异


背景 当涉及到继承选项时,Scala尤其允许。当然,Scala具有一个单类继承模型,其中可以扩展一个类。与Java一样,Scala类可以从多个特征(相当于Java接口)继承。

Scala还具有抽象类的概念,在该类中,

限制类实例化的能力 添加未实现的字段或方法

abstract class Person {
  // the abstract keyword is not necessary in the field/method definition
  val canDrive: Boolean
  def discussWith(another: Person): String
}

val bob = new Person // error - not all members implemented

抽象类可能具有也可能没有抽象(未实现)或非抽象(已实现)方法。

但是,特征也具有大多数相同的功能:

他们不能自己实例化 他们可能有抽象的(未实现的)字段或方法 (更重要的是)它们也可能具有非抽象(已实现)的字段和方法 因此,在Rock JVM上教授Scala的过程中,我经常遇到一个普遍的问题:特征和抽象类到底有什么不同?

相似点 如果您扩展单个类,则抽象类和特征之间在实用上几乎没有什么区别。如果您看下面的示例:

xxxxxxxxxx

class Adult(val name: String, hasDrivingLicence: Boolean) extends Person {
  override def toString: String = name
  override val canDrive: Boolean = hasDrivingLicence
  override def discussWith(another: Person): String = "Indeed, ${other}, Kant was indeed revolutionary for his time..."
}

无论Person是特征还是抽象类,代码都是相同的。因此,从这个角度来看,特征和抽象类是相同的。

实际差异 当我们朝着多重继承迈进时,情况开始看起来有所不同。特征和抽象类之间的根本区别之一是,在类的继承定义中可以使用多个特征,而可以扩展单个抽象类。由于特征也可​​以具有非抽象的方法,因此一个很好的(自然的)后续问题可能是:“但是,如果一个类从两个特征继承而来的方法相同,那么使用同一方法以不同的方式会发生什么呢?”钻石问题。Scala通过一种称为线性化的专用机制解决了这一问题,我们将在另一篇文章中进行讨论。

特征和抽象类之间的第二个更实际的区别是抽象类可以采用构造函数参数:

abstract class Person(name: String, age: Int)

特质不能:

trait Person(name: String, age: Int) // error

也就是说,至少在Scala 3发布之前-Scala 3还将允许特征的构造函数参数。

细微的差异 当我在JVM的Rock上进行关于特质和抽象类之间的上述差异的现场培训时,通常不会给我的听众留下深刻的印象-似乎重叠如此之大,以至于两个不同的概念可能没有目的。

但是,我总是以下面的推理为最终结论:对于良好的实践,使代码易于阅读,理解和推理始终是一个好主意。为此,我们通常将“事物”表示为类,将“行为”表示为特征。这种概念上的差异直接映射到语言中:“事物”(类)可以是另一个“事物”(也是类)的专门版本,但可以包含多个“行为”(特征)。

结论 抽象类和特征是截然不同的概念,具有许多机械重叠。您现在将了解它们之间的(非常)相似之处和不同之处-无论是在Scala机制方面,还是在我们阅读和理解代码方面。


原文链接:http://codingdict.com/