Optional是Java8裏面用避免空指針的,無論什麼語言“空指針異常”總會是最困擾人的東西,老實說這很有可能就是邏輯錯誤。爲了避免這樣的錯誤,Java8建議使用Optional來培養[避免空指針]的好習慣。Optional的方法大綱如下(冒號後表示返回值):
empty() : Optional<T>
of(T) : Optional<T>
ofNullable(T) : Optional<T>
get() : T
isPresent() : boolean
ifPresent(Consumer<? super T>) : void
filter(Predicate<? super T>) : Optional<T>
map(Function<? super T, ? extends U>) : Optional<U>
flatMap(Function<? super T, Optional<U>>) : Optional<U>
orElse(T) : T
orElseGet(Supplier<? extends T>) : T
orElseThrow(Supplier<? extends X>) : T
在之前的篇幅中有個求累加的例子是這麼寫的。
@Test
public void reduceWithInt() {
int sumAll = Stream.of(1, 2, 3, 4).reduce(0, Integer::sum);// 給一個0是用來啓動,的,若給-1,結果會是9</span>
System.out.println(sumAll);// 10
}
例子中調用reduce方法時,我們給了一個初始值0.若我們不給初始值呢會出現什麼情況?不給初始值,我們的代碼該這樣寫:
@Test
public void reduceTest() {
Optional<Integer> sumAll = Stream.of(1, 2, 3, 4).reduce(Integer::sum);// 注意返回值類型
System.out.println(sumAll);// Optional[10] 注意輸出值不再是10了
}
那麼這個Optional是個什麼呢?Optional<T>是Java用來解決空指針異常的一個東西。不過呢,我們想要得到的結果是10,但返回的是Optional[10],怎麼把10弄出來?看下面代碼
@Test
public void reduceTest() {
Integer testInt[]={1, 2, 3, 4};
Optional<Integer> sumAll = Stream.of(testInt).reduce(Integer::sum);
System.out.println(sumAll.get());// 10
}
調用一次Optional的get()方法,就得到真實結果了。猜想一下下面代碼會是什麼?
@Test
public void reduceTest() {
Integer testInt[]={};//空數組
Optional<Integer> sumAll = Stream.of(testInt).reduce(Integer::sum);
System.out.println(sumAll.get());
}
居然保錯了,親!
java.util.NoSuchElementException: No value present
at java.util.Optional.get(Optional.java:135)
看到了麼,所謂避免“空指針異常”就是用“元素缺失異常”來替換?當然不是啦。這裏對空數組沒有做任何操作,沒有初始值,當然是“null”了,爲了不拋異常。只有在確定不爲空的時候才調用get()方法了,怎麼知道爲不爲空呢,那就是調用isPresent()方法判斷是否“元素存在”,存在就可以調用。比如下面代碼:
@Test
public void reduceTest() {
Integer testInt[]={};
Optional<Integer> sumAll = Stream.of(testInt).reduce(Integer::sum);
System.out.println(sumAll);// Optional.empty
if(sumAll.isPresent()){
System.out.println(sumAll.get());
}else{
System.out.println(sumAll.empty());// Optional.empty
}
}
從這段代碼,我們看到了,當Optional裏面值缺失時,直接打印Optional和調用empty()方法,得到的結果是一樣的,即打印“Optional.empty”字符串。你要是覺得多一個判斷好麻煩,那麼沒問題,你可以用ifPresent來代替,在ifPresent裏面加入業務邏輯,只有不爲空的時候才處理。上面的代碼可以改成如下:
@Test
public void reduceTest() {
Integer testInt[] = {};
Optional<Integer> sumAll = Stream.of(testInt).reduce(Integer::sum);
System.out.println(sumAll);
sumAll.ifPresent(x -> {
System.out.println(x); //sumAll不爲空的時候,打印x的值;爲空的時候,不做任何操作
});
}
Java8這麼搞,究竟幾個意思。其實呢,人家的意思就,今後忘掉“空”(null)吧。要養成良好的習慣,不要出現“空”。但是爲了兼容“空”狀態所以用了一個“缺省”。在上面例子中我們可以給Optional一個默認值0,即如下寫法。
@Test
public void reduceTest() {
Integer testInt[]={};
Optional<Integer> sumAll = Stream.of(testInt).reduce(Integer::sum);
System.out.println(sumAll.orElse(0));// 0
}
這樣程序就不會報錯了,值缺省(null)時會返回默認值,但是這樣寫有個問題,那就是默認值是先new出來的,佔有一定空間呀,有沒有一種方式,缺省時再去new呢。有那就是orElseGet(Supplier<? extends T>),這段代碼就可以改成下面這樣:
@Test
public void reduceTest() {
Integer testInt[]={};
Optional<Integer> sumAll = Stream.of(testInt).reduce(Integer::sum);
System.out.println(sumAll.orElseGet(() -> 0)); // 0
}
雖然看到的結果是一樣,但是這種寫法更省內存空間了。
可能有這種情況,就是,值出現缺省狀態,在業務上就是錯了,這個時候你很想拋個特定的異常出來,怎麼弄呢。你只需要調用orElseThrow方法就可以了,比如用.orElseThrow(() -> new Throwable("不能爲空"))。
以上所有例子中Optional都是方法返回給我們的,其實我們也可以自己new 一個Optional但是不是使用new關鍵字。而是使用 of(T)以及ofNullable(T) 等方法。但是of和ofNullable是有區別的。我們看看下面這段代碼:
@Test
public void reduceTest() {
Optional<List<Integer>> optional = Optional.of(new ArrayList<>());//of()裏面不能傳入null
System.out.println(optional.get()); // []
Optional<List<Integer>> optional1 = Optional.ofNullable(new ArrayList<>());
System.out.println(optional1.get()); // []
Optional<List<Integer>> emptyOptional = Optional.empty();
Optional<List<Integer>> emptyOptional2 = Optional.ofNullable(null);
System.out.println(emptyOptional.equals(emptyOptional2)); //true
}
Of裏面必須輸入一個值,也就是說使用of創建的Optional對象一定不是empty的,而ofNullable是可以創建一個empty的Optional對象。
Optional除了這麼蒼白地使用,還可配合過濾轉換一起使用,比如我們要對{1,2,3,4}累加,若結果大於7我們才返回真實值。那麼我們可以這樣寫:
@Test
public void reduceTest() {
Integer testInt[]={1,2,3,4};
Optional<Integer> sumAll = Stream.of(testInt).reduce(Integer::sum);
System.out.println(sumAll.filter(x -> x>7));//Optional[10]
}
若計算結果是6,那麼打印的值會是Optional.empty。當然還可以進行轉換,比如下面是,把小寫轉換成大寫的代碼:
@Test
public void reduceTest() {
String testS[]={"hello"," ","world"," ","!"};
Optional<String> sumAll = Stream.of(testS).reduce(String::concat);
System.out.println(sumAll.map(x-> null));//Optional.empty
System.out.println(sumAll.map(x-> x.toUpperCase()));//Optional[HELLO WORLD !]
}
這裏用到了map就得說說Optional的flatMap了,其實flatMap跟map的操作非常相似,但是呢,flatMap裏面傳入的東西必須是Optional對象。我們看到map可以返回null可以返回x.toUpperCase(),但是flatMap是不可以的。上面的例子我們改由flatMap,就該這麼寫:
@Test
public void reduceTest() {
String testS[]={"hello"," ","world"," ","!"};
Optional<String> sumAll = Stream.of(testS).reduce(String::concat);
System.out.println(sumAll.flatMap(x-> Optional.ofNullable(null)));//Optional.empty
System.out.println(sumAll.flatMap(x-> Optional.of(x.toUpperCase())));//Optional[HELLO WORLD !]
}
好,至此,Optional裏面的方法就算講完一遍了,沒講的,都是從Object類繼承下來的方法,沒啥好講的了。