NullPointerException
——空指針異常是程序中常見異常之一,也是導致程序運行失敗的常見異常柑爸。以前吵护,為了防止出現(xiàn)null
,我們常在代碼中使用if…else…
做防御性檢查,后來Guava為了解決上述方法造成的代碼污染引入了Optional
類馅而。
Java8借鑒Guava的Optional
也加入了同名的Optional
類祥诽,Optional
類提供了很多實用的方法,借此可以避免顯示的空指針判斷瓮恭,從而避免NullPointerException
雄坪。
常見方法
下面逐一講解Optional
類提供的方法屯蹦。
of方法
public static <T> Optional<T> of(T value) {
return new Optional<>(value);
}
Optional
中的構造方法都是private
的,外部無法使用new
方式創(chuàng)建Optional
對象登澜,但Optional
類提供三個方法用來獲取Optional
的實例對象。of方法是其中之一帖渠,用來創(chuàng)建一個包裝對象值非空的Optional
實例對象谒亦,若包裝對象值為空則拋出NullPointerException
異常空郊。
// user若為null則會拋出NullPointerException異常
Optional<User> optional_user = Optional.of(user);
ofNullable方法
public static <T> Optional<T> ofNullable(T value) {
return value == null ? empty() : of(value);
}
獲取Optional
實例對象的方法之一,返回一個允許包裝對象值為空的Optional
實例對象狞甚。ofNullable
與of
方法的區(qū)別就在是否允許包裝對象為空锁摔。
// 可以為空,且optional_user1 == optional_user2為true
Optional<User> optional_user1 = Optional.ofNullable(null);
Optional<User> optional_user2 = Optional.ofNullable(null);
empty方法
public static<T> Optional<T> empty() {
@SuppressWarnings("unchecked")
Optional<T> t = (Optional<T>) EMPTY;
return t;
}
獲取Optional
實例對象的方法之一哼审,創(chuàng)建一個包裝對象值為空的Optional
實例對象谐腰。
// 返回包裝對象值為空的Optional實例對象
Optional<User> optional_user = Optional.empty();
isPresent方法
public boolean isPresent() {
return value != null;
}
判斷包裝對象值是否為空十气。
// 這里的user不為null
Optional<User> optional_user1 = Optional.ofNullable(user);
Optional<User> optional_user2 = Optional.ofNullable(null);
// 返回true
optional_user1.isPresent();
// 返回false
optional_user2.isPresent();
ifPresent方法
public void ifPresent(Consumer<? super T> consumer) {
if (value != null)
consumer.accept(value);
}
判斷包裝對象值是否為空春霍,如果包裝對象的值為空則調(diào)用Consumer
對象的accept()
方法,不為空則不調(diào)用址儒。
Optional.ofNullable(user).ifPresent(() -> {
// 包裝對象值為空執(zhí)行的代碼
……
});
get方法
public T get() {
if (value == null) {
throw new NoSuchElementException("No value present");
}
return value;
}
返回包裝對象的值嫁佳,若為空則拋出NoSuchElementException
異常狰住。
orElse方法
public T orElse(T other) {
return value != null ? value : other;
}
若包裝對象不是null
則返回包裝對象的實際值法挨,若為null
則返回指定值叉寂,即返回傳入的參數(shù)翁逞。
Optional.ofNullable(user).orElse(new User("zhangsan", 20));
orElseGet方法
public T orElseGet(Supplier<? extends T> other) {
return value != null ? value : other.get();
}
作用同orElse
方法,只不過orElseGet
的入?yún)⑹?code>Supplier對象,當Optional
包裝對象為null
時返回指定值彼念,返回值由Supplier
的get
方法產(chǎn)生。
// name是一個String對象
Optional.ofNullable(name).orElseGet(() -> "zhangsan");
orElseThrow方法
public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
if (value != null) {
return value;
} else {
throw exceptionSupplier.get();
}
}
作用同orElseGet
類似逐沙,當包裝對象不為空時返回包裝對象的值,為空時返回一個Throwable
異常棚赔。
filter方法
public Optional<T> filter(Predicate<? super T> predicate) {
Objects.requireNonNull(predicate);
if (!isPresent())
return this;
else
return predicate.test(value) ? this : empty();
}
過濾Optional
的包裝對象徘郭,當包裝對象滿足Predicate
的條件時返回該Optional
實例,否則返回包裝對象為null
的Optional
實例胧后。
// 過濾name為zhangsan的User抱环,當不存在時打印提示信息
Optional.ofNullable(user)
.filter(u -> u.getName.equals("zhangsan"))
.ifPresent(() -> System.out.println("There is no zhangsan"));
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));
}
}
map
方法對包裝對象進行函數(shù)運算(即調(diào)用Function
的apply
方法)后返回一個新的Optional
實例對象,新Optional
實例的包裝對象可以時任意類型(不一定與原包裝對象的類型保持一致)眶痰。
如果包裝對象為null
則依然返回一個包裝對象為null
的Optional
實例梯啤,map可以無限級調(diào)用。
// 返回一個Optional<String>
Optional.of(user).map(u -> u.getName());
flatMap方法
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));
}
}
功能同map
方法黔夭,二者的區(qū)別在mapper
的apply
方法的返回值類型不同羽嫡,map
方法中的mapper
返回值可以是任意類型,而flatMap
中的mapper
的返回值是Optional
類型杭棵,map
方法會自動封裝返回一個Optional
氛赐。
// 返回一個Optional<String>艰管,作用同map的例子
Optional.of(user).flatMap(u -> Optional.of(u.getName()));
解決NPE
在由Optional
之前蒋川,我們會使用if…else…
做防御性檢查,以防止出現(xiàn)空指針異常捺球。
public String getUserName(User user) {
if(user == null) {
return "unknown";
}
return user.getName();
}
有了Optional
之后,我們可以這樣做了……
public String getUserName(User user) {
Optional<User> optional_user = Optional.ofNullable(user);
if(!optional_user.isPresent()) {
return "unknown";
}
return user.getName();
}
啊……這……
顯然這兩種方法并無本質區(qū)別裂逐,依然都有防御性檢查泣栈,依然都不夠簡潔。只不過第二種方法利用isPresent
方法替換了顯示的null
判斷掺涛。
正確的姿勢疼进。
public String getUserName(User user) {
// 善用map方法
return Optional.ofNullable(user)
.map(u -> u.getName())
.orElse("unkonwn");
}
這個例子并不能完全展示Optional
的實力,再看下面的示例颠悬。
/**
* 獲取領導姓名,不使用Optional
* */
public String getBossName(User user) {
if(user != null) {
Company company = user.getCompany();
if(company != null) {
Boss boss = company.getBoss();
if(boss != null) {
return boss.getName();
}
}
}
return "unkonwn";
}
優(yōu)雅的姿勢诞外。
/**
* 獲取領導姓名灾票,使用Optional
* */
public String getBossName(User user) {
return Optional.ofNullable(user)
.map(u -> u.getCompany())
.map(c -> c.getBoss())
.map(b -> b.getName())
.orElse("unkonwn");
}
善用Optional
會使我們的代碼無比的優(yōu)雅和簡潔。