在Scala中Trait为重用代码的一个基本单位。一个Traits封装了方法和变量,和Interface相比,它的方法可以有实现,这一点有点和抽象类定义类似。但和类继承不同的是,Scala中类继承为单一继承,也就是说子类只能有一个父类。当一个类可以和多个Trait混合,这些Trait定义的成员变量和方法也就变成了该类的成员变量和方法,由此可以看出Trait集合了Interface和抽象类的优点,同时又没有破坏单一继承的原则。
下面我们来看看Trait的基本用法:
定义一个Trait的方法和定义一个类的方法非常类似,除了它使用trait而非class关键字来定义一个trait.
trait Philosophical{ def philosophize() { println("I consume memeory, therefor I am!") } }
这个Trait名为Philosophical。它没有声明基类,因此和类一样,有个缺省的基类AnyRef。它定义了一个方法,叫做philosophize。这是个简单的Trait,仅够说明Trait如何工作。
一但定义好Trait,它就可以用来和一个类混合,这可以使用extends或with来混合一个trait.例如:
class Frog extends Philosophical{ override def toString="gree" }
这里我们使用extends为Frog添加Philosophical Trait属性,因此Frog 缺省继承自Philosophical的父类AnyRef,这样Frog类也具有了Philosophical的性质(因此Trait也可以翻译成特质,但后面我们还是继续使用Trait原文)。
scala> val frog = new Frog frog: Frog = green scala> frog.philosophize I consume memeory, therefor I am!
可以看到Frog添加了Philosophical(哲学性)也具有了哲学家的特性,可以说出类似“我思故我在”的话语了。和Interface一样,Trait也定义一个类型,比如:
scala> val phil:Philosophical = frog phil: Philosophical = green scala> phil.philosophize I consume memeory, therefor I am!
变量phil的类型为Philosophical。
如果你需要把某个Trait添加到一个有基类的子类中,使用extends继承基类,而可以通过with 添加Trait。比如:
class Animal class Frog extends Animal with Philosophical{ override def toString="green" }
还是和Interface类似,可以为某个类添加多个Trait属性,此时使用多个with即可,比如:
class Animal trait HasLegs class Frog extends Animal with Philosophical with HasLegs{ override def toString="green" }
目前为止你看到的例子中,类Frog都继承了Philosophical的philosophize实现。此外Frog也可以重载philosophize方法。语法与重载基类中定义的方法一样。
class Animal trait HasLegs class Frog extends Animal with Philosophical with HasLegs{ override def toString="green" def philosophize() { println("It ain't easy being " + toString + "!") } }
因为Frog的这个新定义仍然混入了特质Philosophize,你仍然可以把它当作这种类型的变量使用。但是由于Frog重载了Philosophical的philosophize实现,当你调用它的时候,你会得到新的回应:
scala> val phrog:Philosophical = new Frog phrog: Philosophical = green scala> phrog.philosophize It ain't easy being green!
这时你或许推导出以下结论:Trait就像是带有具体方法的Java接口,不过其实它能做的更多。Trait可以,比方说,声明字段和维持状态值。实际上,你可以用Trait定义做任何用类定义做的事,并且语法也是一样的,除了两点。第一点,Trait不能有任何“类”参数,也就是说,传递给类的主构造器的参数。换句话说,尽管你可以定义如下的类:
class Point(x: Int, y: Int)
但下面的Trait定义直接报错:
scala> trait NoPoint(x:Int,y:Int) <console>:1: error: traits or objects may not have parameters trait NoPoint(x:Int,y:Int)