Scala类在其伴随对象中拥有apply和使用unapply方法是很常见的.
行为unapply是明确的:如果其参数是或可以转换为类的有效实例,则返回Some它的一个.否则,返回None.
举一个具体的例子,让我们想象一个Url案例类:
object Url {
def apply(str: String): Url = ???
def unapply(str: String): Option[Url] = ???
}
case class Url(protocol: String, host: String, path: String)
Run Code Online (Sandbox Code Playgroud)
如果str是有效的URL,则unapply返回a Some[Url],否则返回None.
apply但是对我来说有点不太清楚:它应该如何应对str不成为有效的URL?
来自Java世界,我的第一直觉是抛出一个IllegalArgumentException,这将允许我们实现伴侣对象:
object Url {
def apply(str: String): Url = ... // some function that parses a URI and throws if it fails.
def unapply(str: String): Option[Url] = Try(apply(str)).toOption
}
Run Code Online (Sandbox Code Playgroud)
我理解这在功能世界中并不被认为是非常好的实践(例如,在这个答案中解释).
另一种方法是apply返回一个Option[Url],在这种情况下,它将是一个简单的克隆,unapply并且最好留下未实现的.
这是正确的结论吗?这类潜在的失败apply方法是否应该实施?在这种情况下,投掷是否正常?我没有看到第三种选择吗?
这有点主观,但我认为你不应该这样做.
假设您允许apply失败,即抛出异常或返回空选项.然后做的val url = Url(someString)可能会失败,尽管看起来非常像构造函数.这就是整个问题:apply伴随对象的方法应该可靠地为您构造新实例,并且您无法Url从任意字符串中可靠地构造实例.所以不要这样做.
unapply通常应该用于获取有效Url对象并返回另一个可以Url再次创建的表示.作为一个例子,请查看unapply为case类生成的方法,它只返回一个包含构造它的参数的元组.所以签名应该是def unapply(url: Url): String.
所以我的结论是既不应该用于构建一个Url.我认为有一个方法def parse(str: String): Option[Url]来明确你正在做什么(解析字符串)并且它可能会失败,这将是最惯用的.然后,您可以Url.parse(someString).map(url => ...)使用您的Url实例.