有人可以详细解释一下在scala中继承调用构造函数的订单吗?说我有:
abstract class A {
private var data: T = compute()
protected def compute(): T
}
class ImpA extends A {
var a = 0
override def compute() {
a = 1
null.asInstanceOf[T] // doesn't matter
}
}
val inst = new ImpA
然后它出现inst.a == 0
,所以我想发生的事情是,当ImpA
的构造函数被调用时,A
构造函数也会被调用,这实际上会触发应设置compute()
的{{1}}。但是然后scala返回到a = 1
的构造函数并重置ImpA
。是吗?
是否有一些众所周知的模式可以避免这种情况? (我并没有真正尝试解决这个容易处理的问题,但是如果有建议的模式我很想知道它们;但我更喜欢深入了解理解发生了什么,并希望知道为什么重新初始化变量a = 0
可能会对这种情况感兴趣。如果它是a
,那么内部会发生什么,因为它会导致分配几个如果保留逻辑,则引用相同的变量......)。
提前致谢。
编辑 有趣也是指您只需更改val
并使用引用而不是ImpA.a
:
var
然后抛出class ImpA extends A {
class B {
var b = 0
}
val b = new B
override def compute() {
b.b += 1
null.asInstanceOf[T] // doesn't matter
}
}
,因为java.lang.NullPointerException
尚未实例化。遵循b
解决方案,这是它编译成的内容:
Yuval Itzchakov
尽管理解起来有点困难,但它很简单地解释了 abstract class A extends Object {
private[this] var data: Object = _;
<accessor> private def data(): Object = A.this.data;
<accessor> private def data_=(x$1: Object): Unit = A.this.data = x$1;
protected def compute(): Object;
def <init>(): test.A = {
A.super.<init>();
A.this.data = A.this.compute();
()
}
};
class ImpA extends test.A {
private[this] val b: test.ImpA$B = _;
<stable> <accessor> def b(): test.ImpA$B = ImpA.this.b;
override def compute(): Unit = {
ImpA.this.b().b_=(ImpA.this.b().b().+(1));
{
(null: Object);
()
}
};
override <bridge> <artifact> def compute(): Object = {
ImpA.this.compute();
scala.runtime.BoxedUnit.UNIT
};
def <init>(): test.ImpA = {
ImpA.super.<init>();
ImpA.this.b = new test.ImpA$B(ImpA.this);
()
}
};
class ImpA$B extends Object {
private[this] var b: Int = _;
<accessor> def b(): Int = ImpA$B.this.b;
<accessor> def b_=(x$1: Int): Unit = ImpA$B.this.b = x$1;
<synthetic> <paramaccessor> <artifact> protected val $outer: test.ImpA = _;
<synthetic> <stable> <artifact> def $outer(): test.ImpA = ImpA$B.this.$outer;
def <init>($outer: test.ImpA): test.ImpA$B = {
if ($outer.eq(null))
throw null
else
ImpA$B.this.$outer = $outer;
ImpA$B.super.<init>();
ImpA$B.this.b = 0;
()
}
}
被抛出的原因。
但是如果你使用NullPointerException
这个时间,那么它可以工作:
lazy val b = new B
答案 0 :(得分:3)
让我们看一下编译时编译器生成的内容(使用-Xprint:jvm
标志):
class ImpA extends com.testing.A {
private[this] var a: Int = _;
<accessor> def a(): Int = ImpA.this.a;
<accessor> def a_=(x$1: Int): Unit = ImpA.this.a = x$1;
override def compute(): String = {
ImpA.this.a_=(1);
(null: String)
};
override <bridge> <artifact> def compute(): Object = ImpA.this.compute();
def <init>(): com.testing.ImpA = {
ImpA.super.<init>();
ImpA.this.a = 0;
()
}
};
我们看到了什么?我们看到为ImplA
运行的构造函数(定义为<init>
方法)首先调用ImpA.super.<init>()
,这是对A
的调用,首先初始化自身。 A
的初始化代码如下所示:
def <init>(): com.testing.A = {
A.super.<init>();
A.this.data = A.this.compute();
()
}
调用A.super
Object
,然后调用A.this.compute()
。此方法初始化a
以保存值1.初始化完成后,ImplA
将a
设置为0
,正如您在构造函数初始化期间所说的那样。这就是为什么您会看到0
的值a
。
总之,执行流程如下:
ImplA
调用A
的init方法A
调用compute
,在ImplA
ImplA.compute
分配a
值1
ImplA
分配a
值0
有关详情,请参阅http://docs.scala-lang.org/tutorials/FAQ/initialization-order.html