Java 8 Optional 詳細用法


一、簡介

Optional 是一個對象容器,具有以下兩個特點:

    1. 提示用戶要注意該對象有可能為null
    2. 簡化if else代碼

真正體現Optional“有效避免空指針異常”是其ifPresent()、orElse()、orElseGet()以及orElseThrow()這幾個方法。


二、使用介紹


1. 創建:

    Optional.empty(): 創建一個空的 Optional 實例

    Optional.of(T t):創建一個 Optional 實例,當 t為null時拋出異常      

    Optional.ofNullable(T t):創建一個 Optional 實例,但當 t為null時不會拋出異常,而是返回一個空的實例

2. 獲取:

    get():獲取optional實例中的對象,當optional 容器為空時報錯

3. 判斷:

    isPresent():判斷optional是否為空,如果空則返回false,否則返回true

    ifPresent(Consumer c):如果optional不為空,則將optional中的對象傳給Comsumer函數

    orElse(T other):如果optional不為空,則返回optional中的對象;如果為null,則返回 other 這個默認值

    orElseGet(Supplier<T> other):如果optional不為空,則返回optional中的對象;如果為null,則使用Supplier函數生成默認值other

    orElseThrow(Supplier<X> exception):如果optional不為空,則返回optional中的對象;如果為null,則拋出Supplier函數生成的異常

4. 過濾:

    filter(Predicate<T> p):如果optional不為空,則執行斷言函數p,如果p的結果為true,則返回原本的optional,否則返回空的optional  

5. 映射:

    map(Function<T, U> mapper):如果optional不為空,則將optional中的對象 t 映射成另外一個對象 u,並將 u 存放到一個新的optional容器中。

    flatMap(Function< T,Optional<U>> mapper):跟上面一樣,在optional不為空的情況下,將對象t映射成另外一個optional

    區別:map會自動將u放到optional中,而flatMap則需要手動給u創建一個optional

三、Demo應用

需求:

    學校想從一批學生中,選出年齡大於等於18,參加過考試並且成績大於80的人去參加比賽。

准備數據:

public class Student {undefined
    private String name;
    private int age;
    private Integer score;
    
    //省略 construct get set
}
 
public List<Student> initData(){undefined
    Student s1 = new Student("張三", 19, 80);
    Student s2 = new Student("李四", 19, 50);
    Student s3 = new Student("王五", 23, null);
    Student s4 = new Student("趙六", 16, 90);
    Student s5 = new Student("錢七", 18, 99);
    Student s6 = new Student("孫八", 20, 40);
    Student s7 = new Student("吳九", 21, 88);
 
    return Arrays.asList(s1, s2, s3, s4, s5, s6, s7);
}
java8 之前寫法:

@Test
public void beforeJava8() {undefined
    List<Student> studentList = initData();
 
    for (Student student : studentList) {undefined
        if (student != null) {undefined
            if (student.getAge() >= 18) {undefined
                Integer score = student.getScore();
                if (score != null && score > 80) {undefined
                    System.out.println("入選:" + student.getName());
                }
            }
        }
    }
}
java8 寫法:

@Test
public void useJava8() {undefined
    List<Student> studentList = initData();
    for (Student student : studentList) {undefined
        Optional<Student> studentOptional = Optional.of(student);
        Integer score = studentOptional.filter(s -> s.getAge() >= 18).map(Student::getScore).orElse(0);
 
        if (score > 80) {undefined
            System.out.println("入選:" + student.getName());
        }
    }
}

實戰演練代碼重構

