Reputation: 1239
I generally understand how Scala deals with constructor parameters, i.e. generating getter for val and getter/setter for var. I also have found quite a few discussions about name clashes between constructor parameters and class members.
For those names w/o val & var, I expect that if they're prefixed with this.
, then class members instead should
be used. But that's not the case.
class A(val a: Int) {
def m = println("A.a: " + a)
}
class B(a: Int) extends A(2 * a) {
override def m = {
super.m
println("@a: " + a)
println("this.a: " + this.a) // this is @a, not A.a
}
}
object TestApp extends App {
val b = new B(10)
b.m
println(b.a); // this is A.a
}
Output:
A.a: 20
@a: 10
this.a: 10
20
I feel that it's slightly weird but is there a design reason behind it?
Upvotes: 1
Views: 205
Reputation: 21557
As you wrote in your question Scala generates a getter for a val
, so when you declare a val
parameter in you A
class, it actually has a method m
and a field a
, which are inherited by the B
class. But you also have an a
parameter in you B
class, which is doubled and passed as an argument to the super constructor of A
. To understand what's going on, turn on option -Xprint:typer
for your repo and paste your code, e.g A
represented like this:
class A extends scala.AnyRef {
<paramaccessor> private[this] val a: Int = _;
<stable> <accessor> <paramaccessor> def a: Int = A.this.a;
def <init>(a: Int): A = {
A.super.<init>();
()
};
def m: Unit = scala.this.Predef.println("A.a: ".+(A.this.a))
}
As you can see Scala created a hidden private[this]
field for you a
parameter. And here is representation for the B
class:
class B extends A {
<paramaccessor> private[this] val a: Int = _;
def <init>(a: Int): B = {
B.super.<init>(2.*(a));
()
};
override def m: Unit = {
B.super.m;
println("@a: ".+(B.this.a));
println("this.a: ".+(this.a))
}
}
As you can see Scala also defined private[this]
field for your a
parameter in the B
class. But because you didn't mark it as a val
value, it didn't generate new a
getter, which is still defined as A.this.a
, but not B.this.a
, so that's why you r getting 20
when calling b.a
.
As an answer, i don't think that there is some complex design behind this, it looks logical and quite reasonable. If you rename your a
param in B
class constructor and pass it to A
, nothing would change.
Upvotes: 2