在咱們學習和使用Guava的Optional以前,咱們須要來了解一下Java中null。由於,只有咱們深刻的瞭解了null的相關知識,咱們才能更加深刻體會領悟到Guava的Optional設計和使用上的優雅和簡單。html
null表明不肯定的對象:java
Java中,null是一個關鍵字,用來標識一個不肯定的對象。所以能夠將null賦給引用類型變量,但不能夠將null賦給基本類型變量。程序員
Java中,變量的使用都遵循一個原則:先定義,而且初始化後,纔可使用。例如以下代碼中,咱們不能定義int age後,不給age指定值,就去打印age的值。這條對對於引用類型變量也是適用的(String name也一樣適用),在編譯的時候就會提示爲初始化。數組
public class NullTest { public static void testNull(){ int age; System.out.println("user age:"+age); long money; money=10L; System.out.println("user money"+money); String name; System.out.println("user name:"+name); } }
在Java中,Java默認給變量賦值:在定義變量的時候,若是定義後沒有給變量賦值,則Java在運行時會自動給變量賦值。賦值原則是整數類型int、byte、short、long的自動賦值爲0,帶小數點的float、double自動賦值爲0.0,boolean的自動賦值爲false,其餘各供引用類型變量自動賦值爲null。上面代碼會變爲以下可運行代碼:函數
public class NullTest { public static void testNull(){ int age = 0; System.out.println("user Age:"+age); long money; money=10L; System.out.println("user money"+money); String name = null; System.out.println("user name:"+name); } }
null自己不是對象,也不是Objcet的實例:工具
null只是一個關鍵字,用來標識一個不肯定的對象,他既不是對象,也不是Objcet對象的實例。下面咱們經過代碼肯定一下null是否是Object對象實例:學習
public class NullTest { public static void main(String[] args) { testNullObject(); } public static void testNullObject() { if (null instanceof java.lang.Object) { System.out.println("null屬於java.lang.Object類型"); } else { System.out.println("null不屬於java.lang.Object類型"); } } }
運行上面代碼,輸出:null不屬於java.lang.Object類型,可見,null對象不是Object對象的實例。google
null對象的使用:url
1.常見使用場景:spa
有時候,咱們定義一個引用類型變量,在剛開始的時候,沒法給出一個肯定的值,可是不指定值,程序可能會在try語句塊中初始化值。這時候,咱們下面使用變量的時候就會報錯。這時候,能夠先給變量指定一個null值,問題就解決了。例如:
Connection conn = null; try { conn = DriverManager.getConnection("url", "user", "password"); } catch (SQLException e) { e.printStackTrace(); } String catalog = conn.getCatalog();
若是剛開始的時候不指定conn = null,則最後一句就會報錯。
2.容器類型與null:
List:容許重複元素,能夠加入任意多個null。
Set:不容許重複元素,最多能夠加入一個null。
Map:Map的key最多能夠加入一個null,value字段沒有限制。
數組:基本類型數組,定義後,若是不給定初始值,則java運行時會自動給定值。引用類型數組,不給定初始值,則全部的元素值爲null。
3.null的其餘做用
1>、判斷一個引用類型數據是否null。 用==來判斷。
2>、釋放內存,讓一個非null的引用類型變量指向null。這樣這個對象就再也不被任何對象應用了。等待JVM垃圾回收機制去回收。
4.null的使用建議:
1>. 在Set或者Map中使用null做爲鍵值指向的value,千萬別這麼用。很顯然,在Set和Map的查詢操做中,將null做爲特殊的例子可使查詢結果更淺顯易懂。
2>. 在Map中包含value是null值的鍵值對,你應該把這種鍵值對移出map,使用一個獨立的Set來包含全部null或者非null的鍵。很容易混淆的是,一個Map是否是包含value是 null的key,仍是說這個Map中沒有這樣的鍵值對。最好的辦法就是把這類key值分立開來,而且好好想一想到底一個value是null的鍵值對對於你的程序來講到底意味着什麼。
3>. 在列表中使用null,而且這個列表的數據是稀疏的,或許你最好應該使用一個Map<Integer,E>字典來代替這個列表。由於字典更高效,而且也許更加精確的符合你潛意識裏對程序的需求。
4>. 想象一下若是有一種天然的「空對象」可使用,比方說對於枚舉類型,添加一個枚舉常數實例,這個實例用來表示你想用null值所表示的情形。好比:Java.math.RoundingMode有一個常數實例UNNECESSARY來表示「不須要四捨五入」,任何精度計算的方法若傳以RoundingMode.UNNECESSARY爲參數來計算,必然拋出一個異常來表示不須要舍取精度。
5.問題和困惑:
首先,對於null的隨意使用會一系列難以預料的問題。經過對大量代碼的研究和分析,咱們發現大概95%以上的集合類默認並不接受null值,若是有null值將被放入集合中,代碼會馬上中斷並報錯而不是默認存儲null值,對於開發來講,這樣可以更加容易的定位程序出錯的地方。
另外,null值是一種使人不滿的模糊含義。有的時候會產生二義性,這時候咱們就很難搞清楚具體的意思,若是程序返回一個null值,其表明的含義究竟是什麼,例如:Map.get(key)若返回value值爲null,其表明的含義多是該鍵指向的value值是null,亦或者該鍵在map中並不存在。null值能夠表示失敗,能夠表示成功,幾乎能夠表示任何狀況。用其它一些值(而不是null值)可讓你的代碼表述的含義更清晰。
反過來講,使用null值在有些狀況下是一種正確的選擇,由於從內存消耗和效率方面考慮,使用null更加廉價,並且在對象數組中出現null也是不可避免的。可是在程序代碼中,比方說在函數庫中,null值的使用會變成致使誤解的元兇,也會致使一些莫名的,模糊的,很難修正的問題。就像上述map的例子,字典返回null能夠表明的是該鍵指向的值存在且爲空,或者也能夠表明字典中沒有這個鍵。關鍵在於,null值不能指明到底null表明了什麼含義。
Guava的Optional:
大多數狀況下程序員使用null是爲了表示某種不存在的意思,也許應該有一個value,可是這個value是空或者這個value找不到。比方說,在用不存在的key值從map中取 value,Map.get返回null表示沒有該map中不包含這個key。
若T類型數據能夠爲null,Optional<T>是用來以非空值替代T數據類型的一種方法。一個Optional對象能夠包含一個非空的T引用(這種狀況下咱們稱之爲「存在的」)或者不包含任何東西(這種狀況下咱們稱之爲「空缺的」)。但Optional歷來不會包含對null值的引用。
import com.google.common.base.Optional; public class OptionalTest { public void testOptional() throws Exception { Optional<Integer> possible=Optional.of(6); if(possible.isPresent()){ System.out.println("possible isPresent:"+possible.isPresent()); System.out.println("possible value:"+possible.get()); } } }
因爲這些緣由,Guava庫設計了Optional來解決null的問題。許多Guava的工具被設計成若是有null值存在即刻報錯而不是隻要上下文接受處理null值就默認使用null值繼續運行。並且,Guava提供了Optional等一些工具讓你在不得不使用null值的時候,能夠更加簡便的使用null並幫助你避免直接使用null。
Optional<T>的最經常使用價值在於,例如,假設一個方法返回某一個數據類型,調用這個方法的代碼來根據這個方法的返回值來作下一步的動做,若該方法能夠返回一個null值表示成功,或者表示失敗,在這裏看來都是意義含糊的,因此使用Optional<T>做爲返回值,則後續代碼能夠經過isPresent()來判斷是否返回了指望的值(本來指望返回null或者返回不爲null,其意義不清晰),而且可使用get()來得到實際的返回值。
Optional方法說明和使用實例:
1.經常使用靜態方法:
Optional.of(T):得到一個Optional對象,其內部包含了一個非null的T數據類型實例,若T=null,則馬上報錯。
Optional.absent():得到一個Optional對象,其內部包含了空值
Optional.fromNullable(T):將一個T的實例轉換爲Optional對象,T的實例能夠不爲空,也能夠爲空[Optional.fromNullable(null),和Optional.absent()等價。
使用實例以下:
import com.google.common.base.Optional; public class OptionalTest { @Test public void testOptional() throws Exception { Optional<Integer> possible=Optional.of(6); Optional<Integer> absentOpt=Optional.absent(); Optional<Integer> NullableOpt=Optional.fromNullable(null); Optional<Integer> NoNullableOpt=Optional.fromNullable(10); if(possible.isPresent()){ System.out.println("possible isPresent:"+possible.isPresent()); System.out.println("possible value:"+possible.get()); } if(absentOpt.isPresent()){ System.out.println("absentOpt isPresent:"+absentOpt.isPresent()); ; } if(NullableOpt.isPresent()){ System.out.println("fromNullableOpt isPresent:"+NullableOpt.isPresent()); ; } if(NoNullableOpt.isPresent()){ System.out.println("NoNullableOpt isPresent:"+NoNullableOpt.isPresent()); ; } } }
2.實例方法:
1>. boolean isPresent():若是Optional包含的T實例不爲null,則返回true;若T實例爲null,返回false
2>. T get():返回Optional包含的T實例,該T實例必須不爲空;不然,對包含null的Optional實例調用get()會拋出一個IllegalStateException異常
3>. T or(T):若Optional實例中包含了傳入的T的相同實例,返回Optional包含的該T實例,不然返回輸入的T實例做爲默認值
4>. T orNull():返回Optional實例中包含的非空T實例,若是Optional中包含的是空值,返回null,逆操做是fromNullable()
5>. Set<T> asSet():返回一個不可修改的Set,該Set中包含Optional實例中包含的全部非空存在的T實例,且在該Set中,每一個T實例都是單態,若是Optional中沒有非空存在的T實例,返回的將是一個空的不可修改的Set。
使用實例以下:
import java.util.Set; import com.google.common.base.Optional; public class OptionalTest { public void testMethodReturn() { Optional<Long> value = method(); if(value.isPresent()==true){ System.out.println("得到返回值: " + value.get()); }else{ System.out.println("得到返回值: " + value.or(-12L)); } System.out.println("得到返回值 orNull: " + value.orNull()); Optional<Long> valueNoNull = methodNoNull(); if(valueNoNull.isPresent()==true){ Set<Long> set=valueNoNull.asSet(); System.out.println("得到返回值 set 的 size : " + set.size()); System.out.println("得到返回值: " + valueNoNull.get()); }else{ System.out.println("得到返回值: " + valueNoNull.or(-12L)); } System.out.println("得到返回值 orNull: " + valueNoNull.orNull()); } private Optional<Long> method() { return Optional.fromNullable(null); } private Optional<Long> methodNoNull() { return Optional.fromNullable(15L); } }
輸出結果:
得到返回值: -12 得到返回值 orNull: null 得到返回值 set 的 size : 1 得到返回值: 15 得到返回值 orNull: 15
Optional除了給null值命名所帶來的代碼可閱讀性的提升,最大的好處莫過於Optional是傻瓜式的。Optional對象的使用強迫你去積極的思考這樣一種狀況,若是你想讓你的程序返回null值,這null值表明的含義是什麼,由於你想要取得返回值,必然從Optional對象內部去得到,因此你必然會這麼去思考。可是隻是簡單的使用一個Null值會很輕易的讓人忘記去思索代碼所要表達的含義究竟是什麼,儘管FindBugs有些幫助,可是咱們仍是認爲它並無儘量的解決好幫助程序員去思索null值表明的含義這個問題。
這種思考會在你返回某些存在的值或者不存在的值的時候顯得特別相關。和其餘人同樣,你絕對極可能會忘記別人寫的方法method(a,b)可能會返回一個null值,就好像當你去寫method(a,b)的實現時,你也極可能忘記輸入參數a也能夠是null。若是返回的是Optional對象,對於調用者來講,就能夠忘卻怎麼去度量null表明的是什麼含義,由於他們始終要從optional對象中去得到真正的返回值。
轉載:https://www.cnblogs.com/peida/archive/2013/06/14/Guava_Optional.html