Java Optional解決空指針異常總結(jié)(java 8 功能)
1、概述
Java8的版本,新增了Optional和[Lambda]表達式,Optional主要用于作為返回類型(主要解決的問題是臭名昭著的空指針異常
(NullPointerException)),并將其與流(或返回可選的方法)相結(jié)合以構(gòu)建連貫API。
但是,有些情況可以被認為是陷阱,因為它們會降低代碼的質(zhì)量,甚至導致意想不到的錯誤。總結(jié)以下26個例子,以避免這些陷阱。
2、 目 錄
[第1項:決不將Null分配給可選變量]
[第2項:調(diào)用Optional.get()之前,確保Optional具有值]
[第3項:當不存在任何值時,通過Optional.orElse()方法設置/返回已經(jīng)構(gòu)造的默認對象]
[第4項:不存在任何值時,通過Optional.orElseGet()方法設置/返回不存在的默認對象]
[第5項:當不存在任何值時,自Java 10起通過orElseThrow()拋出java.util.NoSuchElementException異常]
[第6項:當不存在任何值時,通過orElseThrow(Supplier <?extended X> exceptionSupplier)引發(fā)顯式異常]
[第7項:當你有可選的并且需要空引用時,請使用orElse(null)]
[第8項:使用可選(如果存在)。如果不存在,則什么也不做。這是Optional.ifPresent()的工作。]
[第9項:使用可選(如果存在)。如果不存在,請執(zhí)行基于空的操作。這是Optional.ifPresentElse(),Java 9的工作。]
[第10項:當值存在時,設置/返回該可選項。如果不存在任何值,則設置/返回另一個可選項。這是Java 9的Optional.or()的工作。]
[第11項:Optional.orElse / orElseXXX是Lambdas中的isPresent()-get()完美替代]
[第12項:避免僅出于獲取價值的目的而鏈接可選方法]
[第13項:不要聲明任何類型的可選字段]
[第14項:在構(gòu)造函數(shù)參數(shù)中不要使用可選]
[第15項:在Setters參數(shù)中不要使用可選]
[第16項:在方法參數(shù)中不要使用可選]
[第17項:不要使用Optional 的返回空的集合或數(shù)組]
[第18項:避免在集合中使用Optional]
[第19項:不要混淆Optional.of()和Optional.ofNullable()]
[第20項:避免使用可選的,并選擇非通用的OptionalInt,OptionalLong或OptionalDouble]
[第21項:無需包裝主張平等的任擇方案]
[第22項:通過Map()和flatMap()轉(zhuǎn)換值]
[第23項:使用filter()根據(jù)預定義的規(guī)則拒絕包裝值]
[第24項:我們是否需要將可選API與流API鏈接在一起?]
[第25項:避免對可選選項使用身份敏感的操作]
[第26項:如果Optional為空,則返回一個布爾值。首選Java 11,Optional.isEmpty()]
第1項:決不將Null分配給可選變量
避免:
// 避免
public Optional<Cart> fetchCart() {
Optional<Cart> emptyCart = null;
...
}
首選:
// 首選
public Optional<Cart> fetchCart() {
Optional<Cart> emptyCart = Optional.empty();
...
}
首選Optional.empty()初始化 Optional而不是null值。Optional只是一個容器/盒子,用初始化它是沒有意義的null。
第2項:調(diào)用Optional.get()之前,確保Optional具有值
如果出于某種原因你決定使用Optional.get(),那么請不要忘記必須Optional在此調(diào)用之前證明該值存在。通常,將通過基于該Optional.isPresent()方法添加檢查(條件)來執(zhí)行此操作。Optional.get()在某些時候容易被棄用。
避免:
// 避免 Optional<Cart> cart = ... ; // 這很容易是空的 ... // 如果“cart”是空的,那么這段代碼將拋出一個java.util.NoSuchElementException Cart myCart = cart.get();
首選:
// 首選
if (cart.isPresent()) {
Cart myCart = cart.get();
...
} else {
... // 做一些不調(diào)用carter .get()的事情
}
第3項:當不存在任何值時,通過Optional.orElse()方法設置/返回已經(jīng)構(gòu)造的默認對象
使用Optional.orElse()方法代表了用于設置/返回值的isPresent()-get()對的優(yōu)雅替代。這里重要的一點是,即使是非空可選的,也要計算oforElse()參數(shù)。這意味著即使不使用它,也會對它進行評估,這是一種性能損失。在這個上下文中,useorElse()只在參數(shù)(默認對象)已經(jīng)構(gòu)造完成時才使用。在其他情況下,請依賴第4項。
避免:
// 避免
public static final String USER_STATUS = "UNKNOWN";
...
public String findUserStatus(long id) {
Optional<String> status = ... ; // 容易返回一個空的 Optional
if (status.isPresent()) {
return status.get();
} else {
return USER_STATUS;
}
}
首選:
// 首選
public static final String USER_STATUS = "UNKNOWN";
...
public String findUserStatus(long id) {
Optional<String> status = ... ; // 容易返回一個空的 Optional
return status.orElse(USER_STATUS);
}
第4項:不存在任何值時,通過Optional.orElseGet()方法設置/返回不存在的默認對象
使用theOptional.orElseGet()方法表示另一種用于設置/返回值的替代theisPresent()-get()對的優(yōu)雅方法。這里重要的一點是,參數(shù)oforElseGet()是Java 8新特性。這意味著作為參數(shù)傳遞的Suppliermethod只在Optionalvalue不存在時執(zhí)行。因此,這有助于避免創(chuàng)建對象和執(zhí)行在出現(xiàn)Optionalvalue時不需要的代碼時的orElse()性能損失。
避免:
// 避免
public String computeStatus() {
... // 一些用來計算狀態(tài)的代碼
}
public String findUserStatus(long id) {
Optional<String> status = ... ; // 容易返回一個空的 Optional
if (status.isPresent()) {
return status.get();
} else {
return computeStatus();
}
}
另外,請避免:
// 避免
public String computeStatus() {
... // 一些用來計算狀態(tài)的代碼
}
public String findUserStatus(long id) {
Optional<String> status = ... ; // 容易返回一個空的 Optional
// 即使“status”不是空的,也會調(diào)用computeStatus()
return status.orElse(computeStatus());
}
首選:
public String computeStatus() {
... // some code used to compute status
}
public String findUserStatus(long id) {
Optional<String> status = ... ; // 容易返回一個空的 Optional
// 僅當“status”為空時才調(diào)用computeStatus()
return status.orElseGet(this::computeStatus);
}
第5項:當不存在任何值時,自Java 10起通過orElseThrow()拋出java.util.NoSuchElementException異常
使用該Optional.orElseThrow()方法代表了該方法的另一種優(yōu)雅替代方法isPresent()-get()。有時,當Optional不存在值時,你要做的就是拋出java.util.NoSuchElementException異常。從Java 10開始,這可以通過orElseThrow()不帶參數(shù)的方法來完成。對于Java 8和9,請考慮第6項。
避免:
public String findUserStatus(long id) {
Optional<String> status = ... ; // 容易返回一個空的 Optional
if (status.isPresent()) {
return status.get();
} else {
throw new NoSuchElementException();
}
}
首選:
public String findUserStatus(long id) {
Optional<String> status = ... ; // 容易返回一個空的 Optional
return status.orElseThrow();
}
第6項:當不存在任何值時,通過orElseThrow(Supplier <?extended X> exceptionSupplier)引發(fā)顯式異常
在Java 10中,對于 java.util.NoSuchElementException,請使用 orElseThrow(),如第5項所示。使用該Optional.orElseThrow(Supplier<? extends X> exceptionSupplier)方法代表了該方法的另一種優(yōu)雅替代方法isPresent()-get()。有時,當Optional不存在值時,你要做的就是拋出一個明確的異常。從Java 10開始,如果該異常java.util.NoSuchElementException僅依賴于orElseThrow()不帶參數(shù)的方法-第5項。
避免:
public String findUserStatus(long id) {
Optional<String> status = ... ; // 容易返回一個空的 Optional
if (status.isPresent()) {
return status.get();
} else {
throw new IllegalStateException();
}
}
首選:
public String findUserStatus(long id) {
Optional<String> status = ... ; // 容易返回一個空的 Optional
return status.orElseThrow(IllegalStateException::new);
}
第7項:當您有可選的并且需要空引用時,請使用orElse(null)
如果你有Optional需要提供null參考的,請使用 orElse(null)。否則,請避免 orElse(null)。orElse(null)當我們有一個Optional,并且需要null在某些情況下調(diào)用一個接受引用的方法時,就會發(fā)生典型的使用情況。例如,讓我們看一下Method.invoke()Java Reflection API。此方法的第一個參數(shù)是要在其上調(diào)用此特定方法的對象實例。如果方法是static,第一個參數(shù),則應為null。
避免:
Method myMethod = ... ;
...
// 包含MyClass的實例,如果“myMethod”是靜態(tài)的,則為空
Optional<MyClass> instanceMyClass = ... ;
...
if (instanceMyClass.isPresent()) {
myMethod.invoke(instanceMyClass.get(), ...);
} else {
myMethod.invoke(null, ...);
}
首選:
Method myMethod = ... ; ... // 包含MyClass的實例,如果“myMethod”是靜態(tài)的,則為空 Optional<MyClass> instanceMyClass = ... ; ... myMethod.invoke(instanceMyClass.orElse(null), ...);
第8項:使用可選(如果存在)。如果不存在,則什么也不做。這是Optional.ifPresent()的工作。
當你只需要使用值時,Optional.ifPresent()是isPresent()-get()paid的不錯選擇。 如果沒有值,則不執(zhí)行任何操作。
避免:
Optional<String> status = ... ;
...
if (status.isPresent()) {
System.out.println("Status: " + status.get());
}
首選:
Optional<String> status ... ; ... status.ifPresent(System.out::println);
第9項:使用可選(如果存在)。如果不存在,請執(zhí)行基于空的操作。這是Optional.ifPresentElse(),Java 9的工作。
從Java 9開始,它Optional.ifPresentOrElse()是isPresent()-get()配對的不錯選擇。這與ifPresent()方法類似, 只不過它也覆蓋else分支。
避免:
Optional<String> status = ... ;
if(status.isPresent()) {
System.out.println("Status: " + status.get());
} else {
System.out.println("Status not found");
}
首選:
Optional<String> status = ... ;
status.ifPresentOrElse(
System.out::println,
() -> System.out.println("Status not found")
);
第10項:當值存在時,設置/返回該可選項。如果不存在任何值,則設置/返回另一個可選項。這是Java 9的Optional.or()的工作。
有時,對于非空, Optional,我們想設置/返回那個 Optional。當我們Optional為空時,我們想執(zhí)行其他操作,該操作也返回一個 Optional。該orElse()和orElseGet()方法不能做到這一點,因為兩者都將返回展開值?,F(xiàn)在該介紹Java 9 Optional.or()方法了,該方法能夠返回Optional描述值。否則,它將返回Optional提供功能產(chǎn)生的結(jié)果。
避免:
public Optional<String> fetchStatus() {
Optional<String> status = ... ;
Optional<String> defaultStatus = Optional.of("PENDING");
if (status.isPresent()) {
return status;
} else {
return defaultStatus;
}
}
另外,請避免:
public Optional<String> fetchStatus() {
Optional<String> status = ... ;
return status.orElseGet(() -> Optional.<String>of("PENDING"));
}
首選:
public Optional<String> fetchStatus() {
Optional<String> status = ... ;
Optional<String> defaultStatus = Optional.of("PENDING");
return status.or(() -> defaultStatus);
// 或者,不定義“defaultStatus”
return status.or(() -> Optional.of("PENDING"));
}
第11項:Optional.orElse / orElseXXX是Lambdas中的isPresent()-get()完美替代
這可用于獲取鏈接的lambda,而不是被破壞的代碼??紤] ifPresent() 和 ifPresentOrElse() Java中9或 or() Java中9的方法,也是如此。某些特定于lambda的操作正在返回Optional(例如,findFirst(), findAny(), reduce(),...)。嘗試Optional通過該isPresent()-get()對使用此方法是一種笨拙的方法,因為它可能需要您打破lambda鏈,用if語句污染代碼,并考慮繼續(xù)使用鏈。在這種情況下,orElse()and和orElseXXX()非常方便,因為它們可以直接在lambda表達式鏈中鏈接,并避免破壞代碼。
例子1
避免:
List<Product> products = ... ;
Optional<Product> product = products.stream()
.filter(p -> p.getPrice() < price)
.findFirst();
if (product.isPresent()) {
return product.get().getName();
} else {
return "NOT FOUND";
}
另外,請避免:
List<Product> products = ... ;
Optional<Product> product = products.stream()
.filter(p -> p.getPrice() < price)
.findFirst();
return product.map(Product::getName)
.orElse("NOT FOUND");
首選:
List<Product> products = ... ;
return products.stream()
.filter(p -> p.getPrice() < price)
.findFirst()
.map(Product::getName)
.orElse("NOT FOUND");
例子2
避免:
Optional<Cart> cart = ... ;
Product product = ... ;
...
if(!cart.isPresent() ||
!cart.get().getItems().contains(product)) {
throw new NoSuchElementException();
}
首選:
Optional<Cart> cart = ... ; Product product = ... ; ... cart.filter(c -> c.getItems().contains(product)).orElseThrow();
第12項:避免僅出于獲取價值的目的而鏈接可選方法
有時,我們傾向于“過度使用”事物。意味著我們有一個東西,例如 Optional,并且到處都可以看到用例。在的情況下 Optional,常見的情況是出于獲得價值的單一目的而鏈接其方法。避免這種做法,而是依靠簡單明了的代碼。
避免:
public String fetchStatus() {
String status = ... ;
return Optional.ofNullable(status).orElse("PENDING");
}
首選:
public String fetchStatus() {
String status = ... ;
return status == null ? "PENDING" : status;
}
第13項:不要聲明任何類型的可選字段
請勿 Optional 在方法(包括 setters)或構(gòu)造函數(shù)參數(shù)中使用。
請記住,這Optional 并不是要用于字段,并且不會實現(xiàn)Serializable。該Optional班是明確不打算用作一個Java Bean的屬性。
避免:
public class Customer {
[access_modifier] [static] [final] Optional<String> zip;
[access_modifier] [static] [final] Optional<String> zip = Optional.empty();
...
}
首選:
public class Customer {
[access_modifier] [static] [final] String zip;
[access_modifier] [static] [final] String zip = "";
...
}
第14項:在構(gòu)造函數(shù)參數(shù)中不要使用可選
請勿將其 Optional 用作字段或方法(包括 setters)的參數(shù)。
這是違背Optional意圖的另一種用法。 Optional用另一種抽象級別包裝對象,在這種情況下,只需添加額外的樣板代碼。
避免:
public class Customer {
private final String name; // 不可能為null
private final Optional<String> postcode; //可能為null
public Customer(String name, Optional<String> postcode) {
this.name = Objects.requireNonNull(name, () -> "名稱不能為空");
this.postcode = postcode;
}
public Optional<String> getPostcode() {
return postcode;
}
...
}
首選:
public class Customer {
private final String name;
private final String postcode;
public Cart(String name, String postcode) {
this.name = Objects.requireNonNull(name, () -> "名稱不能為空");
this.postcode = postcode;
}
public Optional<String> getPostcode() {
return Optional.ofNullable(postcode);
}
...
}
如你所見,getter現(xiàn)在返回 Optional。不要以這個示例作為轉(zhuǎn)換所有此類getter的規(guī)則。多數(shù)情況下,getter返回集合或數(shù)組,在這種情況下,他們更喜歡返回空集合/數(shù)組而不是 Optional。
我認為,通常將其用作獲取方法的返回值肯定會過度使用。
第15項:在Setters參數(shù)中不要使用可選
Optional 不能用作Java Bean的屬性或持久屬性類型。 Optional 不是Serializable。Optional在二傳手中使用是另一種反模式。通常,我看到它用作持久屬性類型(將實體屬性映射為 Optional)。但是,可以Optional在域模型實體中使用,如以下示例所示。
避免:
@Entity
public class Customer implements Serializable {
private static final long serialVersionUID = 1L;
...
@Column(name="customer_zip")
private Optional<String> postcode; // optional 字段, 因此可能為空
public Optional<String> getPostcode() {
return postcode;
}
public void setPostcode(Optional<String> postcode) {
this.postcode = postcode;
}
...
}
首選:
@Entity
public class Customer implements Serializable {
private static final long serialVersionUID = 1L;
...
@Column(name="customer_zip")
private String postcode;
public Optional<String> getPostcode() {
return Optional.ofNullable(postcode);
}
public void setPostcode(String postcode) {
this.postcode = postcode;
}
...
}
第16項:在方法參數(shù)中不要使用可選
不要 Optional 用作字段或設置器和構(gòu)造函數(shù)的參數(shù)。Optional在in方法中使用參數(shù)是另一個常見錯誤。這將導致不必要的復雜代碼。負責檢查參數(shù),而不是強制調(diào)用站點創(chuàng)建 Optional。這種做法會使代碼混亂,并可能導致依賴性。隨著時間的流逝,您將在所有地方使用它。請記住,這Optional只是另一個對象(容器),而且價它消耗的內(nèi)存是強引用的4倍!但是,你可以用,視情況而定。
避免:
public void renderCustomer(Cart cart, Optional<Renderer> renderer,
Optional<String> name) {
if (cart == null) {
throw new IllegalArgumentException("Cart不能為空");
}
Renderer customerRenderer = renderer.orElseThrow(
() -> new IllegalArgumentException("Renderer 不能為空")
);
String customerName = name.orElseGet(() -> "anonymous");
...
}
//調(diào)用這個是錯誤的
renderCustomer(cart, Optional.<Renderer>of(CoolRenderer::new), Optional.empty());
首選:
public void renderCustomer(Cart cart, Renderer renderer, String name) {
if (cart == null) {
throw new IllegalArgumentException("Cart 不能為空");
}
if (renderer == null) {
throw new IllegalArgumentException("Renderer 不能為空");
}
String customerName = Objects.requireNonNullElseGet(name, () -> "anonymous");
...
}
// 調(diào)用這個方法
renderCustomer(cart, new CoolRenderer(), null);
另外,更喜歡(依靠NullPointerException):
public void renderCustomer(Cart cart, Renderer renderer, String name) {
Objects.requireNonNull(cart, "Cart 不能為空");
Objects.requireNonNull(renderer, "Renderer 不能為空");
String customerName = Objects.requireNonNullElseGet(name, () -> "anonymous");
...
}
// 調(diào)用這個方法
renderCustomer(cart, new CoolRenderer(), null);
另外,更喜歡(避免NullPointerException和使用IllegalArgumentException或其他異常):
public final class MyObjects {
private MyObjects() {
throw new AssertionError("Cannot create instances for you!");
}
public static <T, X extends Throwable> T requireNotNullOrElseThrow(T obj,
Supplier<? extends X> exceptionSupplier) throws X {
if (obj != null) {
return obj;
} else {
throw exceptionSupplier.get();
}
}
}
public void renderCustomer(Cart cart, Renderer renderer, String name) {
MyObjects.requireNotNullOrElseThrow(cart,
() -> new IllegalArgumentException("Cart cannot be null"));
MyObjects.requireNotNullOrElseThrow(renderer,
() -> new IllegalArgumentException("Renderer cannot be null"));
String customerName = Objects.requireNonNullElseGet(name, () -> "anonymous");
...
}
// 調(diào)用這個方法
renderCustomer(cart, new CoolRenderer(), null)
第17項:不要使用Optional 的返回空的集合或數(shù)組
支持返回一個空的集合/數(shù)組??紤]到這一點,依靠 Collections.emptyList(), emptyMap()和emptySet()。
為了保持代碼的清潔,輕便避免返回Optional的null或空的集合/數(shù)組。最好返回一個空數(shù)組或集合。
避免:
public Optional<List<String>> fetchCartItems(long id) {
Cart cart = ... ;
List<String> items = cart.getItems(); //這個可能會返回null
return Optional.ofNullable(items);
}
首選:
public List<String> fetchCartItems(long id) {
Cart cart = ... ;
List<String> items = cart.getItems(); // 這個可能會返回null
return items == null ? Collections.emptyList() : items;
}
第18項:避免在集合中使用Optional
見示例
避免:
Map<String, Optional<String>> items = new HashMap<>();
items.put("I1", Optional.ofNullable(...));
items.put("I2", Optional.ofNullable(...));
...
Optional<String> item = items.get("I1");
if (item == null) {
System.out.println("找不到這把鑰匙");
} else {
String unwrappedItem = item.orElse("NOT FOUND");
System.out.println("Key found, Item: " + unwrappedItem);
}
首選(Java 8):
Map<String, String> items = new HashMap<>();
items.put("I1", "Shoes");
items.put("I2", null);
...
// get an item
String item = get(items, "I1"); // Shoes
String item = get(items, "I2"); // null
String item = get(items, "I3"); // NOT FOUND
private static String get(Map<String, String> map, String key) {
return map.getOrDefault(key, "NOT FOUND");
}
還可以依賴其他方法,例如:
- containsKey()方法
- 通過擴展實現(xiàn)簡單HashMap
- Java 8 computeIfAbsent()方法
- Apache CommonsDefaultedMap
通過將此示例外推到其他集合,我們可以得出結(jié)論,總有比Optional在集合中使用更好的解決方案。
而且,甚至更糟:
Map<Optional<String>, String> items = new HashMap<>(); Map<Optional<String>, Optional<String>> items = new HashMap<>();
第19項:不要混淆Optional.of()和Optional.ofNullable()
混淆或錯誤地使用 Optional.of而不是Optional.ofNullable,反之亦然,可能會導致令人不快的問題。作為此處的關(guān)鍵,請記住Optional.of(null)會拋出NullPointerException,而Optional.ofNullable(null)會導致 Optional.empty。
使用Optional.of代替Optional.ofNullable示例
避免:
// AVOID
public Optional<String> fetchItemName(long id) {
String itemName = ... ; // 這可能導致null
...
return Optional.of(itemName); // 如果“itemName”為空,則拋出NPE :(
}
首選:
public Optional<String> fetchItemName(long id) {
String itemName = ... ; // 這可能導致null
...
return Optional.ofNullable(itemName); // 沒有NPE風險
}
使用Optional.ofNullable代替Optional.of示例
避免:
return Optional.ofNullable("PENDING"); // ofNullable不會增加任何值
首選:
return Optional.of("PENDING"); // 沒有 NPE 風險
第20項:避免使用可選的<T>,并選擇非通用的OptionalInt,OptionalLong或OptionalDouble
除非你有盒裝原語,避免特定需求 Optional< T > ,并選擇非通用 OptionalInt, OptionalLong或OptionalDouble。
裝箱和拆箱是昂貴的操作,容易導致性能下降。為了消除這種風險,我們可以依靠 OptionalInt,OptionalLong 和OptionalDouble。這些是基本類型的包裝int,long,和double。
避免使用(僅在需要裝箱的原語時使用):
Optional<Integer> price = Optional.of(50); Optional<Long> price = Optional.of(50L); Optional<Double> price = Optional.of(50.43d);
首選:
OptionalInt price = OptionalInt.of(50); // 打開通過 getAsInt() OptionalLong price = OptionalLong.of(50L); // 打開通過 getAsLong() OptionalDouble price = OptionalDouble.of(50.43d); // 打開通過 getAsDouble()
第21項:無需包裝主張平等的任擇方案
有兩個Optionals在assertEquals()不需要解開值。這是適用的,因為Optional#equals()比較包裝的值,而不是Optional對象。
Optional.equals()源代碼:
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof Optional)) {
return false;
}
Optional<?> other = (Optional<?>) obj;
return Objects.equals(value, other.value);
}
避免:
Optional<String> actualItem = Optional.of("Shoes");
Optional<String> expectedItem = Optional.of("Shoes");
assertEquals(expectedItem.get(), actualItem.get());
首選:
Optional<String> actualItem = Optional.of("Shoes");
Optional<String> expectedItem = Optional.of("Shoes");
assertEquals(expectedItem, actualItem);
第22項:通過Map()和flatMap()轉(zhuǎn)換值
的Optional.map()和Optional.flatMap()是用于將非常方便的辦法Optional值。該map()方法將函數(shù)參數(shù)應用于值,然后返回包裝在中的結(jié)果 Optional,而通過比較,該flatMap()方法采用應用于值的函數(shù)參數(shù),Optional然后直接返回結(jié)果。
使用 map()
例子1
避免:
Optional<String> lowername ...; // 可能是空的
// transform name to upper case
Optional<String> uppername;
if (lowername.isPresent()) {
uppername = Optional.of(lowername.get().toUpperCase());
} else {
uppername = Optional.empty();
}
首選:
Optional<String> lowername ...; // 可能是空的 // 將名稱轉(zhuǎn)換為大寫 Optional<String> uppername = lowername.map(String::toUpperCase);
例子2
避免:
List<Product> products = ... ;
Optional<Product> product = products.stream()
.filter(p -> p.getPrice() < 50)
.findFirst();
String name;
if (product.isPresent()) {
name = product.get().getName().toUpperCase();
} else {
name = "NOT FOUND";
}
//getName()返回一個非空字符串
public String getName() {
return name;
}
首選:
List<Product> products = ... ;
String name = products.stream()
.filter(p -> p.getPrice() < 50)
.findFirst()
.map(Product::getName)
.map(String::toUpperCase)
.orElse("NOT FOUND");
// getName()返回一個字符串
public String getName() {
return name;
}
使用 flatMap()
避免:
List<Product> products = ... ;
Optional<Product> product = products.stream()
.filter(p -> p.getPrice() < 50)
.findFirst();
String name = null;
if (product.isPresent()) {
name = product.get().getName().orElse("NOT FOUND").toUpperCase();
}
// getName() return Optional
public Optional<String> getName() {
return Optional.ofNullable(name);
}
首選:
List<Product> products = ... ;
String name = products.stream()
.filter(p -> p.getPrice() < 50)
.findFirst()
.flatMap(Product::getName)
.map(String::toUpperCase)
.orElse("NOT FOUND");
// getName() return Optional
public Optional<String> getName() {
return Optional.ofNullable(name);
}
第23項:使用filter()根據(jù)預定義的規(guī)則拒絕包裝值
傳遞謂詞(條件)作為參數(shù)并獲取一個 Optional 對象。Optional 如果滿足條件,則使用首字母縮寫;如果不滿足條件,則使用空白 字母 Optional 。使用filter()接受或拒絕包裝的值是一種非常方便的方法,因為無需顯式包裝值就可以實現(xiàn)它。
避免:
public boolean validatePasswordLength(User userId) {
Optional<String> password = ...; // User password
if (password.isPresent()) {
return password.get().length() > 5;
}
return false;
}
首選:
public boolean validatePasswordLength(User userId) {
Optional<String> password = ...; // User password
return password.filter((p) -> p.length() > 5).isPresent();
}
第24項:我們是否需要將可選API與流API鏈接在一起?
如果是這樣,則我們使用該 Optional.stream() 方法。從Java 9開始,我們可以通過應用方法將Optional實例視為。當您需要將Optional API與Stream API鏈接在一起時,這很有用。此方法創(chuàng)建一個元素或一個空元素(如果不存在)。此外,我們可以使用Stream API中可用的所有方法。 Stream``Optional.stream()``Stream``Stream``Optional
避免:
public List<Product> getProductList(List<String> productId) {
return productId.stream()
.map(this::fetchProductById)
.filter(Optional::isPresent)
.map(Optional::get)
.collect(toList());
}
public Optional<Product> fetchProductById(String id) {
return Optional.ofNullable(...);
}
首選:
public List<Product> getProductList(List<String> productId) {
return productId.stream()
.map(this::fetchProductById)
.flatMap(Optional::stream)
.collect(toList());
}
public Optional<Product> fetchProductById(String id) {
return Optional.ofNullable(...);
}
實際上,Optional.stream()讓我們來代替filter()和 map()用 flatMap()。
另外,我們可以轉(zhuǎn)換Optional為List:
public static <T> List<T> convertOptionalToList(Optional<T> optional) {
return optional.stream().collect(toList());
}
第25項:避免對可選選項使用身份敏感的操作
這包括引用相等(==),基于身份哈希或同步。Optional類是一個基于價值的類作為 LocalDateTime。
避免:
Product product = new Product();
Optional<Product> op1 = Optional.of(product);
Optional<Product> op2 = Optional.of(product);
// op1 == op2 => false, expected true
if (op1 == op2) { ...
首選:
Product product = new Product();
Optional<Product> op1 = Optional.of(product);
Optional<Product> op2 = Optional.of(product);
// op1.equals(op2) => true,expected true
if (op1.equals(op2)) { ...
第26項:如果Optional為空,則返回一個布爾值。首選Java 11,Optional.isEmpty()
從Java 11開始,true如果方法Optional為,則可以很容易地返回a isEmpty()。
避免使用(Java 11+):
// (Java 11+)
public Optional<String> fetchCartItems(long id) {
Cart cart = ... ; // this may be null
...
return Optional.ofNullable(cart);
}
public boolean cartIsEmpty(long id) {
Optional<String> cart = fetchCartItems(id);
return !cart.isPresent();
首選(Java 11+):
// (Java 11
public Optional<String> fetchCartItems(long id) {
Cart cart = ... ; // this may be null
...
return Optional.ofNullable(cart);
}
public boolean cartIsEmpty(long id) {
Optional<String> cart = fetchCartItems(id);
return cart.isEmpty();
}
這就是總結(jié)的這26個例子,希望可以避免這些陷阱,永遠不會出現(xiàn):java.lang.NullPointerException
到此這篇關(guān)于Java Optional解決空指針異??偨Y(jié)(java 8 功能)的文章就介紹到這了,更多相關(guān)Java Optional空指針異常內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot讀取properties配置文件中的數(shù)據(jù)的三種方法
本文主要介紹了SpringBoot讀取properties配置文件中的數(shù)據(jù)的三種方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2024-06-06
教你在?Java?中實現(xiàn)?Dijkstra?最短路算法的方法
這篇文章主要教你在?Java?中實現(xiàn)?Dijkstra?最短路算法的方法,在實現(xiàn)最短路算法之前需要先實現(xiàn)帶權(quán)有向圖,文章中給大家介紹的非常詳細,需要的朋友可以參考下2022-04-04
JavaWeb項目FullCalendar日歷插件使用的示例代碼
本篇文章主要介紹了JavaWeb項目FullCalendar日歷插件使用的示例代碼,具有一定的參考價值,有興趣的可以了解一下2017-08-08
Spring Boot console log 格式自定義方式
這篇文章主要介紹了Spring Boot console log 格式自定義方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-07-07
Java利用Reflect實現(xiàn)封裝Excel導出工具類
這篇文章主要為大家詳細介紹了Java如何利用Reflect實現(xiàn)封裝Excel導出工具類,文中的實現(xiàn)方法講解詳細,具有一定的借鑒價值,需要的可以參考一下2022-11-11
springboot2.5.6集成RabbitMq實現(xiàn)Topic主題模式(推薦)
這篇文章主要介紹了springboot2.5.6集成RabbitMq實現(xiàn)Topic主題模式(推薦),pom.xml引入依賴和常量類創(chuàng)建,本文通過實例代碼給大家介紹的非常詳細,需要的朋友參考下吧2021-11-11

