繁体   English   中英

在scala中选择正确的异常处理

[英]Picking the right exception handling in scala

我是Scala的新手,在处理异常的各种方法上有些困惑,并在寻找有关该主题的最佳实践建议。 我正在编写一个使用现有阻止SDK检索客户的简单方法。 可能的结果是:

  1. 找到了客户
  2. 找不到客户(从SDK返回为NotFoundException)
  3. 与远程服务器通信时出错(SDK引发其他一些异常)

所以我希望我的方法有一个返回类型Future[Option[Customer]] ,并返回上面的每个案例:

  1. 成功的未来:一些(客户)
  2. 成功的未来:没有
  3. 失败的未来

这是我用try / catch写的:

private def findCustomer(userId: Long): Future[Option[Customer]] = future {
  try {
    Some(gateway.find(userId))
  } catch {
    case _: NotFoundException => None
  }
}

这工作正常,对我来说似乎很干净,但似乎并不是真正的“Scala方式” - 我被告知要避免使用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)
}

我不是A的忠实粉丝(尽管它比B更简洁),因为.get似乎有点奸诈。 B绝对是最明确的,但将Try案例映射到相应的Future结果似乎很无聊。

经验丰富的Scala程序员如何写这个?

我认为使用try/catch初始版本非常好,因为它是现有SDK的包装器。

或者,您可以在Future上使用recover方法:

def findCustomer(userId: Long): Future[Option[Customer]] =
  Future(Option(gateway.find(userId))).recover {
    case e: NotFoundException => None
  }

一种选择是避免链接Try和Future。 从某种意义上说,未来是异步尝试。

您可以直接使用Future [Customer]并将NotFoundException视为要从而不是None值进行恢复。 通常情况下,您可以将操作链接到未来本身,而无需处理故障情况(通过映射等)。 这一切都取决于你将如何使用它(未来完成时你的逻辑如何分支)。 换句话说,也许它似乎对你来说很复杂,因为它是,并且你强迫它通过强制Future [Option [Customer]]返回类型。

能够连接多个操作并继续进行计算,当且仅当所有这些都是正确的时候才是Scala的一个很好的特性(特别是对于理解等)。

您可能正在寻找:

Future.fromTry(myTry)

因此,对于您的示例,您可能会:

Future.fromTry {
    Try(Some(gateway.find(userId))).recover {
        case _: NotFoundException => None
    }
}

要仅捕获NotFoundException ,就像在解决方案中一样。 这里唯一的问题是该方法不会异步执行。 如果有必要,请考虑使用Ionut建议的Future.recover

另一个值得一提的惯用选项是使用Either[F, S] ,其中S是成功返回的类型, F可以保存错误(您可能希望传播)。 因此,您可以使用Either[ Exception, Option[String]]Either[String, Option[String]] ,其中第一个String是错误消息。

如果你准备采用一些scalaz 与scalaz脱节在处理错误情景时非常有用和自然。 它就像scala Either但是scalaz disjunction \\/是正确的偏见。 你将获得正确的成功价值和左边的例外。 使用\\/.fromTryCatch包装代码块会在\\/.fromTryCatch取的左侧返回异常。 权利总是有成功价值。 映射over disjunction比Scala更容易,因为析取是正确的偏差,并且很容易从右边给你价值。

import scalaz.\/

private def findCustomer(userId: Long): Future[\/[Throwable,Option[Customer]] ]= future {
  \/.fromTryCatch {
    Some(gateway.find(userId))

  }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM