@MarioGalic 回答了这个问题。我将添加一些对 cmets 来说太长的内容。
常见的误解是i在
class MyClass(i : Int)
只是一个构造函数参数,而不是一个字段。实际上,如果我们这样做
import scala.reflect.runtime.universe._
println(reify{
class MyClass(i : Int)
}.tree)
我们会看到(Scala 2.13.2)
{
class MyClass extends AnyRef {
<paramaccessor> private[this] val i: Int = _;
def <init>(i: Int) = {
super.<init>();
()
}
};
()
}
所以没有val/var的主构造函数的参数生成private[this]字段。所以
class MyClass(i : Int)
类似于
class MyClass(private[this] val i : Int)
与 Java 不相似
public class MyClass {
public MyClass(int i) {
}
}
没有字段。
我们可以检查i 是一个引用它的字段,在类主体中带有this
class MyClass(i : Int) {
println(this.i)
}
new MyClass(1) // prints 1
字段i 是private[this],所以我们不能在类主体之外引用它(或者在与this 不同的实例主体内部)
class MyClass(i : Int) {
//println(new MyClass(2).i) //doesn't compile
}
//new MyClass(1).i //doesn't compile
我没有在 Scala 规范中找到合适的位置,但这种行为早已为人所知。例如,在 Cay S. Horstmann 的“不耐烦的 Scala”中,它是这样写的(第 2 版,第 5.7 节):
构造参数也可以是常规方法参数,不带val或var。这些参数的处理方式取决于它们在类中的用法。
-
如果没有val 或var 的参数在至少一个方法中使用,则它成为一个字段。例如,
class Person(name: String, age: Int) {
def description = name + " is " + age + " years old"
}
声明并初始化对象私有的不可变字段name 和age。这样的字段等效于 private[this] val 字段(请参阅第 56 页的第 5.4 节“对象私有字段”)。
-
否则,参数不会保存为字段。它只是一个可以在主构造函数的代码中访问的常规参数。 (严格来说,这是一个特定于实现的优化。)
其实在2.13.2我无法确认第二种情况。
现在让我们有两个类。
Scala 不允许
class Superclass {
val i: Int = 1
}
class Subclass extends Superclass {
//val i: Int = 2 //doesn't compile
}
除非我们添加override
class Superclass {
val i: Int = 1
}
class Subclass extends Superclass {
override val i: Int = 2
}
但是如果Superclass 的字段是private[this] 没有override 一切正常
class Superclass {
private[this] val i: Int = 1
}
class Subclass extends Superclass {
val i: Int = 2
}
实际上,如果我们尝试添加override,这将无法编译。
原因是这不是压倒一切的。其中一个字段是private[this],即在定义该字段的对象之外无法访问,因此它们只是两个不同的字段:
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
或
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
所以在我们的例子中
class Superclass(var i : Int)
class Subclass(i : Int) extends Superclass(0)
像
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
}
}
在Subclassthis.i 或只是i 内指的是Subclass 的字段private[this] val i: Int,(this: Superclass).i 指的是Superclass 的字段var i: Int。
Do scala constructor parameters default to private val?
Scala Constructor Parameters
https://www.scala-lang.org/old/node/8384.html