java备忘录_Java 8备忘单中的可选
java備忘錄
Java 8 java.util.Optional<T>是scala.Option[T]和Data.Maybe在Haskell中的較差表親。 但這并不意味著它沒有用。 如果您不熟悉此概念,請將Optional想象為可能包含或不包含某些值的容器。 就像Java中的所有引用都可以指向某個對象或為null , Option可以包含一些(非null!)引用或為空。
Aridalsvannet
事實證明,在Optional和可為空的引用之間進行類比非常明智。 Optional是在Java 8中引入的,因此很顯然,它在整個標準Java庫中都沒有使用-永遠不會出于向后兼容的原因。 但我建議您至少嘗試一下,并在有可空引用的情況下使用它。 Optional而不是普通的null在編譯時進行靜態檢查,并且提供更多信息,因為它清楚地表明給定變量可能存在或不存在。 當然,這需要一定的紀律–永遠不要再將null分配給任何變量。
選擇 ( 也許 )模式的用法引起很大爭議,我將不參與討論。 相反,我向您展示了一些null用例以及如何將它們改型為Optional<T> 。 在以下示例中,使用了給定的變量和類型:
public void print(String s) {System.out.println(s); }String x = //... Optional<String> opt = //...x是一個可能為null的String, opt永遠不會為null ,但可能包含或可能不包含某個值( present或empty )。 創建Optional方法很少:
opt = Optional.of(notNull);opt = Optional.ofNullable(mayBeNull);opt = Optional.empty();在第一種情況下, Optional 必須不包含null值,如果傳遞null則將引發異常。 ofNullable()將返回空值或存在(設置) Optional 。 empty(總是返回空Optional ,對應于null 。這是一個單例,因為Optional<T>是不可變的。
乏味的if語句:
if (x != null) {print(x); }可以用高階函數ifPresent()代替:
opt.ifPresent(x -> print(x)); opt.ifPresent(this::print);當lambda參數( String x )與函數形式參數匹配時,可以使用后一種語法(方法參考)。
有時,您不僅要在設置引用時還希望在滿足特定條件時執行一些操作:
if (x != null && x.contains("ab")) {print(x); }這可以替換Optional.filter()輪流本(套) Optional清空Optional如果底層值不滿足給定的謂詞。 如果輸入Optional為空,則按原樣返回:
opt.filter(x -> x.contains("ab")).ifPresent(this::print);這等效于更必要的:
if(opt.isPresent() && opt.get().contains("ab")) {print(opt.get()); }通常,您需要對某個值應用某種轉換,但前提是該值必須不為null (避免使用NullPointerException ):
if (x != null) {String t = x.trim();if (t.length() > 1) {print(t);} }這可以使用map()以更具聲明性的方式完成:
opt.map(String::trim).filter(t -> t.length() > 1).ifPresent(this::print);這變得棘手。 Optional.map()將給定函數應用于Optional內部的值,但僅當存在Optional時才適用。 否則什么也不會發生,并返回empty() 。 請記住,轉換是類型安全的–在此處查看泛型:
Optional<String> opt = //... Optional<Integer> len = opt.map(String::length);如果存在Optional<String>則也存在Optional<Integer> len ,包裝String長度。 但是,如果opt為空,則它上面的map()除了更改泛型類型之外什么也沒有做。
在某些時候,您可能希望解開Optional并獲得其中的真實價值。 但是,如果Optional為空,則無法執行此操作。 這是Java 8之前的處理此類情況的方法:
int len = (x != null)? x.length() : -1;使用Optional我們可以說:
int len = opt.map(String::length).orElse(-1);如果計算默認值緩慢,昂貴或有副作用,還有一個版本可以接受Supplier<T> :
int len = opt.map(String::length).orElseGet(() -> slowDefault()); //orElseGet(this::slowDefault)假設您有一個不接受null但可能會產生一個null的函數:
public String findSimilar(@NotNull String s) //...使用它有點麻煩:
String similarOrNull = x != null? findSimilar(x) : null;使用Optional可以更加簡單:
Optional<String> similar = opt.map(this::findSimilar);如果我們的map()函數返回null ,則map()的結果為空Optional 。 否則,這是用(present) Optional包裝的所述函數的結果。 到目前為止一切都很好,但是如果我們有Optional為什么還要返回null值呢?
public Optional<String> tryFindSimilar(String s) //...我們的意圖很明確,但是使用map()無法產生正確的類型。 相反,我們必須使用flatMap() :
Optional<Optional<String>> bad = opt.map(this::tryFindSimilar); Optional<String> similar = opt.flatMap(this::tryFindSimilar);您看到雙重Optional<Optional<...>>嗎? 絕對不是我們想要的。 如果要在返回Optional的函數上進行映射,請改用flatMap 。 這是此功能的簡化實現:
public <U> Optional<U> flatMap(Function<T, Optional<U>> mapper) {if (!isPresent())return empty();else {return mapper.apply(value);} }通常,如果值不可用,我們通常會拋出異常:
public char firstChar(String s) {if (s != null && !s.isEmpty())return s.charAt(0);elsethrow new IllegalArgumentException(); }整個方法可以替換為以下成語:
opt.filter(s -> !s.isEmpty()).map(s -> s.charAt(0)).orElseThrow(IllegalArgumentException::new);我們不希望事先創建異常實例,因為創建異常的成本很高 。
更大的例子
假設我們有一個Person的Address具有有效的validFrom日期。 所有這些都可以為null 。 我們想知道是否設置了validFrom以及過去:
private boolean validAddress(NullPerson person) {if (person != null) {if (person.getAddress() != null) {final Instant validFrom = person.getAddress().getValidFrom();return validFrom != null && validFrom.isBefore(now());} elsereturn false;} elsereturn false; }非常難看和防御。 或者,但仍然很丑陋:
return person != null &&person.getAddress() != null &&person.getAddress().getValidFrom() != null &&person.getAddress().getValidFrom().isBefore(now());現在想象所有這些( person , getAddress() , getValidFrom() )是適當類型的Optional ,清楚地表明它們可能未設置:
class Person {private final Optional<Address> address;public Optional<Address> getAddress() {return address;}//... }class Address {private final Optional<Instant> validFrom;public Optional<Instant> getValidFrom() {return validFrom;}//... }突然,計算變得更加簡化:
return person.flatMap(Person::getAddress).flatMap(Address::getValidFrom).filter(x -> x.before(now())).isPresent();它更具可讀性嗎? 很難說。 但是至少當一致Optional使用Optional時,不可能產生NullPointerException 。
將
有時我想將Optional視為具有0或1個元素的集合1 。 這可以使對map()和flatMap()理解更加容易。 不幸的是, Optional沒有toList()方法,但是很容易實現:
public static <T> List<T> toList(Optional<T> option) {return option.map(Collections::singletonList).orElse(Collections.emptyList()); }或更習慣地說:
public static <T> List<T> toList(Optional<T> option) {if (option.isPresent())return Collections.singletonList(option.get());elsereturn Collections.emptyList(); }但是為什么將自己限制為List<T>呢? Set<T>和其他集合呢? Java 8已經通過為Stream引入的Collectors API抽象創建任意集合。 該API令人毛骨悚然,但可以理解:
public static <R, A, T> R collect(Optional<T> option, Collector<? super T, A, R> collector) {final A container = collector.supplier().get();option.ifPresent(v -> collector.accumulator().accept(container, v));return collector.finisher().apply(container); }我們現在可以說:
import static java.util.stream.Collectors.*;List<String> list = collect(opt, toList()); Set<String> set = collect(opt, toSet());摘要
在Scala中, Optional<T>不如Option[T]強大(但至少不允許包裝null )。 該API不如null那么簡單,并且可能慢得多。 但是,始終使用Optional的編譯時檢查的好處以及可讀性和文檔價值大大超過了缺點。 此外,它可能會取代番石榴中幾乎相同的com.google.common.base.Optional<T>
1 –從理論上講, 也許抽象和序列抽象都是單子 ,這就是為什么它們共享某些功能的原因
翻譯自: https://www.javacodegeeks.com/2013/08/optional-in-java-8-cheat-sheet.html
java備忘錄
總結
以上是生活随笔為你收集整理的java备忘录_Java 8备忘单中的可选的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ddos攻击花钱吗(ddos攻击就给钱吗
- 下一篇: 安卓typec和苹果typec能通用吗(