Scala开发教程(47): Trait的基本概念

jerry Scala 2015年11月25日 收藏

在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)