Scala中空值检查的最佳实践

时间:2014-06-02 10:07:43

标签: scala

我理解null在Scala中是不受欢迎的,并且应该始终将可选值包装在Option内(或者#34; null类型"如果有的话)。

优点很明显,永远不需要检查null值,并且生成的代码更安全,更舒适。

然而,在实践中,没有任何东西可以阻止值null - 编译器不强制执行该规则,而且更符合绅士协议。例如,在与Java API交互时,这很容易破解。

这是最好的做法吗?比如说,我需要编写一个Url案例类,并且可以从java.net.URI创建此类的实例:

object Url {
  def apply(uri: URI): Url = Url(uri.getScheme, uri.getHost, uri.getRawPath)
}

case class Url(protocol: String, host: String, path: String) {
  def protocol(value: String): Url = copy(protocol = value)
  def host(value: String): Url = copy(host = value)
  def path(value: String): Url = copy(path = value)
}

URI实例可以nullgetSchemegetHost返回getRawPath。是否足以在apply(URI)方法(例如require语句)中防止这些问题?从技术上讲,为了完全安全,保护protocolhostpath辅助方法以及构造函数更好,但这听起来像是一个糟糕的工作和样板。

相反,它是否被认为是安全的,以防止已知接受/返回null值的API(在我们的示例中为URI)并假设外部调用者不会通过null这些价值观或只有责任才能归咎于他们?

1 个答案:

答案 0 :(得分:9)

在处理纯函数时,最好始终坚持总体实现,而不是使用nullrequire或异常抛出来编码数据中的所有失败。像OptionEither这样的类型就是这样的。它们都是monad,因此您可以使用" for" -syntax与它们一起使用。

对代码的以下修改显示了一个总函数apply,当输入Java URI未提供null时,它只生成一个有意义的值。我们编码"有意义的概念"将结果包装在Option

object Url {
  def apply(uri: java.net.URI): Option[Url] =
    for {
      protocol <- Option(uri.getScheme)
      host <- Option(uri.getHost)
      path <- Option(uri.getRawPath)
    }
    yield Url(protocol, host, path)
}

case class Url(protocol: String, host: String, path: String)

函数Option是一个标准的空值检查函数,它将null映射到None并将值包装在Some中。


关于你的&#34;设置&#34;函数,您可以使用null实现Option - 类似地检查它们。 E.g:

case class Url(protocol: String, host: String, path: String) {
  def protocol(value: String): Option[Url] = 
    Option(value).map(v => copy(protocol = v))
  // so on...
}

但是我会建议反对。在Scala中,传统上永远不要使用null s,所以只实现null - 处理&#34; bridge&#34; API,从Java库中获取值。这就是null - 检查从java.net.URI转换时非常有意义的原因,但之后您处于Scala世界,那里应该没有null s,因此null - 检查变得多余。