Scala不使用null 而使用Option,None,Some的好處


剛接觸Scala時就很奇怪, 為什么Java已經有null了,卻偏偏還要弄出個None

后來依然我行我素在Scala里使用null, 結果就是經常被NullPointerException折磨得陰魂不散...於是終於領悟.

 

WHY?

舉個例子:

假設有一個簡單的對象A, A類里有2個Int屬性, a和b

case class A(val a : Int, val b : Int)

  

然后有這么一個map

val map1 = Map(1 -> A(111, 1111), 2 -> A(222, 2222), 3 -> A(333, 3333))

  

然后有這么一段業務邏輯需要從這個map中取出其中2個A,並且拿他們的a屬性相加

val key1 :Int = //其他地方給我的值,我也不知道具體是多少
val key2 :Int = //其他地方給我的值,我也不知道具體是多少
println(map1.getOrElse(key1, null).a + map1.getOrElse(key2, null).a) //111 + 報 NullPointerException

  

那么恭喜你, 遇到了NullPointerException. 為什么? 假設別人給你的key1=1, key2=4, 那么map1.getOrElse(key2, null).a 將會報出NullPointerException

不僅這樣, 你在報錯輸出中得到的拋出異常的代碼就是上面第3行println這句, 單從日志來看你根本無法知道別人給你有問題的是key1還是key2

 

所以在Java中, 一個對象的引用, 已經被默認為可能會是null了, 但有時候敲代碼時會極度容易犯錯, 同時就會信心滿滿的認為 "這個對象怎么可能是null呢?",

可是一旦真的是null, 只能慶幸這個報錯不是出現在生產環境中了.

 

HOW?

那么我們如何在scala里避免使用null?

很簡單, scala提供的Option,和None就是個非常好的設計! 有多好? 代碼擼起

像scala List里, 如果我要查找一個list里面符合要求的元素, 不一定能找得到, 所以返回的是一個Option[元素類型]

    val list = List(1 ,2, 3, 4)[Int]
    val found : Option[Int] = list.find(_ > 5)
    found match {
      case None => //由你決定找不到怎么辦
      case Some(value) =>//由你決定找到想要的value值怎么辦
    }

  

所以對於之前的需求,一段業務邏輯需要從這個map中取出其中2個A,並且拿他們的a屬性相加

    //可以先用get, get 出來的東西是個 Option[A], 再做判斷
    var finalResult = 0 //0默認值
    (map1.get(key1), map2.get(key2)) match {
      case (Some(value1), Some(value2)) =>
        //都找到了, 可以愉快的相加了
        finalResult = value1.a + value2.a
      case (None, Some(value2)) =>
        //第一個找不到, 第二個找到了怎么辦的代碼
      case (Some(value1), None) =>
        //第一個找到了, 第二個找不到怎么辦的代碼
      case _=>
        //其他情況
    }

  

scala的Map可以使用get方法獲取對象, 獲取出來的是一個 Option[對象類型], 為什么不像java那樣直接取出null? 這里就是因為要考慮到取不出來東西的情況.

所以Option存在的意義, 就是為了在代碼中注明, 讓大家一看就知道: "這個東西可能是空的! 你們用的時候給我小心點" 這樣的暗示.

有了這個暗示, 你可就不能隨意取出option里面的東西了, 警醒你每次使用, 都要先判斷. isEmpty 或是 nonEmpty

當然更優雅的寫法就像上面那樣, 使用匹配模式, 在出現多種情況之間選擇做什么事情. 

Option還提供了其他很騷的用法. 你甚至可以把Option當成是一個只能容納1個元素的容器來看待.

假設又有那么一個需求, 我有一個Option[A], 我不確定這個Option是否==None, 但我想要嘗試取出里面的A對象的a屬性, 如果沒有的話就給我個0吧, 那么代碼可以這么寫

    val aOpt : Option[A] = //我不確定他是None, 還是Some(A)反正別人給我的
    
    //就這么簡單粗暴
    val myVal1 :Int = if(aOpt.isEmpty) 0 else aOpt.get.a
    
    //也可以先把Option[A]map成Option[Int](這里是Int,因為是A的a屬性的類型), 再使用getOrElse 取不到就給個自己定的默認值
    val myVal2 :Int = aOpt.map(aObj => aObj.a).getOrElse(0)

  

還有另一個需求, 我想知道這個Option[A] 里的A對象的a屬性是否等於100

val has100 :Boolean = aOpt.exists(aObj => aObj.a == 100)

  

 

Final

已經有不少文章吐槽null的設計是多么糟糕: 傳送門請看:http://www.php230.com/weixin1446428231.html

既然scala已經考慮到並提供優雅代碼的解決方式. 那以后可以少在scala里給用null了.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM