斯卡拉 - 初始化瓦尔斯的顺序(Scala - initialization order of va

2019-07-18 21:27发布

我有这样一段代码从文件中加载属性:

class Config {
  val properties: Properties = {
    val p = new Properties()
    p.load(Thread.currentThread().getContextClassLoader.getResourceAsStream("props"))
    p
  }

  val forumId = properties.get("forum_id")
}

这似乎是工作的罚款。

我曾尝试初始化移动properties到另一个VAL, loadedProperties ,就像这样:

class Config {
  val properties: Properties = loadedProps
  val forumId = properties.get("forum_id")

  private val loadedProps = {
    val p = new Properties()
    p.load(Thread.currentThread().getContextClassLoader.getResourceAsStream("props"))
    p 
   }

}

但是,这是行不通的! ( properties是空properties.get("forum_id")

为什么会是这样? 是不是loadedProps时参考评估properties

其次,这是一个很好的方法来初始化需要不平凡的处理变量? 在Java中,我将宣布他们final领域,并在构造函数中做初始化相关的操作。

是否有这种情况Scala中的一个模式?

谢谢!

Answer 1:

瓦尔斯在声明它们的顺序(当然,准确地说, 不懒瓦尔斯是)初始化,因此properties是越来越之前初始化loadedProps 。 或者换句话说, loadedProps仍然是nullproperties是越来越初始化。 这里最简单的解决方案是定义loadedProps之前properties

class Config {
  private val loadedProps = {
    val p = new Properties()
    p.load(Thread.currentThread().getContextClassLoader.getResourceAsStream("props"))
    p 
  }
  val properties: Properties = loadedProps
  val forumId = properties.get("forum_id")
}

你也可以将loadedProps懒,这意味着它会在其首次访问进行初始化:

class Config {
  val properties: Properties = loadedProps
  val forumId = properties.get("forum_id")

  private lazy val loadedProps = {
    val p = new Properties()
    p.load(Thread.currentThread().getContextClassLoader.getResourceAsStream("props"))
    p 
  }
}

使用懒VAL的优点是你的代码更稳健重构,仅仅是改变你的丘壑的声明顺序不会破坏你的代码。

此外,在这个特殊的出现时,你可以只是把loadedPropsdef (由@NIA的建议),因为它只能使用一次,反正。



Answer 2:

我觉得这里loadedProps只需更换简单地变成了一个功能valdef

private def loadedProps = {
  // Tons of code
}

在这种情况下,你是肯定,当你调用它的叫法。

但不知道是不是这种情况下的模式



Answer 3:

只是除了多一点的解释:

您的properties字段初始化得比较早loadedProps此处字段。 null是初始化之前字段的值-这就是为什么你得到它。 在def情况下,它只是一个方法调用,而不是访问某些领域,所以一切都很好(如方法的代码可以被多次调用-这里没有初始化)。 见, http://docs.scala-lang.org/tutorials/FAQ/initialization-order.html 。 您可以使用deflazy val修复它

为什么def是如此不同? 这是因为def可称为几次,但val -只有一次(所以它的第一个也是唯一一个呼叫实际上是fileld的初始化)。

lazy val当你调用它只能初始化,所以它也将帮助。

另外,这是怎么回事的简单的例子:

scala> class A {val a = b; val b = 5}
<console>:7: warning: Reference to uninitialized value b
       class A {val a = b; val b = 5}
                        ^
defined class A

scala> (new A).a
res2: Int = 0 //null

说话更一般地,理论上可以SCALA字段analize之间的依赖图(其字段需要其他字段),并从最终节点开始初始化。 但在实践中每一个模块单独编译器和编译器可能甚至不知道这些依赖(这可能是连渣,这就要求Scala中,它调用的Java),所以它只是做顺序初始化。

所以,正因为如此,它甚至无法检测到简单的循环:

scala> class A {val a: Int = b; val b: Int = a}
<console>:7: warning: Reference to uninitialized value b
       class A {val a: Int = b; val b: Int = a}
                             ^
defined class A

scala> (new A).a
res4: Int = 0

scala> class A {lazy val a: Int = b; lazy val b: Int = a}
defined class A

scala> (new A).a
java.lang.StackOverflowError

其实,这样的循环(一个模块内)可以在理论上建立单独检测,但它不会帮助它更是相当明显。



文章来源: Scala - initialization order of vals