這么說比較抽象,我么結合具體的項目代碼看一看,例如,前台發起請求,傳來一個報警參數,后台提取報警的ID,去數據庫中查詢對應ID的報警事件,並且獲取該報警事件的名字和類型。我們來看看實現這個需求,傳統的JAVA7的代碼會怎么寫

    public String test0(AlarmAllParmeter alarmAllParmeter) {undefined
        String errorResult = "";
        if (null != alarmAllParmeter) {undefined
            Integer alarmId = alarmAllParmeter.getAlarmEventInputId();
            if (null != alarmId) {undefined
                AlarmEventInput alarmEventInput = alarmEventInputService.get(alarmId);
                if (null != alarmEventInput) {undefined
                    String alarmName = alarmEventInput.getAlarmName();
                    int alarmType = alarmEventInput.getAlarmType();
                    return String.valueOf(alarmType) + "-" + alarmName;
                } else {undefined
                    return errorResult;
                }
            } else {undefined
                return errorResult;
            }
        } else {undefined
            return errorResult;
        }
    }

可以明顯看出,為了防止空指針異常,我們在代碼中寫了大量的if(null != T)的模板代碼。而初次學習Optional類的朋友很可能會寫出如下代碼:

    public String test1(AlarmAllParmeter alarmAllParmeter){undefined
        String errorResult = "";
        Optional<AlarmAllParmeter> op = Optional.ofNullable(alarmAllParmeter);
        if(op.isPresent()){undefined
            Integer alarmId = op.get().getAlarmEventInputId();
            Optional<Integer> op1 = Optional.ofNullable(alarmId);
            if(op1.isPresent()){undefined
                AlarmEventInput alarmEventInput = alarmEventInputService.get(op1.get());
                Optional<AlarmEventInput> op2 = Optional.ofNullable(alarmEventInput);
                if (op2.isPresent()) {undefined
                    String alarmName = alarmEventInput.getAlarmName();
                    int alarmType = alarmEventInput.getAlarmType();
                    return String.valueOf(alarmType) + "-" + alarmName;
                } else {undefined
                    return errorResult;
                }
            }
            else {undefined
                return errorResult;
            }
        }
        else {undefined
            return errorResult;
        }
    }

可以看出,其編程的思路還是停留在“命令式編程”的層面,這種強行用Optional類的做法反而顯得多此一舉,本質上和傳統寫模板代碼一樣,真的就只是給對象套了個Optional容器而已。接下來,我們用Optional正確的打開方式來實現這個需求,重構最初的代碼:

    public String test2(AlarmAllParmeter alarmAllParmeter){undefined
        return Optional.ofNullable(alarmAllParmeter)
                       .map(a -> a.getAlarmEventInputId())
                       .map(a -> alarmEventInputService.get(a))
                       .map(a -> String.valueOf(a.getAlarmType())+"-"+a.getAlarmName())
                       .orElse("");
    }

最終通過Junit4測試結果如下:

public class OptionalTestTest {undefined
 
    AlarmAllParmeter alarmAllParmeter = new AlarmAllParmeter();
    @Before
    public void setAlarmAllParmeter(){undefined
        alarmAllParmeter.setAlarmEventInputId(1001);
    }
 
    @Test
    public void test0() {undefined
        System.out.println("Test0 is: "+new OptionalTest().test0(alarmAllParmeter));
    }
 
    @Test
    public void test1() {undefined
        System.out.println("Test1 is: "+new OptionalTest().test1(alarmAllParmeter));
    }
 
    @Test
    public void test2() {undefined
        System.out.println("Test2 is: "+new OptionalTest().test2(alarmAllParmeter));
    }
} 

控制台輸出結果一致————
Test0 is: 1-測試報警實體
Test1 is: 1-測試報警實體
Test2 is: 1-測試報警實體
 

最后,再次強調一下,這一篇文章所提到的Optional的正確使用方式是進行鏈式處理,而不應該像不少網文所說的那樣去做isPresent()判斷,再去get()取值。

來源:https://blog.csdn.net/luckykapok918/article/details/106239526?utm_medium=distribute.pc_relevant.none-task-blog-2~default~baidujs_title~default-0.pc_relevant_antiscanv2&spm=1001.2101.3001.4242.1&utm_relevant_index=3
 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM