3

我知道有多个问题可以解决相关问题,但我不确定它是否会攻击我正在寻找的东西。经过几年的 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 如此陌生,但我可能错了。这是实现我想要的最佳方式还是有任何其他不涉及选项的选项?

4

2 回答 2

5

也许你需要一个惰性变量。

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

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

于 2013-01-18T15:52:40.973 回答
5

在 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是我会选择的,而不是_. IfX是一个原语,您可能无论如何都需要明智地选择正确的值(例如,Double.NaN而不是0.0-1而不是0forInt) 表示我未初始化。如果它是通用代码,并且您想要Any而不是AnyRef, asInstanceOf-ing 在Any并且AnyRef可能是摆脱类型检查不佳情况的最佳方法(假设您真的,真的不能使用Option,此时更清楚)。

于 2013-01-18T16:22:06.903 回答