@MarioGalic answered this question. I'll just make some additions that are too long for comments.
Common misunderstanding is that i
in
class MyClass(i : Int)
is just a constructor parameter and not a field. Actually if we do
import scala.reflect.runtime.universe._
println(reify{
class MyClass(i : Int)
}.tree)
we'll see (Scala 2.13.2)
{
class MyClass extends AnyRef {
<paramaccessor> private[this] val i: Int = _;
def <init>(i: Int) = {
super.<init>();
()
}
};
()
}
So a parameter of primary constructor without val
/var
generates private[this]
field. So
class MyClass(i : Int)
is similar to
class MyClass(private[this] val i : Int)
and NOT similar to Java's
public class MyClass {
public MyClass(int i) {
}
}
without fields.
We can check that i
is a field referring to it with this
inside class body
class MyClass(i : Int) {
println(this.i)
}
new MyClass(1) // prints 1
The field i
is private[this]
so we can't refer it outside the class body (or inside the body on instance different than this
)
class MyClass(i : Int) {
//println(new MyClass(2).i) //doesn't compile
}
//new MyClass(1).i //doesn't compile
I didn't find proper place in Scala specification but such behavior is well-known for a long time. For example in "Scala for the impatient" by Cay S. Horstmann it's written (edition 2, section 5.7):
Construction parameters can also be regular method parameters, without val
or var
. How these parameters are processed depends on their usage inside the class.
If a parameter without val
or var
is used inside at least one method, it becomes a field. For example,
class Person(name: String, age: Int) {
def description = name + " is " + age + " years old"
}
declares and initializes immutable fields name
and age
that are object-private. Such a field is the equivalent of a private[this] val
field (see Section 5.4,“Object-Private Fields,” on page 56).
Otherwise, the parameter is not saved as a field. It’s just a regular parameter that can be accessed in the code of the primary constructor. (Strictly speaking, this is an implementation-specific optimization.)
Actually in 2.13.2 I can't confirm the second case.
Now let's have two classes.
Scala doesn't allow
class Superclass {
val i: Int = 1
}
class Subclass extends Superclass {
//val i: Int = 2 //doesn't compile
}
unless we add override
class Superclass {
val i: Int = 1
}
class Subclass extends Superclass {
override val i: Int = 2
}
But if Superclass
's field is private[this]
everything is ok without override
class Superclass {
private[this] val i: Int = 1
}
class Subclass extends Superclass {
val i: Int = 2
}
Actually if we try to add override
this will not compile.
The reason is this being not overriding. One of fields is private[this]
i.e. not accessible outside the object where the field is defined in, so these are just two different fields:
class Superclass {
private[this] val i: Int = 1
}
class Subclass extends Superclass {
val i: Int = 2
println(this.i) // or just println(i)
// println((this: Superclass).i) //doesn't compile
}
new Subclass
//2
or
class Superclass {
val i: Int = 1
}
class Subclass extends Superclass {
private[this] val i: Int = 2
println(this.i) // or just println(i)
println((this: Superclass).i)
}
new Subclass
//2
//1
So in our case
class Superclass(var i : Int)
class Subclass(i : Int) extends Superclass(0)
are like
class Superclass extends AnyRef {
var i: Int = _
def this(_i: Int) = {
super() //pseudocode
i = _i
}
}
class Subclass extends Superclass {
private[this] val i: Int = _ //pseudocode
def this(_i: Int) = {
super(0) //pseudocode
i = _i //pseudocode because "i" is a val -- well, compiler can do things that we can't do in source code
}
}
Inside Subclass
this.i
or just i
refers to Subclass
's field private[this] val i: Int
and (this: Superclass).i
refers to Superclass
's field var i: Int
.
Do scala constructor parameters default to private val?
Scala Constructor Parameters
https://www.scala-lang.org/old/node/8384.html