我有这样一段代码从文件中加载属性:
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中的一个模式?
谢谢!
瓦尔斯在声明它们的顺序(当然,准确地说, 不懒瓦尔斯是)初始化,因此properties
是越来越之前初始化loadedProps
。 或者换句话说, loadedProps
仍然是null
当properties
是越来越初始化。 这里最简单的解决方案是定义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的优点是你的代码更稳健重构,仅仅是改变你的丘壑的声明顺序不会破坏你的代码。
此外,在这个特殊的出现时,你可以只是把loadedProps
成def
(由@NIA的建议),因为它只能使用一次,反正。
我觉得这里loadedProps
只需更换简单地变成了一个功能val
与def
:
private def loadedProps = {
// Tons of code
}
在这种情况下,你是肯定,当你调用它的叫法。
但不知道是不是这种情况下的模式 。
只是除了多一点的解释:
您的properties
字段初始化得比较早loadedProps
此处字段。 null
是初始化之前字段的值-这就是为什么你得到它。 在def
情况下,它只是一个方法调用,而不是访问某些领域,所以一切都很好(如方法的代码可以被多次调用-这里没有初始化)。 见, http://docs.scala-lang.org/tutorials/FAQ/initialization-order.html 。 您可以使用def
或lazy 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
其实,这样的循环(一个模块内)可以在理论上建立单独检测,但它不会帮助它更是相当明显。