1

我正在使用 Scala 开发一个应用程序。我正在尝试在应用程序中实现异常处理。我尝试使用Either来处理异常。对于简单的情况,处理异常似乎就足够了。但是,当我需要通过连接从多个表中获取结果时,异常处理会产生比解决更多的问题。我的应用程序结构解释如下:

对定义所有数据库交互操作的数据库操作使用存储库模式。例如,我有一个中央存储库,它定义了findAllfindByIdinsertdeleteupdateexists。我的findAll方法签名已更改为Either[CustomException, List[TEntity]],类似findById的方法类型是Either[CustomException, Option[TEntity]]

现在假设,如果我需要在我的方法中从数据库中获取员工,我将执行如下操作:

def getVehicleById(id:Long) = {

val vehicle = repository.findById(id)
//i have one-to-one mapping with Employee table for the driver of the vehicle
val driver = empRepository.findById(vehicle.driverId)

}

现在车辆的Either[CustomException, Option[Vehicle]]类型是,司机的类型是Either[CustomException, Employee]

如果我在得到结果后需要做更多的操作,我必须使用右/左大小写,然后再做。问题是,可能在 Right case 中,我可能需要加入另一个表,这使得结果再次成为Either[CustomException, Entity]。如果该操作发生任何错误,我需要再次使用 try catch 块。我觉得当代码复杂度增加时,这种处理变得非常难以管理,而且我会有很多样板代码来处理这些情况,这违背了 Scala 原则本身。

所以我的问题是,如何在没有太多样板代码的情况下以更好的方式处理异常。

注意:我来自 Java 背景,现在只在 Scala 上工作了几个月。

编辑

使用 Try 添加示例代码:此示例非常接近我的要求。

import scala.util._
def checkTry:Try[List[Int]] = Success(List(2))
def checkTryStr:Try[String] = Success("Asd")

def getVehicleWithDriver = for {
  a <- checkTry
  c <- a
  b <- checkTryStr
}yield {
  (c,b)
}
getVehicleWithDriver

但是使用上面的代码,我得到了编译错误。

Error:(9, 6) type mismatch;
 found   : scala.util.Try[(Int, String)]
 required: scala.collection.GenTraversableOnce[?]
  b <- checkTryStr
    ^
Error:(9, 6) type mismatch;
 found   : scala.util.Try[(Int, String)]
 required: scala.collection.GenTraversableOnce[?]
  b <- checkTryStr
    ^
Error:(8, 6) type mismatch;
 found   : List[Nothing]
 required: scala.util.Try[?]
  c <- a
    ^
4

3 回答 3

2

你想用for/yield糖。您可以养成戴上.right所有Eithers 的习惯,或者按照@Gangstead 的建议,\/从 Scalaz 中使用。(Scalaz 肯定有一些可怕的部分——我已经使用它 4 年了,但仍然对 eg 感到害怕Strength——但完全有可能从简单的部分开始并逐步提高)。

for {
  vehicle <- repository.findById(id).right
  driver <- empRepository.findById(vehicle.driverId).right
  somethingElse <- somePossiblyFailingComputation.right
} yield somethingElse
//Don't need the .right if you're using Scalaz \/

其他重要提示:当您想将有效功能应用于列表时,您需要使用 Scalaz traverse

vehicleList <- vehicleIdList.traverse(repository.findById) //assuming you're using \/
//might need a few .right if you're using Either

当您调用可能引发异常的 Java 代码时,请使用scala.util.control.Exception's catching

val myEither = catching(classOf[SomeException]) either someJavaMethodThatThrows()
// (...).disjunction if you're using \/.

如果您需要在使用Either/的同时使用另一种“有效上下文”,则\/必须开始查看monad transformers,但最好先熟悉一下Either

希望有帮助;祝你好运,希望你喜欢 Scala。

于 2014-12-27T09:34:38.130 回答
1

看看 Scalaz 的析取\/

警告:Scalaz 是由铁杆家伙编写的铁杆 scala。

看看 Brendon McAdams (@Rit) 关于“Scalaz' Gateway Drugs”的演讲。对于来自 Java 的程序员来说,他更加平易近人,而且他非常实用

于 2014-12-27T07:40:09.217 回答
0

你想用Try,没有Either。它是一个单子,所以你可以mapflatMap它,或者在for理解中使用它。它实际上看起来很优雅。

def getVehicleWithDriver(id: String): Try [(Vehicle, Driver)] = for {
    vehicle <- repository. findById(id)
    driver <- empRepository.findById(vehicle.driverId)
} yield {
    (vehicle, driver)
}
于 2014-12-27T16:35:59.047 回答