Looking through the Scala source code, I stumbled across Enumeration.scala
:
abstract class Enumeration(initial: Int, names: String*) extends Serializable {
thisenum =>
def this() = this(0)
def this(names: String*) = this(开发者_如何学编程0, names: _*)
/* Note that `readResolve` cannot be private, since otherwise
the JVM does not invoke it when deserializing subclasses. */
protected def readResolve(): AnyRef = thisenum.getClass.getField("MODULE$").get()
// ... SNIP ...
}
What is the the thisenum =>
for? I couldn't find any info in the "Programming in Scala" book.
The Programming in Scala 2d edition introduce the concept of self type in the section 29.4 "Splitting modules into trait":
The SimpleFoods trait could look as:
trait SimpleFoods {
object Pear extends Food("Pear")
def allFoods = List(Apple, Pear)
def allCategories = Nil
}
So far so good, but unfortunately, a problem arises if you try to define a SimpleRecipes trait like this:
trait SimpleRecipes { // Does not compile
object FruitSalad extends Recipe(
"fruit salad",
List(Apple, Pear), // Uh oh
"Mix it all together."
)
def allRecipes = List(FruitSalad)
}
The problem here is that
Pear
is located in a different trait from the one that uses it, so it is out of scope.
The compiler has no idea thatSimpleRecipes
is only ever mixed together withSimpleFoods
.
There is a way you can tell this to the compiler, however. Scala provides the self type for precisely this situation.
Technically, a self type is an assumed type for this whenever this is mentioned within the class.
Pragmatically, a self type specifies the requirements on any concrete class the trait is mixed into.
If you have a trait that is only ever used when mixed in with another trait or traits, then you can specify that those other traits should be assumed.
In the present case, it is enough to specify a self type ofSimpleFoods
, as shown:
trait SimpleRecipes {
this: SimpleFoods =>
object FruitSalad extends Recipe(
"fruit salad",
List(Apple, Pear), // Now Pear is in scope
"Mix it all together."
)
def allRecipes = List(FruitSalad)
}
Given the new self type,
Pear
is now available.
Implicitly, the reference toPear
is thought of asthis.Pear
.
This is safe, because any concrete class that mixes inSimpleRecipes
must also be a subtype ofSimpleFoods
, which means thatPear
will be a member.
Abstract subclasses and traits do not have to follow this restriction, but since they cannot be instantiated with new, there is no risk that thethis.Pear
reference will fail
It's a self type. See section 29.4 of Programming in Scala Second Edition. I don't think it was covered on the first edition, and I don't have one around to lookup anyway.
All that did, in this example, was make sure thisenum
would refer to Enumeration
's this
from any inner of Enumeration
.
It is not a self type annotation, but just an alias for this since there is no type requirement involved in the question, check this SO question
It is indeed self type annotation. See the official Scala specification:
https://scala-lang.org/files/archive/spec/2.13/13-syntax-summary.html
According to this specification, its context free EBNF syntax is:
SelfType ::= id [‘:’ Type] ‘=>’ | ‘this’ ‘:’ Type ‘=>’
So, basically, this means SelfType has two basic forms. In one form, you can use an id with or without Type. In the other, you can use this
but it must be accompanied with a Type.
As for your question regarding the book, you can find it in section 29.4 of Programming in Scala Second Edition. However, remember that books can be quickly out of date, so you need to refer to the specification.
精彩评论