我明白那个 null
在Scala中不受欢迎,并且应始终将可选值包装在一个 Option
(或“null type”,如果有的话)。
优点很明显,永远不需要检查 null
值和结果代码更安全,更舒适。
然而,在实践中,没有什么可以阻止价值 null
- 规则不是由编译器强制执行的,而是更多的绅士协议。例如,在与Java API交互时,这很容易破坏。
这有最好的做法吗?比方说,我需要写一个 Url
case类,并且可以从a创建此类的实例 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
价值观或只是责备他们呢?
在处理纯函数时,总是更好地坚持总体实现而不是使用 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
。
关于“设置”功能,您可以实现 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
当从Java库中获取值时,处理“桥”API。这就是为什么 null
转换时,检查确实很有意义 java.net.URI
,但在那之后你就在斯卡拉世界,那里应该没有 null
s,因此 null
- 检查只是变得多余。
在处理纯函数时,总是更好地坚持总体实现而不是使用 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
。
关于“设置”功能,您可以实现 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
当从Java库中获取值时,处理“桥”API。这就是为什么 null
转换时,检查确实很有意义 java.net.URI
,但在那之后你就在斯卡拉世界,那里应该没有 null
s,因此 null
- 检查只是变得多余。