1. 程式人生 > 其它 >【優雅程式碼】03-optional杜絕空指標異常

【優雅程式碼】03-optional杜絕空指標異常

在日常開發中總會遇到NPE問題,但是java提供了optional,可以讓我們流暢寫程式碼的同時避免NPE

【優雅程式碼】03-optional杜絕空指標異常

歡迎關注b站賬號/公眾號【六邊形戰士夏寧】,一個要把各項指標拉滿的男人。該文章已在github目錄收錄。
螢幕前的大帥比大漂亮如果有幫助到你的話請順手點個贊、加個收藏這對我真的很重要。別下次一定了,都不關注上哪下次一定。

1.背景介紹

在日常開發中總會遇到NPE問題,但是java提供了optional,可以讓我們流暢寫程式碼的同時避免NPE

2.建立Child、Parent、GrandParent的層級

建立如下結構的類GrandParent->Parent->List

  • Child
@Data
@AllArgsConstructor
@ToString(callSuper = true)
@NoArgsConstructor
@Accessors(chain = true)
public class Child {
    private List<String> list;
    private String str;
}
  • GrandParent
@Data
@AllArgsConstructor
@ToString(callSuper = true)
@NoArgsConstructor
@Accessors(chain = true)
public class GrandParent {
    private Parent parent;
}
  • Parent
@Data
@AllArgsConstructor
@ToString(callSuper = true)
@NoArgsConstructor
@Accessors(chain = true)
public class Parent {
    private Child child;
}

  • 測試類
GrandParent opt1 = null;
// 獲取最底層資料。此時最頂層是null,不會報NPE
String opt1Str = Optional.ofNullable(opt1).map(o1 -> o1.getParent())
                .map(o2 -> o2.getChild()).map(l -> l.getStr()).orElse(null);
System.out.println(String.format("%s:%s", "opt1Object", opt1Str));

// 獲取最底層資料。此時第二層是null,不會報NPE
GrandParent opt2 = null;
List<String> opt2list = Optional.ofNullable(opt2).map(o1 -> o1.getParent())
                .map(o2 -> o2.getChild()).map(l -> l.getList()).orElse(null);
System.out.println(String.format("%s:%s", "opt2list", opt2list));

// 獲取最底層資料。此時所有資料都是全的,無論是list還是string都沒有問題
GrandParent opt3 = new GrandParent().setParent(new Parent().setChild(new Child().setStr("ssss").setList(Stream.of("1", "2").collect(Collectors.toList()))));
List<String> opt3list = Optional.ofNullable(opt3).map(o1 -> o1.getParent())
                .map(o2 -> o2.getChild()).map(l -> l.getList()).orElse(null);
String opt3Str = Optional.ofNullable(opt3).map(o1 -> o1.getParent())
                .map(o2 -> o2.getChild()).map(l -> l.getStr()).orElse(null);
System.out.println(String.format("%s:%s", "opt3list", opt3list));
System.out.println(String.format("%s:%s", "opt3Str", opt3Str));
  • 輸出結果
opt1Object:null
opt2list:null
opt3list:[1, 2]
opt3Str:ssss

可以發現巢狀類無論是string還是list,中間任何一個類為null都會直接返回null,而不用去多層巢狀if這種很蠢的做法

3.非常規復雜optional用法

當然平常時候總會遇到一些奇奇怪怪的結果,例如查詢資料庫會返回List<Map<String, String>>這樣的結構,也是可以用Optinoal做的

// 獲取list嵌map並隨機返回一個key,此時list為null的獲取不會NPE
List<Map<String, String>> listR = null;
String result = Optional.ofNullable(listR).flatMap(l -> l.stream().findAny())
        .flatMap(l -> l.keySet().stream().findAny()).orElse(null);
System.out.println(result);

// 獲取list嵌map並隨機返回一個key,此時list無元素的獲取不會NPE
listR = new ArrayList<Map<String, String>>();
result = Optional.ofNullable(listR).flatMap(l -> l.stream().findAny())
        .flatMap(l -> l.keySet().stream().findAny()).orElse(null);
System.out.println(result);

// 獲取list嵌map並隨機返回一個key,此時有1個HashMap,但HashMap無元素的獲取不會NPE
listR = new ArrayList<Map<String, String>>() {{
    add(new HashMap<String, String>());
}};
result = Optional.ofNullable(listR).flatMap(l -> l.stream().findAny())
        .flatMap(l -> l.keySet().stream().findAny()).orElse(null);
System.out.println(result);

// 獲取list嵌map並隨機返回一個key,此時所有結構完全
listR = new ArrayList<Map<String, String>>() {{
    add(new HashMap<String, String>() {{
        put("C", "0");
    }});
}};
result = Optional.ofNullable(listR).flatMap(l -> l.stream().findAny())
        .flatMap(l -> l.keySet().stream().findAny()).orElse(null);
System.out.println(result);

輸出結果如下:

null
null
null
C

4.其它優秀杜絕空指標異常的優秀方法

首先是lombok的@NonNull,這個可以作用於方法引數上,如果傳入空則直接拋異常,並在日誌精準打印出異常位置及情況,非常適合校驗引數,增加程式碼簡潔性。非空指標的校驗方式可以看優雅和前端互動

當然剛才寫的類還是返回了null,但是沒關係,可以用以下工具類,在各種情況下都可以丟擲自定義異常或直接return出去

 // 常規物件的判空不會NPE
System.out.println(String.format("%s:%s", "StringUtils", StringUtils.isBlank(null)));
System.out.println(String.format("%s:%s", "defaultIfNull", ObjectUtils.defaultIfNull(null, "defaultIfNull")));
List list = null;
Map map = null;
Set set = null;
String[] arr = null;
// 常規collection的判空不會NPE
System.out.println(String.format("%s:%s", "list", CollectionUtils.isEmpty(list)));
System.out.println(String.format("%s:%s", "map", CollectionUtils.isEmpty(map)));
System.out.println(String.format("%s:%s", "set", CollectionUtils.isEmpty(set)));
System.out.println(String.format("%s:%s", "arr", ArrayUtils.isEmpty(arr)));

輸出結果如下:

StringUtils:true
defaultIfNull:defaultIfNull
list:true
map:true
set:true
arr:true