一、什么是特质
因为Scala没有多重继承,为了提高代码复用率,故而创造了新的编程概念——特质。
特质是用关键字“trait”为开头来定义的,它与单例对象很像,两者都不能有入参。但是,单例对象天生就是具体的,特质天生就是抽象的,不过不需要用“abstract”来说明。所以,特质可以包含抽象成员,而单例对象却不行。另外,两者都不能用new来实例化,因为特质是抽象的,而单例对象已经是具体的对象。类、单例对象和特质三者一样,内部可以包含字段和方法,甚至包含其他类、单例对象、特质的定义。
特质可以被其它类、单例对象和特质“混入”。这里使用术语“混入”而不是“继承”,是因为特质在超类方法调用上采用线性化机制,与多重继承有很大的区别。其它方面,“混入”和“继承”其实是一样的。例如,某个类混入一个特质后,就包含了特质的所有公有成员,而且也可以用“override”来重写特质的成员。
Scala只允许继承自一个类,但是对特质的混入数量却没有限制,故而可用于替代多重继承语法。要混入一个特质,可以使用关键字“extends”。但如果“extends”已经被占用了,比如已经拿去继承一个类或混入一个特质,那么后续则通过关键字“with”来混入其他特质。例如:
scala> class A {
| val a = "Class A"
| }
defined class Ascala> trait B {
| val b = "Trait B"
| }
defined trait Bscala> trait C {
| def c = "Trait C"
| }
defined trait Cscala> object D extends A with B with C
defined object Dscala> D.a
res0: String = Class Ascala> D.b
res1: String = Trait Bscala> D.c
res2: String = Trait C
特质也定义了一个类型,而且类型为该特质的变量,可以指向混入该特质的对象。例如:
scala> trait A
defined trait Ascala> class B extends A
defined class Bscala> val x: A = new B
x: A = B@7cc1f72c
二、特质的层次
特质也可以继承自其他类,或混入任意个特质,这样该特质就是关键字“extends”引入的那个类/特质的子特质。如果没有继承和混入,那么这个特质就是AnyRef类的子特质。前面讲过AnyRef类是所有非值类和特质的超类。当某个类、单例对象或特质用关键字“extends”混入一个特质时,会隐式继承自这个特质的超类。也就是说,类/单例对象/特质的超类,都是由“extends”引入的类或特质决定的。