trait T extends S
并且。
trait T { this: S =>
这两者都可以实现类似的功能(即在创建实例之前必须定义S
中的抽象方法)。它们的区别是什么?为什么要选择其中之一?
trait T extends S
trait T { this: S =>
这两者都可以实现类似的功能(即在创建实例之前必须定义S
中的抽象方法)。它们的区别是什么?为什么要选择其中之一?
自类型注释允许您表达循环依赖关系。例如:
trait A extends B
trait B { self: A => }
简单的继承方式无法实现这一点。
我建议使用自类型来进行依赖管理:这个特质需要另一个特质被混入。而我会使用继承来完善另一个特质或接口。
举个例子:
trait FooService
trait FooRemoting { this : FooService => }
trait FooPersistence { this : FooService => }
object Services extends FooService with FooRemoting with FooPersistence
现在,如果FooRemoting和FooPersistence都继承自FooService,并且FooService有成员和方法,那么Services会是什么样子?
而对于继承,我们将会有如下内容:
trait Iterator[T] {
def hasNext : boolean
def next : T
}
trait InfiniteIterator[T] extends Iterator[T] {
def hasNext = true
}
我知道这个问题是老问题了,但我想添加一些澄清和示例。
特质继承和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!
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
只是一只狗,所以我们不能调用 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 **/ }
这种代码在依赖注入(蛋糕模式)中非常常见。
最后,使用我们的traits的人可以决定它们被使用的顺序,因此由于Trait线性化,最终结果可能会有所不同,尽管使用的traits相同。
使用普通继承无法做到这一点,traits和类之间的关系是固定的。
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
希望这对您有所帮助。
自从提出这个问题后,我看到了以下几篇文章:
Spiros Tzavellas谈到了使用特质作为公共接口和自身类型作为必须由实现类混合的辅助类。
总之,如果我们想要将方法实现移动到特质内部,那么我们就有可能会污染那些特质的接口,因为那些抽象方法是支持具体方法实现且与特质的主要职责无关的。解决这个问题的方法是将这些抽象方法移动到其他特质中,并使用自身类型注释和多重继承组合这些特质。
例如:
trait PublicInterface { this: HelperTrait =>
// Uses helperMethod
}
trait HelperTrait {
def helperMethod = // ...
}
class ImplementationClass extends PublicInterface with HelperTrait
Scala之旅讨论了使用自类型注释和抽象类型成员 - 大概不可能extend
一个抽象类型成员(?)
PublicInterface
子类的实现关注点。为什么不使用protected
方法呢? - Robin Greentrait A { self: X => def a = reuseme}
trait B { self: X => def b = a }
class X extends A with B { def reuseme=null }
虽然这并没有回答你的问题,但我一直在尝试理解自类型注释,最终在各种答案中迷失了方向,不知怎么就陷入了循环,反复思考了你关于使用自类型注释来说明依赖关系的问题。
因此,在这里我发布一个使用案例的描述,其中自类型注释得到了很好的说明,即类似于“this”作为子类型的类型安全案例:
http://programming-scala.labs.oreilly.com/ch13.html#SelfTypeAnnotationsAndAbstractTypeMembers
希望这对那些偶然看到这个问题的人有所帮助(就像我一样,在开始探索之前没有时间阅读Scala书籍 :-) )