Scala:继承中的构造函数顺序
有人能详细解释一下scala继承中调用构造函数的顺序吗?假设我有:Scala:继承中的构造函数顺序,scala,Scala,有人能详细解释一下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 } }
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()
,应该设置a=1
。但是scala回到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;
()
}
}
让我们看看编译器在编译时生成了什么(使用
-Xprint:jvm
标志):
它调用A.super
,这是对象
,然后调用A.this.compute()
。此方法初始化a
以保持值1。初始化完成后,ImplA
将a
设置为0
,就像您在构造函数初始化期间告诉它的那样。这就是为什么您看到a
的值0
综上所述,执行流程如下:
ImplA
调用A
s init方法A
调用compute
,在ImplA
ImplA.compute
分配a
值1
ImplA
分配a
值0
有关更多信息,请参见编辑:没关系,我太笨了,不会读书<代码>计算()不会在任何地方调用。您刚刚定义了它。@stefanfisher
compute
在s构造函数中调用。谢谢,我甚至没有想到使用-Xprint:jvm
标志!我将看到我的编辑会发生什么,然后自己理解NPE!=)@谢谢!这确实有助于理解。我还发现,将其设置为lazy val
(但需要将var a
放入绑定类)也可以正常工作(请参见我对问题的编辑)。
class ImpA extends test.A {
@volatile private[this] var bitmap$0: Boolean = false;
private def b$lzycompute(): test.ImpA$B = {
{
ImpA.this.synchronized({
if (ImpA.this.bitmap$0.unary_!())
{
ImpA.this.b = new test.ImpA$B(ImpA.this);
ImpA.this.bitmap$0 = true;
()
};
scala.runtime.BoxedUnit.UNIT
});
()
};
ImpA.this.b
};
lazy private[this] var b: test.ImpA$B = _;
<stable> <accessor> lazy def b(): test.ImpA$B = if (ImpA.this.bitmap$0.unary_!())
ImpA.this.b$lzycompute()
else
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>();
()
}
};
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;
()
}
};
def <init>(): com.testing.A = {
A.super.<init>();
A.this.data = A.this.compute();
()
}