Scala中的Either是一个有趣的类,它代表两个可能的类型的其中一个类型的值。这两个值之间没有交集。
Either是抽象类,有两个具体的实现类: Left
和Right
。
Either可以作为scala.Option
的替换,可以用scala.util.Left
替换scala.None
,用scala.Right
替换scala.Some
。
一般在时实践中使用scala.util.Left
作为Failure,而scala.util.Right
作为成功的值。
另一个类似的类是scala.util.Try
举个例子:
1 2 3 4 5 6 7 8 9 10 11 12
| val in = Console.readLine("Type Either a string or an Int: ") val result: Either[String,Int] = try { Right(in.toInt) } catch { case e: Exception => Left(in) } println( result match { case Right(x) => "You passed me the Int: " + x + ", which I will increment. " + x + " + 1 = " + (x+1) case Left(x) => "You passed me the String: " + x })
|
如果输入的值可以转换成Int类型,则结果result为Right(int)
,否则result为Left(String)
。
你可以使用match处理结果。
Either类型还提供了投影(projection)操作。根据类型是Left还是Right返回不同的结果。
例如,使用一个函数转换Either类型的值时,如果值是Left
,应用left
投影,然后调用map返回处理的结果,而应用right
投影只是返回Left,如:
1 2 3 4
| val l: Either[String, Int] = Left("flower") val r: Either[String, Int] = Right(12) l.left.map(_.size): Either[Int, Int] l.right.map(_.toDouble): Either[String, Double]
|
同样地, 对Right
值进行left
投影,只是返回Right,不做改变,只会对right
投影起作用:
1 2 3 4
| val l: Either[String, Int] = Left("flower") val r: Either[String, Int] = Right(12) r.left.map(_.size): Either[Int, Int] r.right.map(_.toDouble): Either[String, Double]
|
既然定义了map操作,就可以使用for语句:
1
| for (s <- l.left) yield s.size
|
Either除了left
,right
投影外,还提供一些其它的方法:
fold
: l.fold(x => x.size, y => y.toDouble);r.fold(x => x.size, y => y.toDouble)
swap
: l.swap //Right(flower)
merge
: l.merge == "flower"
joinLeft
: LeftEither[Int, String], String).joinLeft
joinRight
: Either[A, Either[A, C]]
在Scala中使用Future
,经常会处理Future的最终结果,要不成功,要不失败,这里就使用了Either:
1 2 3 4 5 6 7
| val f: Future[List[String]] = future { session.getRecentPosts } f onComplete { case Right(posts) => for (post <- posts) render(post) case Left(t) => render("An error has occured: " + t.getMessage) }
|
前面提到Try
也是两个值,Success和Failure,它可以和Either进行转换:
1 2 3 4 5 6 7 8 9
| object Helper{ implicit class EitherPimp[L <: Throwable,R](e:Either[L,R]){ def toTry:Try[R] = e.fold(Failure(_), Success(_)) } implicit class TryPimp[T](t:Try[T]){ def toEither:Either[Throwable,T] = t.transform(s => Success(Right(s)), f => Success(Left(f))).get } }
|
或者
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| import scala.util.{ Either, Failure, Left, Right, Success, Try } implicit def eitherToTry[A <: Exception, B](either: Either[A, B]): Try[B] = { either match { case Right(obj) => Success(obj) case Left(err) => Failure(err) } } implicit def tryToEither[A](obj: Try[A]): Either[Throwable, A] = { obj match { case Success(something) => Right(something) case Failure(err) => Left(err) }
|
参考文档
- http://www.scala-lang.org/files/archive/nightly/docs/library/index.html#scala.util.Either
- http://danielwestheide.com/blog/2013/01/02/the-neophytes-guide-to-scala-part-7-the-either-type.html
- http://stackoverflow.com/questions/22532357/either-to-try-and-visa-versa-in-scala