在Scala中通常不会将部分构建的对象放在周围。你通常会重新思考你的对象是如何被实例化的,以查看你是否不能使用不太脆弱的不同模式。例如,而不是设置初始化变量的方法:
class Foo { var a: String = null; var b: String = null }
def initFooA(s: String, f: Foo) { if (f.a == null) f.a = s }
def initFooB(s: String, f: Foo) { if (f.b == null) f.b = s }
f
initFooA("salmon", f)
// Do stuff
initFooB("herring", f)
你会尝试重组你的代码来生成你需要对需求的值,延缓foo的实例化,直到然后:
case class Bar(a: String, b: String) {}
def initBarA(s: String) = s
def initBarB(s: String) = s
val iba = initBarA("halibut")
// Do stuff
val ibb = initBarB("cod")
Bar(iba, ibb)
因为Scala可以方便地访问元组(和类型推断),所以这比Java中的痛苦小得多。
你可以做的另一件事是推迟后期初始化给其他人。
case class Baz(a: String)(bMaker: => String) {
lazy val b = bMaker
}
现在你传入一些参数b,并安排它处理任何需要处理的后期初始化事件。这并不总是避免需要设置变量,但它可以帮助将它从您的类代码中移出到您的初始化逻辑中(通常这对于它来说是更好的选择)。
用vars做这件事不那么简单。实际上,你可能是最好的选择,例如,通过:
class LazyVar[A](initial: => A) {
private[this] var loaded = false
private[this] var variable: A = _
def apply() = { if (!loaded) { loaded = true; variable = initial }; variable }
def update(a: A) { loaded = true; variable = a }
}
然后你(可悲)不得不使用()
每读写。
scala> val lv = new LazyVar({ println("Hi!"); 5 })
lv: LazyVar[Int] = [email protected]
scala> lv()
Hi!
res2: Int = 5
scala> lv() = 7
scala> lv()
res4: Int = 7
然后你使用这个类,而不是实际var
的实例,通过懒惰初始化。 (lazy val
非常类似于引擎盖;编译器只是保护您不会注意到。)
最后,如果您想要一个功能齐全的对象偶尔缺少一个值,var x: Option[X]
是您想要的构造使用;如果你无法找到解决标准Java创建模式的方法(并且你不想尝试一些更具异国情调的东西,比如使用越来越多的信息创建对方的对象,或者是因为性能很重要,而且你负担不起,或者你不喜欢编写那么多的样板文件以允许类型检查来验证你的对象是否被正确创建),但是否则你想要使用它,var x: X = null
是我选择的,而不是_
。如果X
是原语,则可能需要明智地选择正确的值(例如,Double.NaN
而不是0.0
,-1
而不是0
对于Int
)以指示I-am-not-initialized。如果它是通用代码,并且您希望Any
而不是AnyRef
,asInstanceOf
-来回之间Any
和AnyRef
之间可能是最好的方式出来typechecked很差的情况下(假设你真的,真的不能使用Option
,在这一点上是多更清晰)。
感谢雷克斯。我喜欢延迟初始化。在我的情况下,我需要初始化一个变量。是否有可能以其他方式使用延迟初始化? – Dan
@丹 - 排序;我已经修改了我的答案。 –