我是Scala的新手,对于处理异常的各种方式以及寻找有关该主题的最佳实践建议感到有些困惑 . 我正在编写一个使用现有阻止SDK检索客户的简单方法 . 可能的结果是:
-
找到客户
-
找不到客户(从SDK返回为NotFoundException)
-
与远程服务器通话时出错(SDK引发其他一些异常)
所以我希望我的方法有一个返回类型 Future[Option[Customer]]
,并返回上面的每个案例:
-
成功的未来:一些(客户)
-
成功的未来:无
-
未来失败
这是我用try / catch写的:
private def findCustomer(userId: Long): Future[Option[Customer]] = future {
try {
Some(gateway.find(userId))
} catch {
case _: NotFoundException => None
}
}
这很好,对我来说似乎很干净,但是没有被告知要避免使用try / catch . 所以我一直在寻找一种方法来改写它,而不是使用 Try
.
这里有2个变体(我认为)表现完全相同,使用 Try
.
变式A:
private def findCustomer(userId: Long): Future[Option[Customer]] = future {
Try(
Some(gateway.find(userId))
).recover {
case _: NotFoundException => None
}.get
}
变式B:
private def findCustomer(userId: Long): Future[Option[Customer]] = future {
Try(
Some(gateway.find(userId))
).recover {
case _: NotFoundException => None
}
} flatMap {
case Success(s) => Future.successful(s)
case Failure(f) => Future.failed(f)
}
我比B)更简洁,因为 .get
似乎有点奸诈 . B绝对是最明确的但是将 Try
案例映射到相应的结果似乎很无聊 .
经验丰富的Scala程序员如何写这个?
4 回答
我认为使用
try/catch
的初始版本非常好,因为它是现有SDK的包装器 .或者,您可以在
Future
上使用recover
方法:一种选择是避免链接Try和Future . 从某种意义上说,未来是异步尝试 .
您可以直接使用Future [Customer]并将NotFoundException视为要从而不是None值进行恢复 . 通常情况下,您可以将操作链接到未来本身,而无需处理故障情况(通过映射等) . 这一切都取决于你将如何使用它(当你的未来完成时你的逻辑如何分支) . 换句话说,也许它似乎对你来说很复杂,因为它是,并且你强迫它通过强制Future [Option [Customer]]返回类型 .
能够连接多个操作并继续进行计算,当且仅当所有这些都是正确的时候才是Scala的一个很好的特性(特别是对于理解等) .
您可能正在寻找:
因此,对于您的示例,您可能会:
仅在您的解决方案中捕获
NotFoundException
. 这里唯一的问题是该方法不会异步执行 . 如果有必要,请考虑使用Future.recover
,如Ionut所建议的那样 .另一个值得一提的惯用选项是使用
Either[F, S]
,其中S
是成功返回的类型,F
可以保存错误(您可能希望传播) . 因此,您可以使用Either[ Exception, Option[String]]
或Either[String, Option[String]]
,其中第一个String
是错误消息 .如果你准备采用一些scalaz . 与scalaz脱节在处理错误情景时非常有用和自然 . 这就像scala
Either
但是scalaz disjunction\/
是正确的偏见 . 你将获得正确的成功 Value 和左边的例外 . 用\/.fromTryCatch
包装代码块会在析取的左侧返回异常 . 权利总是有成功 Value . 映射over disjunction比Scala更容易,因为析取是正确的偏差并且很容易从右边给你 Value .