# 1.1-使用和避免null
[原文鏈接](http://code.google.com/p/guava-libraries/wiki/UsingAndAvoidingNullExplained)?[譯文鏈接](http://ifeve.com/?p=8785)?**譯者:** 沈義揚
_? ?[Doug Lea](http://en.wikipedia.org/wiki/Doug_Lea)?說,“Null 真糟糕。”_
_? 當[Sir C. A. R. Hoare](http://en.wikipedia.org/wiki/C._A._R._Hoare)?使用了null引用后說,”使用它導致了十億美金的錯誤。”_
輕率地使用null可能會導致很多令人驚愕的問題。通過學習Google底層代碼庫,我們發現95%的集合類不接受null值作為元素。我們認為, 相比默默地接受null,使用快速失敗操作拒絕null值對開發者更有幫助。
此外,Null的含糊語義讓人很不舒服。Null很少可以明確地表示某種語義,例如,Map.get(key)返回Null時,可能表示map中的值是null,亦或map中沒有key對應的值。Null可以表示失敗、成功或幾乎任何情況。使用Null以外的特定值,會讓你的邏輯描述變得更清晰。
Null確實也有合適和正確的使用場景,如在性能和速度方面Null是廉價的,而且在對象數組中,出現Null也是無法避免的。但相對于底層庫來說,在應用級別的代碼中,Null往往是導致混亂,疑難問題和模糊語義的元兇,就如同我們舉過的Map.get(key)的例子。最關鍵的是,Null本身沒有定義它表達的意思。
鑒于這些原因,很多Guava工具類對Null值都采用快速失敗操作,除非工具類本身提供了針對Null值的因變措施。此外,Guava還提供了很多工具類,讓你更方便地用特定值替換Null值。
**具體案例**
不要在Set中使用null,或者把null作為map的鍵值。使用特殊值代表null會讓查找操作的語義更清晰。
如果你想把null作為map中某條目的值,更好的辦法是 不把這一條目放到map中,而是單獨維護一個”值為null的鍵集合” (null keys)。Map 中對應某個鍵的值是null,和map中沒有對應某個鍵的值,是非常容易混淆的兩種情況。因此,最好把值為null的鍵分離開,并且仔細想想,null值的鍵在你的項目中到底表達了什么語義。
如果你需要在列表中使用null——并且這個列表的數據是稀疏的,使用Map<Integer, E>可能會更高效,并且更準確地符合你的潛在需求。
此外,考慮一下使用自然的null對象——特殊值。舉例來說,為某個enum類型增加特殊的枚舉值表示null,比如java.math.RoundingMode就定義了一個枚舉值UNNECESSARY,它表示一種不做任何舍入操作的模式,用這種模式做舍入操作會直接拋出異常。
如果你真的需要使用null值,但是null值不能和Guava中的集合實現一起工作,你只能選擇其他實現。比如,用JDK中的Collections.unmodifiableList替代Guava的ImmutableList
**Optional**
大多數情況下,開發人員使用null表明的是某種缺失情形:可能是已經有一個默認值,或沒有值,或找不到值。例如,Map.get返回null就表示找不到給定鍵對應的值。
Guava用[Optional<T>](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/base/Optional.html)表示可能為null的T類型引用。一個Optional實例可能包含非null的引用(我們稱之為引用存在),也可能什么也不包括(稱之為引用缺失)。它從不說包含的是null值,而是用存在或缺失來表示。但Optional從不會包含null值引用。
```
Optional<Integer> possible = Optional.of(5);
possible.isPresent(); // returns true
possible.get(); // returns 5
```
Optional無意直接模擬其他編程環境中的”可選” or “可能”語義,但它們的確有相似之處。
Optional最常用的一些操作被羅列如下:
**創建Optional實例(以下都是靜態方法):**
| [`Optional.of(T)`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/base/Optional.html#of%28T%29) | 創建指定引用的Optional實例,若引用為null則快速失敗 |
|:--- |:--- |
| [`Optional.absent()`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/base/Optional.html#absent%28%29) | 創建引用缺失的Optional實例 |
| [`Optional.fromNullable(T)`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/base/Optional.html#fromNullable%28T%29) | 創建指定引用的Optional實例,若引用為null則表示缺失 |
**用Optional實例查詢引用(以下都是非靜態方法):**
| [`boolean isPresent()`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/base/Optional.html#isPresent%28%29) | 如果Optional包含非null的引用(引用存在),返回true |
|:--- |:--- |
| [`T get()`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/base/Optional.html#get%28%29) | 返回Optional所包含的引用,若引用缺失,則拋出java.lang.IllegalStateException |
| [`T or(T)`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/base/Optional.html#or%28T%29) | 返回Optional所包含的引用,若引用缺失,返回指定的值 |
| [`T orNull()`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/base/Optional.html#orNull%28%29) | 返回Optional所包含的引用,若引用缺失,返回null |
| [`Set<T> asSet()`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/base/Optional.html#asSet%28%29) | 返回Optional所包含引用的單例不可變集,如果引用存在,返回一個只有單一元素的集合,如果引用缺失,返回一個空集合。 |
**使用****Optional****的意義在哪兒?**
使用Optional除了賦予null語義,增加了可讀性,最大的優點在于它是一種傻瓜式的防護。Optional迫使你積極思考引用缺失的情況,因為你必須顯式地從Optional獲取引用。直接使用null很容易讓人忘掉某些情形,盡管FindBugs可以幫助查找null相關的問題,但是我們還是認為它并不能準確地定位問題根源。
如同輸入參數,方法的返回值也可能是null。和其他人一樣,你絕對很可能會忘記別人寫的方法method(a,b)會返回一個null,就好像當你實現method(a,b)時,也很可能忘記輸入參數a可以為null。將方法的返回類型指定為Optional,也可以迫使調用者思考返回的引用缺失的情形。
**其他處理null的便利方法**
當你需要用一個默認值來替換可能的null,請使用[`Objects.firstNonNull(T, T)`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/base/Objects.html#firstNonNull%28T, T%29) 方法。如果兩個值都是null,該方法會拋出NullPointerException。Optional也是一個比較好的替代方案,例如:Optional.of(first).or(second).
還有其它一些方法專門處理null或空字符串:[emptyToNull(String)](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/base/Strings.html#emptyToNull%28java.lang.String%29),[`nullToEmpty(String)`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/base/Strings.html#nullToEmpty%28java.lang.String%29)`,`[`isNullOrEmpty(String)`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/base/Strings.html#isNullOrEmpty%28java.lang.String%29)。我們想要強調的是,這些方法主要用來與混淆null/空的API進行交互。當每次你寫下混淆null/空的代碼時,Guava團隊都淚流滿面。(好的做法是積極地把null和空區分開,以表示不同的含義,在代碼中把null和空同等對待是一種令人不安的壞味道。
- Google Guava官方教程(中文版)
- 1-基本工具
- 1.1-使用和避免null
- 1.2-前置條件
- 1.3-常見Object方法
- 1.4-排序: Guava強大的”流暢風格比較器”
- 1.5-Throwables:簡化異常和錯誤的傳播與檢查
- 2-集合
- 2.1-不可變集合
- 2.2-新集合類型
- 2.3-強大的集合工具類:java.util.Collections中未包含的集合工具
- 2.4-集合擴展工具類
- 3-緩存
- 4-函數式編程
- 5-并發
- 5.1-google Guava包的ListenableFuture解析
- 5.2-Google-Guava Concurrent包里的Service框架淺析
- 6-字符串處理:分割,連接,填充
- 7-原生類型
- 9-I/O
- 10-散列
- 11-事件總線
- 12-數學運算
- 13-反射