Scala:继承中的构造函数命令

时间:2017-08-16 13:23:26

标签: 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()的{​​{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

1 个答案:

答案 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.初始化完成后,ImplAa设置为0,正如您在构造函数初始化期间所说的那样。这就是为什么您会看到0的值a

总之,执行流程如下:

  1. ImplA调用A的init方法
  2. A调用compute,在ImplA
  3. 上调用
  4. ImplA.compute分配a1
  5. ImplA分配a0
  6. 有关详情,请参阅http://docs.scala-lang.org/tutorials/FAQ/initialization-order.html