有人能詳細解釋一下我在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
}
}
val inst = new ImpA
然後似乎inst.a == 0
,所以我想這會發生什麼情況是,當ImpA
的構造函數被調用,然後,A
構造也被稱爲,這實際上觸發compute()
應該設置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
,那麼它的工作原理:
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>();
()
}
};
編輯:沒關係,我太傻看... '計算()'不被任何調用。你剛剛定義它。 –
@StefanFischer'compute'在'A'構造函數中被調用。 –