什么';什么是处理Scala中错误的推荐方法?
假设我有一个将用户添加到数据库的方法什么';什么是处理Scala中错误的推荐方法?,scala,exception,Scala,Exception,假设我有一个将用户添加到数据库的方法addUser。调用时,该方法可能: 成功 失败,因为输入无效(即用户名已存在) 失败,因为数据库崩溃或其他原因 该方法可能由单个数据库API调用组成,该调用在发生故障时会引发异常。如果是纯Java,我可能会捕获方法内部的异常并检查原因。如果它属于第二类(无效输入),我将抛出一个自定义的检查异常,解释原因(例如useralreadyexistException)。对于第二类,我只需重新抛出原始异常 我知道Java中有关于错误处理的强烈意见,所以可能有人不同意这
addUser
。调用时,该方法可能:
useralreadyexistException
)。对于第二类,我只需重新抛出原始异常
我知道Java中有关于错误处理的强烈意见,所以可能有人不同意这种方法,但我现在想重点讨论Scala
所述方法的优点是,当我调用addUser
时,我可以选择捕获useralreadyexistException
并处理它(因为它适合我当前的抽象级别)但同时,我可以选择完全忽略可能引发的任何其他低级数据库异常,并让其他层处理它
现在,我如何在Scala中实现同样的功能?或者什么是正确的Scala方法?显然,异常在Scala中的工作方式完全相同,但我发现有更好、更合适的方法
据我所知,我可以选择选项
,或尝试
。然而,这两种情况都不像过去的好例外那样优雅
例如,处理Try
结果如下(借用自):
最后两行代码正是我想要避免的,因为我必须将它们添加到任何我进行数据库查询的地方(而且指望MatchError
似乎不是一个好主意)
另外,处理多个错误源似乎有点麻烦:
(for {
u1 <- addUser(user1)
u2 <- addUser(user2)
u3 <- addUser(user3)
} yield {
(u1, u2, u3)
}) match {
case Success((u1, u2, u3)) => Ok(...)
case Failure(...) => ...
}
前一种方法有我不知道的优点吗
据我所知,Try
在不同线程之间传递异常时非常有用,但只要我在单个线程中工作,它就没有多大意义
我想听听关于这方面的一些论点和建议 这个话题的大部分内容当然是意见问题。尽管如此,仍有一些具体的观点可以提出:
- 您可以正确地观察到
选项
,或者
和尝试
都是非常通用的;这些名称没有提供太多文档。因此,您可以考虑一个定制的密封特性:
sealed trait UserAddResult
case object UserAlreadyExists extends UserAddResult
case class UserSuccessfullyAdded(user: User) extends UserAddResult
这在功能上相当于一个选项[User]
,并具有文档的附加好处
- Scala中的异常总是未选中的。因此,您将在Java中使用未检查异常的相同情况下使用它们,而不是在使用已检查异常的情况下使用它们
- 存在一元错误处理机制,例如,或
这些一元工具的主要目的是供调用者一起组织和处理几个异常。因此,让您的方法返回这些类型,无论是在文档方面还是在行为方面,都没有多大好处。任何想要使用
Try
或Validation
的调用方都可以将方法的返回类型转换为所需的一元形式
正如您可能从我的措辞中猜到的,我倾向于定义定制的密封特征,因为这提供了最好的自文档化代码。但是,这是品味的问题。谢谢你的回答。所以你会建议我用这个“包装”的返回值替换选中的异常,并保留未选中的异常,对吗?托比克:这是一个很好的问题,但我现在还没有准备好回答。我鼓励您查看验证的文档
,了解如何单独处理错误。不过,在我看来,对结果进行手动模式匹配并不是一个坏的解决方案。另一个问题是,使用模式匹配处理错误会大大提高嵌套级别。我想这在某种程度上是函数方法的必然结果。。。
try {
u1 = addUser(user1)
u2 = addUser(user2)
u3 = addUser(user3)
Ok(...)
} catch {
case (e: UserAlreadyExistsException) => ...
}
sealed trait UserAddResult
case object UserAlreadyExists extends UserAddResult
case class UserSuccessfullyAdded(user: User) extends UserAddResult