【发布时间】:2017-08-16 13:23:25
【问题描述】:
有人可以详细解释一下在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()应该设置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;
()
}
}
虽然正确理解有点困难,但它非常直截了当地解释了为什么会抛出 NullPointerException。
但是如果你这次使用lazy val b = new B,那么它可以工作:
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>();
()
}
};
【问题讨论】:
-
编辑:没关系,我太笨了,无法阅读...
compute()不会在任何地方调用。你刚刚定义了它。 -
@StefanFischer
compute在As 构造函数中被调用。
标签: scala