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

标签 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 如此陌生,但我可能错了。这是实现我想要的最佳方式还是有任何其他选项不涉及 Option?

最佳答案

在 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 的东西,并安排它处理任何需要处理的后期初始化内容。这并不总是避免需要设置 var,但它可以帮助将其从您的类代码中推出到您的初始化逻辑中(这通常是一个更好的地方)。

使用 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 for Int) 表示我未初始化。如果它是通用代码并且你想要 Any 而不是 AnyRefasInstanceOf - 在 AnyAnyRef 可能是摆脱类型检查不佳情况的最佳方法(假设你真的,真的不能使用 Option,这在这一点上要清楚得多)。

关于Scala - 处理对象的初始化(_ vs Option[T]),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14402467/

相关文章:

scala - 使用 Java 9 编译 Scala 时出现不明确的引用错误

Scala:只映射元组中的第一个元素

json - 合并不区分大小写的 json 列名称

C# 嵌套 null 检查 if 语句

swift - swift toInt() 中的用户输入返回 nil

C++位域成员变量初始化值(UE4示例)

exception - 我的持久性类应该返回 Option 还是依赖异常?

python - 当值为 None 或空字符串时排序python

java - 为什么java不与静态外部字段同时初始化静态嵌套类字段?

swift - UIView 子类在初始化时固定高度