Named Scala self-type still shadows "this"?
Working through these posts had me thinking I understood self-types, at least somewhat.
So I created an example which failed as expected:
scala> trait A { val v = "a" }
defined trait A
scala> trait B { this :A => ; var v = "" ; this.v = "b" }
<console>:6: error: reassignment to val
trait B { this :A => ; var v = "" ; this.v = "b" }
^
The self-type's "this" shadows B's "this" -- it looks weird, but makes sense.
It would seem wise, then, to give the self-type a different name. I did this and was rather surprised:
scala> trait C { a :A => ; var v = "" ; this.v = "c" }
<console>:6: error: reassignment to val
trait C { a :A => ; var v = "" ; this.v = "c" }
开发者_如何学编程 ^
It's still shadowed???
Changing the name of the 'local' variable let things compile, which makes sense:
scala> trait D { a :A => ; var w = "" ; this.w = a.v }
defined trait D
(And the self-type name can optionally be used to clarify which "v" to use.)
Okay. Which means that the following should fail?
scala> trait E { this :A => ; var w = "" ; this.w = this.v }
defined trait E
Huh? Which this is this? :-(
So... is there a point to naming self-types? "this" seems to end up shadowed regardless.
Or is this an edge-case of scoping rules, where the self-type's "this" takes precedence over the trait's "this" -- and one should just avoid using the same names for things in related traits?
Your problem is not the name of the self type (in all your examples both this
and the alternate self-type name refer to the very same thing and have the same type, namely ‘the greatest lower bound of B
and A
’ [§5.1, Scala Language Spec]) but that you try to define a field with the same name again without explicitly overriding it.
Look at the simpler example:
trait A { val v = "a" }
trait B { this: A =>
var v = "b"
}
new A with B {} // does not compile
<console>:9: error: overriding value v in trait A$class of type java.lang.String;
variable v in trait B$class of type java.lang.String needs `override' modifier
new A with B {}
So, even though, you don’t get an error in defining B
, you’re simply not able to use it anyway.
This would work
trait A { val v = "a" }
trait B { this:A => override val v = "b" }
new A with B {}
Here, you are explicitly overriding A
’s v
in B
. (Note that new B with A {}
would fail because B
needs to come last.) Also, it has to be a val
because in most cases you cannot really override var
s and you cannot override something else using a var
.
Generally, you should not be concerned about the name of the self-type in these simple cases. As long as you do not create another trait or class inside B
, both this
and whatever you’d call your self-type variable will point to the same thing. There’ll be no shadowing. If you had a new trait inside B
, and you needed to refer to the instance of B
inside that trait, you’d need another name for your self-type.
Consider this
trait B { this =>
val v = "b"
trait C {
val v = "c"
println(this.v)
}
new C {}
}
new B {}
// prints c
vs this:
trait B { self =>
val v = "b"
trait C {
val v = "c"
println(this.v) // prints c
println(self.v) // prints b
}
new C {}
}
new B {}
(Without any further type annotation, you could leave out all of the this
in this example.)
The self-type is not shadowing this. It IS the type of this. (Actually the self-type is the intersection: in trait A { self: B => ...} the type of this is A with B.) You can supply a name when giving the self type as a convenience to disambiguate multiple thises, but as you never have more than one this in any of your code snippets, it's irrelevant.
[...] where the self-type's "this" takes precedence over the trait's "this"
You have this same idea several places, that there is more than one this. There isn't. There is only this. You don't get another this until you declare another template.
trait A {
self1 =>
trait B {
self2 =>
// Here unqualified this refers to B and not A, so "self1" is useful.
// ...but not necessary, because A.this.xxx does the same thing.
}
}
精彩评论