Scala中的类的初始化是如何进行的?

问题描述 投票:2回答:1

下面的代码抛出了一个 java.lang.NullPointerException 因为性状被过早地初始化了。

trait DummyTrait {
  def intSeq: Seq[Int]
  require(intSeq.exists(_ > 2))
}
object Dummy extends DummyTrait {
  val extraIntSeq: Seq[Int] = Seq(-2,-3)
  override def intSeq = Seq(1,0,4) ++ extraIntSeq
}

Dummy.intSeq

然而,下面的简单修改就解决了这个问题,但我不明白为什么。

trait DummyTrait {
  def intSeq: Seq[Int]
  require(intSeq.exists(_ > 2))
}
object Dummy extends DummyTrait {
  lazy val extraIntSeq: Seq[Int] = Seq(-2,-3) // using `def` also works
  override def intSeq = Seq(1,0,4) ++ extraIntSeq
}

Dummy.intSeq

我发现 这篇关于被覆盖的val为NULL的文档。但似乎并不适用于上面的例子,因为这个修复方法并没有涉及到在超类的接口中定义的变量。

另外,上面提出的解决方案是否是一种反模式?作为一个正在开发特质的人,我应该如何执行对抽象值的要求,而不至于让实现子类的用户感到惊讶,因为潜在的 NullPointerException?

观察..: 我使用的是Scala 2.13.0版本。

scala anti-patterns
1个回答
3
投票

在第一种情况下 extraIntSeq 在构造函数中被初始化 之后 超级构造函数被调用,因此NPE

  object Dummy extends DummyTrait {
    private[this] val extraIntSeq: Seq = _;
    ...
    def <init>($outer: O): Dummy.type = {
      Dummy.super.<init>();
      Dummy.this.extraIntSeq = ... // note that it comes AFTER super
      ()
    }
  }

而在后者中,懒惰的val被转化为方法。


3
投票

到目前为止,你可以使用早期的初始化器

object Dummy extends {
  val extraIntSeq: Seq[Int] = Seq(-2,-3)
  override val intSeq = Seq(1,0,4) ++ extraIntSeq
} with DummyTrait

在Scala中,什么是 "早期初始化器"?

https:/dotty.epfl.chdocsreferencedropped-featuresearly-initializers.html。

https:/contributors.scala-lang.orgtproposal-to-remove-early-initializers-from-the-language2144。

https:/github.comscalascala-devissues513。

© www.soinside.com 2019 - 2024. All rights reserved.