首頁  >  文章  >  Java  >  Java 8新增的API--Optional 的使用實例

Java 8新增的API--Optional 的使用實例

零下一度
零下一度原創
2017-05-24 11:40:221999瀏覽

我們知道Java 8 增加了一些很有用的API, 其中一個就是Optional. 如果對它不稍假探索, 只是輕描淡寫的認為它可以優雅的解決NullPointException 的問題, 於是程式碼就開始這麼寫了

Optional4c8e0c17c3bd7e0081bb17cc795e1984 user = ......if (user.isPresent()) {return user.getOrders();

} else {return Collections.emptyList();

}

那麼不得不說我們的思維仍然是在原地踏步, 只是本能的認為它不過是User 實例的包裝, 這與我們之前寫成

User user = .....if (user != null) {return user.getOrders();

} else {return Collections.emptyList();

}

實質上是沒有任何分別. 這就是我們將要講到的使用好Java 8 Optional 類型的正確姿勢.

在裡約奧運之時, 新聞一再提起五星級紅旗有問題, 可是我怎麼看都看不出來有什麼問題, 後來才道是小星星膜拜中央的姿勢不對. 因此我們千萬也別對自己習以為常的事情覺得理所當然, 絲毫不會覺得有何不妥, 換句話說當我們切換到Java 8 的Optional 時, 不能繼承性的對待過往null 時的那種思維, 應該掌握好新的, 正確的使用Java 8 Optional 的正確姿勢.

直白的講, 當我們還在以如下幾種方式使用Optional 時,就得開始檢視自己了

  1. 呼叫isPresent()  方法時

  2. 呼叫get()  方法時

  3. #Optional 類型作為類別/實例屬性時

  4. Optional 類型作為方法參數時

isPresent() 與obj != null無任何分別, 我們的生活依然在步步驚心. 而沒有isPresent() 作鋪墊的get() 調用在IntelliJ IDEA 中會收到告警

Reports calls to java.util.Optional.get() without first checking with a isPresent() call if a value is available. If the Optional does not contain a value, get() will throw an exception.

(調用Optional.get() 前不事先用isPresent() 檢查值是否可用. 假如Optional 不包含一個值, get() 將會拋出一個異常)

把Optional 類型用作屬性或是方法參數在IntelliJ IDEA 中更是強力不推薦的

Reports any uses of java.util.Optional<T>, java.util.OptionalDouble, java.util.OptionalInt, java.util.OptionalLong or com.google.common.base.Optional as the type 
for
 a field or a parameter. Optional was designed to provide a limited mechanism for library method
 return types where there needed to be a 
clear
 way to represent "no result". Using a field with type java.util.Optional is also problematic if the 
class
 needs to be Serializable, which java.util.Optional is not.

(使用任何像Optional 的類型作為字段或方法參數都是不可取的. Optional 只設計為類別庫方法的, 可明確表示可能無值情況下的返回類型. Optional 類型不可被序列化, 用作字段類型會出問題的)

所以Optional 中我們真正可依賴的應該是除了isPresent() 和get() 的其他方法:

public<U> Optional<U> map(Function<? super T, ? extends U> mapper)
public T orElse(T other)
public T orElseGet(Supplier<? extends T> other)
public void ifPresent(Consumer<? super T> consumer)
public Optional<T> filter(Predicate<? super T> predicate)
public<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper)
public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X

        我略有自信的按照它們大概使用頻度對上面的方法排了一下序.

先我不得不提一下Optional 的三種建構方式: Optional.of(obj) ,   Optional.ofNullable(obj) 和明確的Optional.empty()

    Optional.of(obj) : 它要求传入的 obj 不能是 null 值的, 否则还没开始进入角色就倒在了 NullPointerException 异常上了.Optional.ofNullable(obj) : 它以一种智能的, 宽容的方式来构造一个 Optional 实例. 来者不拒, 传 null 进到就得到 Optional.empty() , 非 null 就调Optional.of(obj) .

