Scala - 处理对象的初始化(_ vs Option [T])

时间:2013-01-18 15:47:05

标签: scala null initialization

我知道解决相关问题有多个问题,但我不确定它是否确实会攻击我正在寻找的内容。经过几年的Java开发,我仍然是Scala的新手。我正在寻找测试对象是否已初始化的最佳方法,如果没有,则初始化它。例如,在Java中:

private MyObject myObj = null;

并且在未来的某个时刻:

public void initMyObj(){
    if (myObj == null){
        myObj = new MyObj();
    }
    // do something with myObj
}

在此之后,我可能会将myObj重新分配给另一个对象,但这不太可能。在Scala中,我有这个:

class Test {
    var myObj: MyObj = _
}

我读过我可以使用Option,比如:

var myObj = None : Option[MyObj]

然后检查:

myObj match {
  case None => ...
  case Some(value) => ...
}

但是当我在任何其他时间在其他任何地方都不能进行这种检查时,使用这种模式会感到很尴尬 - 尽管Scala是新手,但我可能错了。这是实现我想要的最佳方式还是没有其他选项不涉及选项?

2 个答案:

答案 0 :(得分:5)

也许你需要一个懒惰的变量。

lazy val myObj: MyObj = //here you put the object creation code

通过这种方式,对象创建被推迟到代码第一次尝试访问它时。

答案 1 :(得分:4)

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] = LazyVar@2626ea08

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)表示我未初始化。如果它是通用代码而你想要Any而不是AnyRefasInstanceOf来回AnyAnyRef之间来回可能是最好的方法typechecked情况(假设你真的,真的不能使用Option,在那一点上更清楚)。