一、簡介
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