天天看點

Scala教程之:Either

在之前的文章中我們提到了Option,scala中Option表示存在0或者1個元素,如果在處理異常的時候Option就會有很大的限制,因為Option如果傳回None,那麼我并不知道具體的異常到底是什麼,這樣scala引入了Either。

顧名思意,Either表示或者是這一個元素或者是那個元素。這樣在異常處理的時候就非常有用了。

我們先看一下Either的定義:

sealed abstract class Either[+A, +B] extends Product with Serializable      

它有兩個子類Left和Right:

/** The left side of the disjoint union, as opposed to the [[scala.util.Right]] side.
 *
 *  @author <a href="mailto:[email protected]">Tony Morris</a>, Workingmouse
 */
final case class Left[+A, +B](@deprecatedName('a, "2.12.0") value: A) extends Either[A, B] {
  def isLeft  = true
  def isRight = false

  @deprecated("Use .value instead.", "2.12.0") def a: A = value
}

/** The right side of the disjoint union, as opposed to the [[scala.util.Left]] side.
 *
 *  @author <a href="mailto:[email protected]">Tony Morris</a>, Workingmouse
 */
final case class Right[+A, +B](@deprecatedName('b, "2.12.0") value: B) extends Either[A, B] {
  def isLeft  = false
  def isRight = true

  @deprecated("Use .value instead.", "2.12.0") def b: B = value
}      

我們通過這兩個子類從兩個可能的元素中選擇一種。

Either 概念的産生時間早于Scala。很長時間以來它被認為是抛出異常的一種替代方案。

為了尊重曆史習慣,當Either 用于表示錯誤标志或某一對象值時,Left 值用于表示錯誤标志,如:資訊字元串或下層庫抛出的異常;而正常傳回時則使用Right 對象。很明顯,Either 可以用于任何需要持有某一個或另一個對象的場景中,而這兩個對象的類型可能不同。

我們看下怎麼用Either的正常使用:

def positive(i: Int): Either[String,Int] = 
  if (i > 0) Right(i) else Left(s"nonpositive number $i")

for {
  i1 <- positive(5).right
  i2 <- positive(10 * i1).right
  i3 <- positive(25 * i2).right
  i4 <- positive(2  * i3).right
} yield (i1 + i2 + i3 + i4)
// Returns: scala.util.Either[String,Int] = Right(3805)

for {
  i1 <- positive(5).right
  i2 <- positive(-1 * i1).right   // EPIC FAIL!
  i3 <- positive(25 * i2).right
  i4 <- positive(-2 * i3).right   // EPIC FAIL!
} yield i1 + i2 + i3 + i4
// Returns: scala.util.Either[String,Int] = Left(nonpositive number -5)      

再看一下Either怎麼在代碼中消除程式錯誤,将錯誤封裝在Either中。

scala> def addInts(s1: String, s2: String): Int =
| s1.toInt + s2.toInt
addInts: (s1: String, s2: String)Int
scala> for {
| i <- 1 to 3
| j <- 1 to i
| } println(s"$i+$j = ${addInts(i.toString,j.toString)}")
1+1 = 2
2+1 = 3
2+2 = 4
3+1 = 4
3+2 = 5
204 | 第7 章
3+3 = 6
scala> addInts("0", "x")
java.lang.NumberFormatException: For input string: "x"      

先看上面的例子,我們定義了一個addInts方法,接收兩個String參數,并将其轉換為Int。如果兩個參數都是可以轉換的字元串當然沒問題,但是如果輸入了一個無法轉換的字元串就會報異常。

雖然異常有時候是好事情,但是異常會阻止程式的正常運作。我們看下怎麼用Either來将其封裝起來:

scala> def addInts2(s1: String, s2: String): Either[NumberFormatException,Int]=
| try {
| Right(s1.toInt + s2.toInt)
| } catch {
| case nfe: NumberFormatException => Left(nfe)
| }
addInts2: (s1: String, s2: String)Either[NumberFormatException,Int]
scala> println(addInts2("1", "2"))
Right(3)
scala> println(addInts2("1", "x"))
Left(java.lang.NumberFormatException: For input string: "x")
scala> println(addInts2("x", "2"))
Left(java.lang.NumberFormatException: For input string: "x")      
  • 區塊鍊從入門到放棄系列教程-涵蓋密碼學,超級賬本,以太坊,Libra,比特币等持續更新
  • Spring Boot 2.X系列教程:七天從無到有掌握Spring Boot-持續更新
  • Spring 5.X系列教程:滿足你對Spring5的一切想象-持續更新
  • java程式員從小工到專家成神之路(2020版)-持續更新中,附詳細文章教程

繼續閱讀