我明白 null
在 Scala 中不受欢迎,并且应该始终将可选值包装在 Option
中(或“空类型”,如果有的话)。
优点很明显,永远不需要检查 null
值,并且生成的代码更安全,更易于阅读。
然而在实践中,没有什么能阻止一个值成为 null
- 规则不是由编译器强制执行的,更像是君子协定。例如,当与 Java API 交互时,这很容易中断。
是否有最佳实践?例如,假设我需要写一个 Url
case 类,并且该类的实例可以从 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
辅助方法以及构造函数,但这听起来像是大量的工作和样板。相反,防止已知接受/返回的 API 是否被认为是安全的
null
值(在我们的示例中为 URI
)并假设外部调用者不会通过 null
值(value)观或只有在他们这样做时才怪自己?
最佳答案
在处理纯函数时,最好坚持完全实现,而不是使用 null
, require
或异常抛出,对数据中的所有失败进行编码。类型如 Option
和 Either
正是为了这个。它们都是 monad,因此您可以对它们使用“for”语法。
以下对您的代码的修改显示了一个总函数 apply
,当输入 Java URI
时,它只产生一个有意义的值不提供 null
s。我们通过将结果包装在 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
中.关于你的“设置”功能,你可以实现
null
- 类似地检查它们,使用 Option
.例如。: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
也是惯例。 -处理“桥接”API,当您从 Java 库中获取值时。这就是为什么null
- 从 java.net.URI
转换时检查确实很有意义,但在那之后你就进入了 Scala 世界,那里应该没有 null
s,因此 null
-检查只是变得多余。
关于scala - 在 Scala 中进行空检查的最佳实践,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23992051/