那是不是我们只要用 Optional.ofNullable(obj) 一劳永逸, 以不变应二变的方式来构造 Optional 实例就行了呢? 那也未必, 否则 Optional.of(obj) 何必如此暴露呢, 私有则可?

我本人的观点是:  1. 当我们非常非常的明确将要传给 Optional.of(obj) 的 obj 参数不可能为 null 时, 比如它是一个刚 new 出来的对象( Optional.of(new User(...)) ), 或者是一个非 null 常量时;  2. 当想为 obj 断言不为 null 时, 即我们想在万一 obj 为 null 立即报告 NullPointException 异常, 立即修改, 而不是隐藏空指针异常时, 我们就应该果断的用 Optional.of(obj) 来构造 Optional 实例, 而不让任何不可预计的 null 值有可乘之机隐身于 Optional 中.

现在才开始怎么去使用一个已有的 Optional 实例, 假定我们有一个实例 Optional4c8e0c17c3bd7e0081bb17cc795e1984 user , 下面是几个普遍的, 应避免 if(user.isPresent()) { ... } else { ... } 几中应用方式.

存在即返回, 无则提供默认值

return user.orElse(null);  //而不是 return user.isPresent() ? user.get() : null;return user.orElse(UNKNOWN_USER);

存在即返回, 无则由函数来产生

return user.orElseGet(() -> fetchAUserFromDatabase()); //而不要 return user.isPresent() ? user: fetchAUserFromDatabase();

存在才对它做点什么

user.ifPresent(System.out::println);//而不要下边那样if (user.isPresent()) {
  System.out.println(user.get());
}

map 函数隆重登场

当 user.isPresent() 为真, 获得它关联的 orders , 为假则返回一个空集合时, 我们用上面的 orElse , orElseGet 方法都乏力时, 那原本就是 map 函数的责任, 我们可以这样一行

return user.map(u -> u.getOrders()).orElse(Collections.emptyList())//上面避免了我们类似 Java 8 之前的做法if(user.isPresent()) {  return user.get().getOrders();
} else {  return Collections.emptyList();
}

map 是可能无限级联的, 比如再深一层, 获得用户名的大写形式

return user.map(u -> u.getUsername())
           .map(name -> name.toUpperCase())
           .orElse(null);

这要搁在以前, 每一级调用的展开都需要放一个 null 值的判断

User user = .....if(user != null) {
  String name = user.getUsername();  if(name != null) {    return name.toUpperCase();
  } else {    return null;
  }
} else {  return null;
}

针对这方面 Groovy 提供了一种安全的属性/方法访问操作符 ?.

user?.getUsername()?.toUpperCase();

Swift 也有类似的语法, 只作用在  Optional 的类型上.

用了 isPresent() 处理 NullPointerException 不叫优雅, 有了  orElse, orElseGet 等, 特别是 map 方法才叫优雅.

其他幾個, filter() 把不符合條件的值變為empty() ,   flatMap() 總是與map() 方法成對的,   orElseThrow() 在有值時直接返回, 無值時拋出想要的異常.

一句話小結: 使用Optional 時盡量不直接呼叫Optional.get() 方法, Optional.isPresent() 更應該被視為一個私有方法, 應依賴於其他像Optional .orElse() , Optional.orElseGet() , Optional.map() 等這樣的方法.

最後, 最好的理解Java 8 Optional 的方法莫過於看它的源代碼.util.Optionaljava.util.Optional , 閱讀了原始碼才能真真正正的讓你解釋起來最有底氣, Optional 的方法中基本上都是內部調用  isPresent() 判斷, 真時處理值, 假時什麼也不做.

【相關推薦】

1. 分享Java8中新引入的類別Optional實例代碼

2. 解析Java 8 Optional類別實例教程

以上是Java 8新增的API--Optional 的使用實例的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn