[Guava原始碼日報](5)Optional分析
大多數情況下,開發人員使用null表明的是某種缺失情形:可能是已經有一個預設值,或沒有值,或找不到值。例如,Map.get返回null就表示找不到給定鍵對應的值或者給定鍵對應值就是為null。
Guava用Optional表示可能為null的T型別引用。一個Optional例項可能包含非null的引用(我們稱之為引用存在),也可能什麼也不包括(稱之為引用缺失)。它從不說包含的是null值,而是用存在或缺失來表示。但Optional從不會包含null值引用。
1. 主要方法
1.1 建立Optional例項 (靜態方法)
方法 | 描述 |
---|---|
Optional.of(T) | 建立指定引用的Optional例項,若引用為null則快速失敗。 |
Optional.absent(T) | 建立引用缺失的Optional例項。 |
Optional.fromNullable(T) | 建立指定引用的Optional例項,若引用為null則表示缺失。 |
1.2 使用Optional例項查詢引用(非靜態方法)
方法 | 描述 |
---|---|
boolean isPresent(T) | 如果Optional包含非null的引用,引用存在,返回true |
T get(T) | 返回Optional所包含的引用,若引用缺失,則丟擲java.lang.IllegalStateException |
T or(T) | 返回Optional所包含的引用,若引用缺失,返回指定的值 |
T orNull() | 返回Optional所包含的引用,若引用缺失,返回null |
Set asSet() | 返回Optional所包含引用的單例不可變集,如果引用存在,返回一個只有單一元素的集合,如果引用缺失,返回一個空集合。 |
2. 使用意義
使用Optional除了賦予null語義,增加了可讀性,最大的優點在於它是一種傻瓜式的防護。Optional迫使你積極思考引用缺失的情況,因為你必須顯式地從Optional獲取引用。直接使用null很容易讓人忘掉某些情形。
如同輸入引數,方法的返回值也可能是null。和其他人一樣,你絕對很可能會忘記別人寫的方法method(a,b)會返回一個null,就好像當你實現method(a,b)時,也很可能忘記輸入引數a可以為null。將方法的返回型別指定為Optional,也可以迫使呼叫者思考返回的引用缺失的情形。
3. 類宣告
@GwtCompatible(serializable = true)
public abstract class Optional<T> implements Serializable
4. 分析
4.1 Optional.of(T)
public void test1(){
Integer num = null;
Optional<Integer> op1 = Optional.of(num); // java.lang.NullPointerException
System.out.println(op1.get());
}
上面的程式,我們使用Optional.of(null)方法,這時候程式會第一時間丟擲空指標異常,這可以幫助我們儘早發現問題。如果給定值不為null,則會返回給定值的Optional例項。
原始碼
public static <T> Optional<T> of(T reference) {
return new Present<T>(checkNotNull(reference));
}
首先使用checkNotNull來判斷給定值是否為null,如果為null,則會丟擲空指標異常,否則返回給定值的Optional的例項(Present是Optional的子類)。
4.2 Optional.absent()
public void test3(){
Integer num = new Integer(4);
Optional<Integer> op = Optional.absent();
Optional<Integer> op2 = Optional.of(num);
System.out.println("op:" + op.isPresent() + " op2:" + op2.isPresent());
}
上面的程式,我們使用Optional.absent()方法,建立引用缺失的Optional例項。 原始碼:
public static <T> Optional<T> absent() {
return Absent.withType();
}
Absent是Optional的子類:
static final Absent<Object> INSTANCE = new Absent<Object>();
@SuppressWarnings("unchecked") // implementation is "fully variant"
static <T> Optional<T> withType() {
return (Optional<T>) INSTANCE;
}
通過withType方法返回一個靜態Absent物件,並強制轉換為Optional物件。從上面就可以看出其中不包含任何的引用。
4.3 Optional.fromNullable(T)
建立指定引用的Optional例項,若引用為null則表示缺失,返回應用缺失物件Absent,否則返回引用存在物件Present。
public void test4(){
Integer num1 = null;
Integer num2 = new Integer(4);
Optional<Integer> op1 = Optional.fromNullable(num1); // 引用缺失
Optional<Integer> op2 = Optional.fromNullable(num2); // 引用存在
System.out.println("op1:" + op1.isPresent() + " op2:" + op2.isPresent()); // false true
}
}
原始碼:
public static <T> Optional<T> fromNullable(@Nullable T nullableReference) {
return (nullableReference == null)
? Optional.<T>absent()
: new Present<T>(nullableReference);
}
從上面原始碼中可以看出如果T為null,則呼叫Optional靜態方法absent(),表示引用缺失;如果T不為null,則建立一個Present物件,表示引用存在。
4.4 T get()
返回Optional包含的T例項,該T例項必須不為空;否則,對包含null的Optional例項呼叫get()會丟擲一個IllegalStateException異常。
public void test5(){
Integer num1 = null;
Integer num2 = new Integer(4);
Optional<Integer> op1 = Optional.fromNullable(num1); // 引用缺失
Optional<Integer> op2 = Optional.fromNullable(num2); // 引用存在
System.out.println("op2:" + op2.get()); // 4
System.out.println("op1:" + op1.get()); // java.lang.IllegalStateException: Optional.get() cannot be called on an absent value
}
因為fromNullable物件根據給定值是否為null,返回不同的物件:
return (nullableReference == null)
? Optional.<T>absent()
: new Present<T>(nullableReference);
因此呼叫的get方法也將會不一樣。
原始碼:
public abstract T get();
如果返回的是一個Present物件,將呼叫Present類中的get()方法:
@Override
public T get() {
return reference;
}
如果返回的是一個Absent物件,將呼叫Absent類中的get()方法:
@Override
public T get() {
throw new IllegalStateException("Optional.get() cannot be called on an absent value");
}
4.5 T or (T)
返回Optional所包含的引用,若引用缺失,返回指定的值。
public void test6(){
Integer num1 = null;
Integer num2 = new Integer(4);
Optional<Integer> op1 = Optional.fromNullable(num1); // 引用缺失
Optional<Integer> op2 = Optional.fromNullable(num2); // 引用存在
System.out.println("op2:" + op2.or(0)); // 4
System.out.println("op1:" + op1.or(0)); // 0
}
因為fromNullable物件根據給定值是否為null,返回不同的物件:
return (nullableReference == null)
? Optional.<T>absent()
: new Present<T>(nullableReference);
因此呼叫的or方法也將會不一樣。
原始碼:
public abstract T or(T defaultValue);
(1)如果返回的是一個Present物件,將呼叫Present類中的or()方法:
@Override
public T or(T defaultValue) {
checkNotNull(defaultValue, "use Optional.orNull() instead of Optional.or(null)");
return reference;
}
這個方法首先對預設值進行判斷,如果不為null,則返回引用;如果為null,丟擲空指標異常,這種情況可以使用Optional.orNull()方法代替。
(2)如果返回的是一個Absent物件,將呼叫Absent類中的or()方法:
@Override
public T or(T defaultValue) {
return checkNotNull(defaultValue, "use Optional.orNull() instead of Optional.or(null)");
}
這個方法首先對預設值進行判斷,如果不為null,則返回預設值;如果為null,丟擲空指標異常,這種情況可以使用Optional.orNull()方法代替。
public void test6(){
String num1 = null;
String num2 = "123";
String defaultNum = null;
Optional<String> op1 = Optional.fromNullable(num1); // 引用缺失
Optional<String> op2 = Optional.fromNullable(num2); // 引用存在
System.out.println("op2:" + op2.or("0")); // 123
System.out.println("op1:" + op1.or(defaultNum)); // java.lang.NullPointerException: use Optional.orNull() instead of Optional.or(null)
}
4.6 T orNull()
返回Optional所包含的引用,若引用缺失,返回null
public void test6(){
String num1 = null;
String num2 = "123";
Optional<String> op1 = Optional.fromNullable(num1); // 引用缺失
Optional<String> op2 = Optional.fromNullable(num2); // 引用存在
System.out.println("op2:" + op2.orNull()); // 123
System.out.println("op1:" + op1.orNull()); // null
}
因為fromNullable物件根據給定值是否為null,返回不同的物件:
return (nullableReference == null)
? Optional.<T>absent()
: new Present<T>(nullableReference);
因此呼叫的orNull方法也將會不一樣。
原始碼:
@Nullable
public abstract T orNull();
(1)如果返回的是一個Present物件,將呼叫Present類中的orNull()方法:
@Override
public T orNull() {
return reference;
}
引用存在,返回引用。
(2)如果返回的是一個Absent物件,將呼叫Absent類中的orNull()方法:
@Override
@Nullable
public T orNull() {
return null;
}
引用缺失,返回null,此時沒有預設值。