我知道这个问题很老,但我想补充一些说明和例子。
trait 继承和 self 类型之间存在三个主要区别。
语义
继承是对象范式中耦合度最高的关系之一,如果A扩展了B,那么A就是B。
假设我们有以下代码,
trait Animal {
def stop():Unit = println("stop moving")
}
class Dog extends Animal {
def bark:String = "Woof!"
}
val goodboy:Dog = new Dog
goodboy.bark
// Woof!
我们说狗是动物。我们可以将消息bark 和stop 发送给goodboy,因为它是狗,两种方法都懂。
现在假设我们有一个新的特征,
trait Security {
this: Animal =>
def lookout:Unit = { stop(); println("looking out!") }
}
这次安全不是动物,这很好,因为如果我们确认安全是动物,这在语义上是不正确的,它们是不同的概念,可以一起使用。
所以现在我们可以创造一种新的狗,
val guardDog = new Dog with Security
guardDog.lookout
// stop moving
// looking out!
guardDog 是狗、动物和安全。它可以理解stop、bark 和lookout,因为它是有安全保障的狗。
但是如果我们像这样创造一条新狗会发生什么?
val guardDog2:Dog = new Dog with Security
guardDog2.lookout // no such method!
guardDog2 只是一个 Dog,所以我们不能调用 lookout 方法。 (好吧,这是一只带安全性的狗,但我们只看到一只狗)
循环依赖
自类型允许我们在类型之间创建循环依赖。
trait Patient {
this: Reader =>
def isQuite:Boolean = isReading
def isSlow:Boolean = true
}
trait Reader {
this: Patient =>
def read():Unit = if(isSlow) println("Reading Slow...") else println("Reading Fast...")
def isReading = true
}
val person = new Patient with Reader
以下代码无法编译。
trait Patient extends Reader { /** code **/}
trait Reader extends Patient { /** code **/ }
这种代码在依赖注入(蛋糕模式)中很常见。
多功能性
最后但同样重要的是,谁使用我们的特征可以决定它们的使用顺序,因此由于特征线性化,尽管使用的特征相同,但最终结果可能会有所不同。
使用普通继承我们不能这样做,特征和类之间的关系是固定的。
trait Human {
def isGoodForSports:Boolean
}
trait Programmer extends Human {
def readStackOverflow():Unit = println("Reading...")
override def isGoodForSports: Boolean = false
}
trait Sportsman extends Human {
def play():Unit = println("Playing something")
override def isGoodForSports: Boolean = true
}
val foo = new Programmer with Sportsman
foo.isGoodForSports
// true
val bar = new Sportsman with Programmer
bar.isGoodForSports
// false
希望这能有用。