问题
In Scala, I've seen the constructs
trait T extends S
and
trait T { this: S =>
used to achieve similar things (namely that the abstract methods in S
must be defined before an instance may be created). What's the difference between them? Why would you use one over the other?
回答1:
I'd use self-types for dependency-management: This trait requires another trait to be mixed in. And I'd use inheritance to refine another trait or interface.
Just as an example:
trait FooService
trait FooRemoting { this : FooService => }
trait FooPersistence { this : FooService => }
object Services extends FooService with FooRemoting with FooPersistence
Now, if FooRemoting and FooPersistence both would have inherited from FooService, and FooService has members and methods, how would Services look like?
Whereas for inheritance, we'd have something like:
trait Iterator[T] {
def hasNext : boolean
def next : T
}
trait InfiniteIterator[T] extends Iterator[T] {
def hasNext = true
}
回答2:
Self type annotations allow you to express cyclic dependencies. For instance:
trait A extends B
trait B { self: A => }
This is not possible with simple inheritance.
回答3:
Since asking the question I came across these posts:
Spiros Tzavellas talks about using a trait as the public interface and the self type as a helper that must be mixed in by the implementation class.
In conclusion, if we want to move method implementations inside traits then we risk polluting the interface of those traits with abstract methods that support the implementation of the concrete methods and are unrelated with the main responsibility of the trait. A solution to this problem is to move those abstract methods in other traits and compose the traits together using self type annotations and multiple inheritance.
For example:
trait PublicInterface { this: HelperTrait =>
// Uses helperMethod
}
trait HelperTrait {
def helperMethod = // ...
}
class ImplementationClass extends PublicInterface with HelperTrait
A Tour of Scala discusses using self type annotations with abstract type members - presumably it's not possible to extend
an abstract type member(?)
回答4:
The answer is "circularity". But not only.
Self type annotation solves for me the fundamental problem of inheritance: what you inherit from cannot use what you are. With the self type, everything becomes easy.
My pattern is the following and can be considered as a degenerated cake:
trait A { self: X => def a = reuseme}
trait B { self: X => def b = a }
class X extends A with B { def reuseme=null }
You can explode your class in multiple behaviours which can be called from anywhere in the assembly, while staying cleanly typed. No need for the painful indirection too often (and wrongly) identified with the cake pattern.
Half (if not the totality) of the convoluted Java DI frameworks of the last ten years have been devoted to do this, of course without the typing. People still using JAVA in this domain are clearly loosing their time: "SCALA ouakbar".
回答5:
I know this question is old but I would like to add some clarification and examples.
There are three main differences between trait inheritance and self types.
Semantics
Inheritance is one of the relationships with the most coupling of the object paradigm, if A extends B, that means that A is a B.
Let's say we have the following code,
trait Animal {
def stop():Unit = println("stop moving")
}
class Dog extends Animal {
def bark:String = "Woof!"
}
val goodboy:Dog = new Dog
goodboy.bark
// Woof!
We are saying that a Dog is an Animal. We can send the messages bark
and stop
to goodboy
because is a Dog, it understand both methods.
Now suppose we have a new trait,
trait Security {
this: Animal =>
def lookout:Unit = { stop(); println("looking out!") }
}
This time Security is NOT an Animal, and that is fine because would be semantically incorrect if we affirm that a Security is an Animal, they are different concepts, that can be used together.
So now we can create a new kind of dog,
val guardDog = new Dog with Security
guardDog.lookout
// stop moving
// looking out!
guardDog
is a Dog, an Animal and Security. It understand stop
, bark
and lookout
because is a Dog with Security.
But what happens if we create a new dog like this?
val guardDog2:Dog = new Dog with Security
guardDog2.lookout // no such method!
guardDog2
is just a Dog, so we can't call lookout
method. (okok, it's a Dog with Security, but we just see a Dog)
Cyclic Dependencies
Self Types allow us to create cyclic dependencies between types.
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
The following code doesn't compile.
trait Patient extends Reader { /** code **/}
trait Reader extends Patient { /** code **/ }
This kind of code is very common in dependency injection (cake pattern).
Versatility
Last but not least, who uses our traits can decide the order in which they are used, so thanks to Trait Linearization the final result can be different although the traits used are the same.
With normal inheritance we can't do that, the relations between traits and classes are fixed.
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
Hope this can be useful.
回答6:
Although it doesn't answer your question, I was trying to understand the self-type annotations and basically got lost in answers, and somehow ended up cycling through variations of your question, which focuses on usage of self-type annotations for stating dependencies.
So here I post a description of an use case where self-type annotations are well illustrated, namely something like a type-safe case of 'this' as a subtype:
http://programming-scala.labs.oreilly.com/ch13.html#SelfTypeAnnotationsAndAbstractTypeMembers
hoping that it would be helpful to those who end up on this question by chance (and, like me, didn't have time to read a scala book before starting to explore :-) )
来源:https://stackoverflow.com/questions/2224932/difference-between-trait-inheritance-and-self-type-annotation