Try和Either有什么区别?

时间:2015-04-21 17:25:17

标签: scala

根据the documentation

  

Try类型表示可能导致a的计算   异常,或返回成功计算的值。它类似于,   但语义上与scala.util.Either类型不同。

文档没有详细说明语义差异是什么。两者似乎都能够传达成功和失败。你为什么要用一个呢?

6 个答案:

答案 0 :(得分:33)

我介绍了this answerTryEitherOption之间的关系。关于TryEither之间关系的重点摘要如下:

Try[A]Either[Throwable, A]同构。换句话说,您可以将Try视为Either,其左侧类型为Throwable,您可以处理左侧类型为Either的任何Throwable }作为Try。通常使用Left表示失败,Right表示成功。

当然,您也可以更广泛地使用Either,不仅适用于缺失值或特殊值的情况。还有其他情况Either可以帮助表达简单联合类型的语义(其中value是两种类型之一)。

从语义上讲,您可以使用Try来指示操作可能会失败。在这种情况下,您可能会同样使用Either,特别是如果您的错误"}类型不是Throwable(例如Either[ErrorType, SuccessType])。然后,当您对联合类型(例如Either)进行操作时,您也可以使用Either[PossibleType1, PossibleType2]

标准库不包含从EitherTry或从TryEither的转化,但丰富Try非常简单,和Either根据需要:

object TryEitherConversions {
    implicit class EitherToTry[L <: Throwable, R](val e: Either[L, R]) extends AnyVal {
        def toTry: Try[R] = e.fold(Failure(_), Success(_))
    }

    implicit class TryToEither[T](val t: Try[T]) extends AnyVal {
        def toEither: Either[Throwable, T] = t.map(Right(_)).recover(PartialFunction(Left(_))).get
    }
}

这将允许你这样做:

import TryEitherConversions._

//Try to Either
Try(1).toEither //Either[Throwable, Int] = Right(1)
Try("foo".toInt).toEither //Either[Throwable, Int] = Left(java.lang.NumberFormatException)

//Either to Try
Right[Throwable, Int](1).toTry //Success(1)
Left[Throwable, Int](new Exception).toTry //Failure(java.lang.Exception)

答案 1 :(得分:10)

要简单回答你的问题:“语义差异是什么”:

这可能是指flatMap和map,它们在Either中不存在,并且传播失败或映射Try中的成功值。例如,这允许链接像

for { 
   a <- Try {something} 
   b <- Try {somethingElse(a)}
   c <- Try {theOtherThing(b)}
} yield c

这正是您所希望的 - 返回包含第一个异常或结果的Try。

尝试有许多其他有用的方法,当然还有它的伴随应用方法,使其非常方便其预期用途 - 异常处理。

如果你真的想要被淹没,那么还有另外两个类可能对这种应用感兴趣。 Scalaz有一个名为“\/”(以前称为Prince)的类,发音为“Either”,其大部分类似于Either,但flatMap和map在Right值上工作。类似地,而不是,Scalactic有一个“Or”,它也类似于Either,但是flatMap和map在Left值上工作。

我不建议初学者使用Scalaz。

答案 2 :(得分:3)

Either并不意味着成功和失败,它只是A或B的容器。通常用它来表示成功和失败,惯例是将失败放在左边一边,右边的成功。

Try可以看作是Either,左侧类型设置为ThrowableTry[A]等同于Either[Throwable, A]

使用Try清楚地识别计算中的潜在故障,故障由异常表示。如果您想用不同的类型表示失败(例如String或一组扩展密封特征的案例类),请使用Either

答案 3 :(得分:1)

Either更为通用,因为它只代表不相交的类型联盟。 特别是,它可以表示某种类型XException的有效返回值的并集。但是,它并不试图自己捕获任何异常。您必须在危险代码周围添加try-catch块,然后确保每个分支返回Either的适当子类(通常:Left表示错误,Right表示成功计算)。

Try[X]可以被视为Either[Exception, X],但它也可以自行捕获异常。

答案 4 :(得分:1)

Either[X, Y]用法更为一般。顾名思义它可以代表X型或Y型的对象。

Try[X]只有一种类型,可能是Success [X]或Failure(包含Throwable)。

在某些时候,您可能会将Try[X]视为Either[Throwable,X]

Try[X]的好处是你可以将更多的操作链接到它,如果它真的是一个成功他们将执行,如果它是一个失败他们将不会

val connection = Try(factory.open())
val data = connection.flatMap(conn => Try(conn.readData()))
//At some point you can do 
data matches {
  Success(data) => print data
  Failure(throwable) => log error
}

当然,你可以像

一样直播
Try(factory.open()).flatMap(conn => Try(conn.readData()) matches {
      Success(data) => print data
      Failure(throwable) => log error
}

答案 5 :(得分:0)

正如已经提到的,Either更通用,因此它不仅可以包装错误/成功的结果,还可以用作Option的替代品,以分支代码路径。

为抽象错误的影响,仅出于此目的,我确定了以下差异:

  1. Either可用于指定错误的描述,该描述可显示给客户端。 Try-用堆栈跟踪包装一个异常,该异常具有较少的描述性,较少的面向客户端的用途,更多地用于内部使用。
  2. Either允许我们使用现有的monoid来指定错误类型。结果,它使我们能够合并错误(通常是通过应用效果)。 Try抽象没有定义,没有定义monoid。使用Try,我们必须花更多的精力来提取错误并进行处理。

    基于此,这是我的最佳做法: 当我想抽象错误的效果时,我总是以Either / List / Vector作为错误类型为第一选择。

    NonEmptyList仅在调用以OOP编写的代码时使用。 Try的最佳候选方法是可能会引发异常的方法,或者是将请求发送到外部系统的方法(如果方法返回原始结果(未包装到FP抽象中,则是{rest / soap / database请求,例如{例如{1}}。