我理解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
实例可以null
,getScheme
和getHost
返回getRawPath
。是否足以在apply(URI)
方法(例如require
语句)中防止这些问题?从技术上讲,为了完全安全,保护protocol
,host
和path
辅助方法以及构造函数更好,但这听起来像是一个糟糕的工作和样板。
相反,它是否被认为是安全的,以防止已知接受/返回null
值的API(在我们的示例中为URI
)并假设外部调用者不会通过null
这些价值观或只有责任才能归咎于他们?
答案 0 :(得分:9)
在处理纯函数时,最好始终坚持总体实现,而不是使用null
,require
或异常抛出来编码数据中的所有失败。像Option
和Either
这样的类型就是这样的。它们都是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
- 检查变得多余。