java中npe问题,【Java 8】巧用Optional之优雅规避NPE问题
避之不及的 NullPointerException
NPE : NullPointerException
空指針異常是最常見的Java異常之一,拋出NPE錯(cuò)誤不是用戶操作的錯(cuò)誤,而是開發(fā)人員的錯(cuò)誤,應(yīng)該被避免,那么只能在每個(gè)方法中加入非空檢查,閱讀性和維護(hù)性都比較差。
以下是一個(gè)常見的嵌套對(duì)象:一個(gè)用戶所擁有的汽車,以及為這個(gè)汽車配備的保險(xiǎn)。
public class User {
private String userName;
private Car car;
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public Car getCar() {
return car;
}
public void setCar(Car car) {
this.car = car;
}
}
public class Car {
private String carName;
private Insurance insurance;
public String getCarName() {
return carName;
}
public void setCarName(String carName) {
this.carName = carName;
}
public Insurance getInsurance() {
return insurance;
}
public void setInsurance(Insurance insurance) {
this.insurance = insurance;
}
}
public class Insurance {
private String insuranceName;
public String getInsuranceName() {
return insuranceName;
}
public void setInsuranceName(String insuranceName) {
this.insuranceName = insuranceName;
}
}
如果我們此時(shí),需要獲取一個(gè)用戶對(duì)應(yīng)的汽車保險(xiǎn)名稱,我們可能會(huì)寫出來以下的代碼
private String getInsuranceName(User user) {
return user.getCar().getInsurance().getInsuranceName();
}
顯然上面的程序是存在諸多NullPointerException隱患的,為了保證程序的健壯性,我們需要盡量避免出現(xiàn)空指針NullPointerException,那么通常我們會(huì)有以下兩種寫法。
深層質(zhì)疑
private String getInsuranceName(User user) {
if (user != null) {
Car car = user.getCar();
if (car != null) {
Insurance insurance = car.getInsurance();
if (insurance != null) {
return insurance.getInsuranceName();
}
}
}
return "not found";
}
及時(shí)退出
private String getInsuranceName(User user) {
if (user == null) {
return "not found";
}
Car car = user.getCar();
if (car == null) {
return "not found";
}
Insurance insurance = car.getInsurance();
if (insurance == null) {
return "not found";
}
return insurance.getInsuranceName();
}
為了避免出現(xiàn)空指針,我們通常會(huì)采用以上兩種寫法,但是它們復(fù)雜又冗余,為了鼓勵(lì)程序員寫更干凈的代碼,代碼設(shè)計(jì)變得更加的優(yōu)雅。JAVA8提供了Optional類來優(yōu)化這種寫法。
Optional
Java 8中引入了一個(gè)新的類java.util.Optional。這是一個(gè)封裝Optional值的類。舉例來說,使用新的類意味著,如果你知道一個(gè)人可能有也可能沒有車,那么User類內(nèi)部的car變量就不應(yīng)該聲明為Car, 遇某人沒有車時(shí)把null引用值給它,而是應(yīng)該如下圖所示直接將其聲明為Optional類型。
變量存在時(shí),Optional類只是對(duì)類簡(jiǎn)單封裝。變量不存在時(shí),缺失的值會(huì)被建模成一個(gè)“空” 的Optional對(duì)象,由方法Optional.empty()返回。它返回Optional類的特定單一實(shí)例。
null引用和Optional.empty() 有什么本質(zhì)的區(qū)別嗎?
從語義上,你可以把它們當(dāng)作一回事兒,但是實(shí)際中它們之間的差別非常大:如果你嘗試直接引用一個(gè)null,一定會(huì)觸發(fā)NullPointerException,不過使用 Optional.empty()就完全沒事兒,它是Optional類的一個(gè)有效對(duì)象。
使用Optional而不是null的一個(gè)非常重要而又實(shí)際的語義區(qū)別是,第一個(gè)例子中,我們?cè)诼暶髯兞繒r(shí)使用的是Optional類型,而不是Car類型,這句聲明非常清楚地表明了這里發(fā)生變量缺失是允許的。與此相反,使用Car這樣的類型,可能將變量賦值為null,你只能依賴你對(duì)業(yè)務(wù)模型的理解,判斷一個(gè)null是否屬于該變量的有效值又或是異常情況。
public class User {
private String userName;
private Optional car;
public String getUserName() {
return userName;
}
public Optional getCar() {
return car;
}
}
public class Car {
private String carName;
private Optional insurance;
public String getCarName() {
return carName;
}
public Optional getInsurance() {
return insurance;
}
}
public class Insurance {
private String insuranceName;
public String getInsuranceName() {
return insuranceName;
}
}
發(fā)現(xiàn)Optional是如何 富你模型的語義了吧。代碼中user引用的是Optional, 而car引用的是Optional,這種方式非常清晰地表達(dá)了你的模型中一個(gè)user 可能有也可能沒有car的情形,同樣,car可能進(jìn)行了保險(xiǎn),也可能沒有保險(xiǎn)。
與此同時(shí),我們看到insurance的名稱insuranceName被聲明成String類型,而不是Optional ,這非常清楚地表明聲明為insurance的類中的名稱字段insuranceName是必須存在的。
使用這種方式, 一旦通過引用insurance獲取insuranceName時(shí)發(fā)生NullPointerException,你就能非常確定地知道出錯(cuò)的原因,不再需要為其添加null的檢查查,因?yàn)閚ull的檢查查只會(huì)掩蓋問題,并未真正地修復(fù)問題。
insurance必須有個(gè)名字,所以,如果你遇到一個(gè)insurance沒有名稱,你需要調(diào)查你的數(shù)據(jù)出了什么問題,而不應(yīng)該再添加一段代碼,將這個(gè)問題隱藏。
Optional的方法介紹
創(chuàng)建Optional
of(T value)
如果構(gòu)造參數(shù)是一個(gè)null,這段代碼會(huì)立即 出一個(gè)NullPointerException,而不是等到你 圖訪問car的屬性值時(shí)才返回一個(gè)錯(cuò)誤。
public static Optional of(T value) {
return new Optional<>(value);
}
ofNullable(T value)
創(chuàng)建一個(gè)允許null值的Optional對(duì)象
public static Optional ofNullable(T value) {
return value == null ? empty() : of(value);
}
empty()
創(chuàng)建一個(gè)空的Optional對(duì)象
public static Optional empty() {
Optional t = (Optional) EMPTY;
return t;
}
常用方法get()是這些方法中最簡(jiǎn)單但又最不安全的方法。如果變量存在,它直接返回封裝的變量值,否則就拋出一個(gè)NoSuchElementException異常。所以,除非你非常確定Optional變量一定包含值,否則最好不要使用這個(gè)方法。
orElse(T other),它允許你在 Optional對(duì)象不包含值時(shí)提供一個(gè)默認(rèn)值。
orElseGet(Supplier extends T> other)是orElse方法的延遲調(diào)用版,Supplier方法只有在Optional對(duì)象不含值時(shí)才執(zhí)行調(diào)用。
orElseThrow(Supplier extends X> exceptionSupplier)和get方法非常類似,它們?cè)庥鯫ptional對(duì)象為空時(shí)都會(huì)拋出一個(gè)異常,但是使用orElseThrow你可以定制希望拋出的異常類型。
ifPresent(Consumer super T>)讓你能在變量值存在時(shí)執(zhí)行一個(gè)作為參數(shù)傳入的方法,否則就不進(jìn)行任何操作。
注意:orElse(T other)和orElseGet(Supplier extends T> other)的區(qū)別
這兩個(gè)函數(shù)的區(qū)別:當(dāng)value值不為null時(shí),orElse函數(shù)依然會(huì)執(zhí)行返回T的方法,而orElseGet函數(shù)并不會(huì)執(zhí)行返回T的方法。
用map從Optional中提取和轉(zhuǎn)換值
map(Function super T, ? extends U> mapper)
可以把Optional對(duì)象看成一種特殊的集合數(shù)據(jù),它至多包含一個(gè)元素。如果Optional包含一個(gè)值,那函數(shù)就將該值作為參數(shù)傳遞給map,對(duì)該值進(jìn)行轉(zhuǎn)換。如果Optional為空,就什么也不做。
String optionMap = Optional.ofNullable("abc").map(value -> value.toUpperCase()).get();
使用flatMap鏈接Optional對(duì)象
flatMap(Function super T, Optional> mapper)
將兩層的optional合并為一個(gè)
String optionFlatMap = Optional.ofNullable("abc").flatMap(value -> Optional.of((value + "flat-map").toUpperCase())).get();
用filter剔除特定的值
filter(Predicate super T> predicate)
filter方法接受一個(gè)謂詞作為參數(shù)。如果Optional對(duì)象的值存在,并且它符合謂詞的條件, filter方法就返回其值;否則它就返回一個(gè)空的Optional對(duì)象。
Optional filterOptional = Optional.ofNullable("abc").filter(value -> Objects.equals(value, "abc"));
實(shí)戰(zhàn)
嘗試獲取用戶的用戶名稱,不存在則返回默認(rèn)值
String userName = Optional.ofNullable(userOfNull).orElse(new User()).getUserName();
嘗試獲取用戶的carName,不存在則返回null
String carName = Optional.ofNullable(userOfNull).map(u -> u.getCar()).map(c -> c.getCarName()).orElse(null);
用戶名存在的時(shí)候轉(zhuǎn)為大寫
Optional.ofNullable(user).map(u -> u.getUserName()).ifPresent(userName -> System.out.println(userName.toUpperCase()));
過濾出來用戶名稱是張三的用戶
Optional.ofNullable(user).filter(u -> Objects.equals(u.getUserName(),"張三")).map(u -> u.getUserName()).ifPresent(userName -> System.out.println(userName + "實(shí)戰(zhàn)Test"));
將張三的用戶名稱更改為李四
Optional.ofNullable(user).ifPresent(x -> {
if (Objects.equals(user.getUserName(),"張三")){
user.setUserName("李四");
}
});
Optional.ofNullable(user).filter(u -> Objects.equals(user.getUserName(),"張三")).ifPresent(x -> user.setUserName("李四"));
總結(jié)
以上是生活随笔為你收集整理的java中npe问题,【Java 8】巧用Optional之优雅规避NPE问题的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 按窗口文件php代码,在Windows命
- 下一篇: php mk的支持扩展,Linux部署