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