Java8引入了全新的Optional類,主要用來處理空指針異常(NullPointerException)。從本質(zhì)上說該類屬于包含可選值的封裝類(wrapper class),因此它既可以包含對象也可以僅僅為空。
舉例說明:
在 Java 8 之前,凡涉及到訪問對象方法或者對象屬性的操作,無論數(shù)量多寡,都可能導(dǎo)致空指針異常:
String isocode = user.getAddress().getCountry().getIsocode().toUpperCase();
為確保上面實例不出現(xiàn)空指針異常,需對每一個值進行顯式的null檢查
if (user != null) {
Address address = user.getAddress();
if (address != null) {
Country country = address.getCountry();
if (country != null) {
String isocode = country.getIsocode();
if (isocode != null) {
isocode = isocode.toUpperCase();
}
}
}
}
但是很多時候會忘記null的檢查,即使沒有忘記寫出上面的代碼也將很難維護
為了解決上述問題,JDK在Java8的時候加入了Optional,Optional的javadoc介紹如下
A container object which may or may not contain a non-null value. If a value is present, isPresent() will return true and get() will return the value.
這是一個可以包含或者不包含非 null 值的容器。如果值存在則 isPresent()方法會返回 true,調(diào)用 get() 方法會返回該對象
構(gòu)造Optional
JDK提供了三個靜態(tài)方法來構(gòu)造一個Optional
Optional.of(T value)
該方法通過一個非null的value來構(gòu)造一個Optional,返回的Optional包含了value這個值,對于該方法,傳入的參數(shù)一定不能為null,否則會拋出NullPointerException
Optional.ofNullable(T value)
該方法和of的區(qū)別在于,傳入的參數(shù)可以為null,與之前Optional javadoc提到的不為null有沖突,原因可以參考ofNullable方法的源碼
public static <T> Optional<T> ofNullable(T value) {
return value == null ? empty() : of(value);
}
該方法會對傳入的參數(shù)進行null判斷,如果為null,實際上返回的是Optional.empty
Optional.empty()
該方法用來構(gòu)造一個空的Optional,底層代碼實現(xiàn)如下
/**
* Common instance for {@code empty()}.
*/
private static final Optional<?> EMPTY = new Optional<>();
/**
* If non-null, the value; if null, indicates no value is present
*/
private final T value;
/**
* Constructs an empty instance.
*
* @implNote Generally only one empty instance, {@link Optional#EMPTY},
* should exist per VM.
*/
private Optional() {
this.value = null;
}
方法使用總結(jié)
前面JavaDoc提到,Optional的isPresent()用來判斷是否包含值,get()方法用來獲取Optional包含的值,需要注意在Optional.empty上調(diào)用get()方法將拋出NoSuchElementException異常
舉例如下,完整代碼在文章底部
Optional<User> user = Optional.ofNullable(getUserById(id));
if (user.isPresent()) {
String username = user.get().getUsername();
System.out.println("Username is: " + username); // 使用 username
}
和之前使用null的判斷沒啥區(qū)別,還需要使用Optional封裝value,增加了代碼里,實際上這不是使用Optional的正確姿勢,下面將詳細介紹Optional各個方法的使用
ifPresent
底層源碼
public void ifPresent(Consumer<? super T> consumer) {
if (value != null)
consumer.accept(value);
}
上面獲取用戶名的例子可以重構(gòu)成如下代碼
Optional<User> user = Optional.ofNullable(getUserById(userId));
user.ifPresent(u -> System.out.println(u.getUsername()));
orElse
底層源碼
public T orElse(T other) {
return value != null ? value : other;
}
使用舉例
User user1 = Optional
.ofNullable(getUserById(nullUserId))
.orElse(new User("Unknown", "Unknown", 99));
System.out.println(user1.toString());
orElseGet
orElseGet與orElse的區(qū)別在于,orElseGet方法傳入的參數(shù)為一個Supplier接口的實現(xiàn),當(dāng)Optional有值的時候,返回值,沒有值的時候,返回該Supplier的值
底層源碼
public T orElseGet(Supplier<? extends T> other) {
return value != null ? value : other.get();
}
使用舉例
User user2 = Optional
.ofNullable(getUserById(userId))
.orElseGet(() -> new User("Unknown", "unknown", 88));
System.out.println(user2.toString());
orElseThrow
orElseThrow與orElse方法的區(qū)別,orElseThrow方法當(dāng)Optional中有值的時候返回值,沒有值的時候拋出異常,拋出的異常由傳入的exceptionSupplier提供
底層源碼
public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
if (value != null) {
return value;
} else {
throw exceptionSupplier.get();
}
}
使用舉例
try {
User user3 = Optional
.ofNullable(getUserById(nullUserId))
.orElseThrow(() -> new Exception("cannot found user of " + nullUserId));
} catch (Exception e) {
e.printStackTrace();
}
map
如果當(dāng)前Optional為Optional.empty,則依舊返回Optional.empty;否則返回一個新的Optional,該Optional包含的是:函數(shù)mapper在以value作為輸入時的輸出值,可以多次使用map操作
底層源碼
public<U> Optional<U> map(Function<? super T, ? extends U> mapper) {
Objects.requireNonNull(mapper);
if (!isPresent())
return empty();
else {
return Optional.ofNullable(mapper.apply(value));
}
}
使用舉例
String userName = Optional.ofNullable(getUserById(nullUserId))
.map(u -> u.getUsername())
.map(name -> name.toLowerCase())
.orElse("Unknown");
System.out.println(userName);
flatMap
flatMap與map方法的區(qū)別在于,map方法參數(shù)中函數(shù)mapper輸出的是值,然后map方法會使用Optional.ofNullable將其包裝為Optional,而flatMap要求參數(shù)中的函數(shù)mapper輸出的就是Optional
底層源碼
public<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper) {
Objects.requireNonNull(mapper);
if (!isPresent())
return empty();
else {
return Objects.requireNonNull(mapper.apply(value));
}
}
使用舉例
Optional<String> userName1 = Optional.ofNullable(getUserById(userId))
.flatMap(u -> Optional.of(u.getUsername()))
.flatMap(name -> Optional.of(name.toLowerCase()));
System.out.println(userName1);
filter
filter方法接收一個Predicate來對Optional中包含的值進行過濾,如果包含的值滿足條件,則還是返回這個Optional,否則返回Optional.empty
底層源碼
public Optional<T> filter(Predicate<? super T> predicate) {
Objects.requireNonNull(predicate);
if (!isPresent())
return this;
else
return predicate.test(value) ? this : empty();
}
使用舉例
Optional<String> userName2 = Optional.ofNullable(getUserById(userId))
.filter(u -> u.getUsername() != "Jack")
.map(u -> u.getUsername());
System.out.println(userName2);
完整代碼
public static void main(String[] args) {
int nullUserId = 0;
int userId = 666;
Optional<User> user = Optional.ofNullable(getUserById(userId));
user.ifPresent(u -> System.out.println(u.getUsername()));
User tmpUser = new User();
user.ifPresent(u -> tmpUser.setUsername(u.getUsername()));
System.out.println(tmpUser.toString());
user.ifPresent(u -> {int age = u.getAge();
System.out.println(age);});
User user1 = Optional
.ofNullable(getUserById(nullUserId))
.orElse(new User("Unknown", "Unknown", 99));
System.out.println(user1.toString());
User user2 = Optional
.ofNullable(getUserById(userId))
.orElseGet(() -> new User("Unknown", "unknown", 88));
System.out.println(user2.toString());
try {
User user3 = Optional
.ofNullable(getUserById(nullUserId))
.orElseThrow(() -> new Exception("cannot found user of " + nullUserId));
} catch (Exception e) {
e.printStackTrace();
}
String userName = Optional.ofNullable(getUserById(nullUserId))
.map(u -> u.getUsername())
.map(name -> name.toLowerCase())
.orElse("Unknown");
System.out.println(userName);
Optional<String> userName1 = Optional.ofNullable(getUserById(userId))
.flatMap(u -> Optional.of(u.getUsername()))
.flatMap(name -> Optional.of(name.toLowerCase()));
System.out.println(userName1);
Optional<String> userName2 = Optional.ofNullable(getUserById(userId))
.filter(u -> u.getUsername() != "Jack")
.map(u -> u.getUsername());
System.out.println(userName2);
}
private static User getUserById(int userId) {
if (666 == userId) {
return new User("Jack", "Beijing", 18);
} else {
return null;
}
}