特质(trait)是scala中代码复用的基础单元。特质封装了方法和字段的定义,并可以通过混入到类中重用他们。与类的继承时每个类都只能继承唯一的超类不同,类可以混入任意多个特质。
最常用的两种方式:拓宽瘦接口为胖接口
定义可堆叠的改变
12.1特质是如何工作的
特质的定义除了使用关键字trait之外,与类的定义无异。
trait Philosophical{
def philosophize(){
println("I consume memory,therefore I am")
}
}
//这个特质名为Philosophical.他没有 超类,因此和类一样,有个默认的超类AnyRef.他定义了一个
//具体的方法philosophize
//一旦定义,就可以使用extends 或with 关键字混入类中,是混入特质而不是继承他们。
class Frog extends Philosophical{
override def toString = "green"
}
//使用extends关键字混入特质;这种情况下隐式的继承了特质的超类。举例来说,Frog类是AnyRef的子类,并混入了Philosophical特质。
//从特质继承的方法可以像从超类继承的方法那样使用。
val frog = new Frog
println(frog.toString)
println(frog.philosophize())
val phil : Philosophical = frog
println(phil)
println(phil.philosophize())
如果想把特质混入显式扩展超类的类中,可以用extends指明带扩展的超类,用with混入特质。
//如果想混入多个特质,都加在with字句就可以了,
class Frog extends Animal with philosophical{
override def toString = "green"
}
//Frog也可以重写philosophical方法。语法与重写超类中定义的方法一样。
class Frog extends Animal with philosophical{
override def toString = "green"
override def philosophize(){
println("It ain't easy being" + toString +"!")
}
}
//有人说,特质就像是带有具体方法的Java接口。不过他其实能做更多的事情。例如特质可以声明字段和维持状态值
//实际上,你也可以用特质定义任何类可以做的事情,并且除了以下以下两点之外,连语法都一样。
//第一点:特质不能有任何类参数,即传递给类的主构造器参数。换句话说,你可以这样定义类:
class Point(x: Int,y: Int)
//但是下面这个定义特质则是错误的,并且不能编译通过
trait NoPoint(x: Int. y: Int)
//另外,super调用都是静态绑定的,而特质中他们是动态绑定的
12.2 瘦接口对阵胖接口
特质的一种主要应用方式是可以根据类已有的方法自动为类添加方法。也就是说,特质可以丰满
一个瘦接口,把它变成胖接口。
瘦接口与胖接口的对阵体现了面向对象设计中常会面临的在实现者与接口用户之间的权衡。胖接
口有更多的方法,对于调用者来说更便捷。客户可以捡一个完全符合他们功能需要的方法。另一
方面瘦接口有较少的方法,对于实现者来说更简单。然而调用瘦接口的客户因此要写更多的代码。
由于没有更多可选的方法调用,他们或许不得不选一个不太完美匹配他们所需的方法并为了使用
它写一些额外的代码。
Java的接口常常是过瘦而非过胖。例如,从Java 1.4开始引入的CharSequence接口,是对于字
串类型的类来说通用的瘦接口,它持有一个字符序列。下面是把它看作Scala特质的定义:
trait CharSequence {
def charAt(index: Int): Char
def length: Int
def subSequence(start: Int, end: Int): CharSequence
def toString(): String
}
尽管类String成打的方法中的大多数都可以用在任何CharSequence上,Java的CharSequence
接口定义仅提供了4个方法。如果CharSequence代以包含全部String 接口,那它将为
CharSequence的实现者压上沉重的负担。任何实现Java里的CharSequence接口的程序员将不
得不定义一大堆方法。因为Scala特质可以包含具体方法,这使得创建胖接口大为便捷。
在特质中添加具体方法使得胖瘦对阵的权衡大大倾向于胖接口。不像在Java 里那样,在Scala
中添加具体方法是一次性的劳动。你只要在特质中实现方法一次,而不再需要在每个混入特质的
方法中重新实现它。因此,与没有特质的语言相比,Scala里的胖接口没什么工作要做。
要使用特质丰满接口,只要简单地定义一个具有少量抽象方法的特质——特质接口的瘦部分——
和潜在的大量具体方法,所有的都实现在抽象方法之上。然后你就可以把丰满了的特质混入到类
中,实现接口的瘦部分,并最终获得具有全部胖接口内容的类。