一文掌握 Java8 的 Optional 的 6 種操作

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"你好,我是看山。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Java8 中引入了一個特別有意思類:","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Optional","attrs":{}}],"attrs":{}},{"type":"text","text":",一個可以讓我們更加輕鬆的避免 NPE(空指針異常,NullPointException)的工具。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"很久很久以前,爲了避免 NPE,我們會寫很多類似","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"if (obj != null) {}","attrs":{}}],"attrs":{}},{"type":"text","text":"的代碼,有時候忘記寫,就可能出現 NPE,造成線上故障。在 Java 技術棧中,如果誰的代碼出現了 NPE,有極大的可能會被笑話,這個異常被很多人認爲是低級錯誤。","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Optional","attrs":{}}],"attrs":{}},{"type":"text","text":"的出現,可以讓大家更加輕鬆的避免因爲低級錯誤被嘲諷的概率。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"定義示例數據","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"先定義待操作對象,萬能的","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Student","attrs":{}}],"attrs":{}},{"type":"text","text":"類和","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Clazz","attrs":{}}],"attrs":{}},{"type":"text","text":"類(用到了 lombok 和 guava):","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"@Data\n@AllArgsConstructor\n@NoArgsConstructor\npublic class Clazz {\n private String id;\n private String name;\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"@Data\n@AllArgsConstructor\n@NoArgsConstructor\npublic class Student {\n private String id;\n private String name;\n private Clazz clazz;\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"然後定義一組測試數據:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"final Clazz clazz1 = new Clazz(\"1\", \"高一一班\");\n\nfinal Student s1 = new Student(\"1\", \"張三\", clazz1);\nfinal Student s2 = new Student(\"2\", \"李四\", null);\n\nfinal List students = Lists.newArrayList(s1, s2);\nfinal List emptyStudents = Lists.newArrayList();\nfinal List nullStudents = null;\n","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"創建實例:of、ofNullable","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"爲了控制生成實例的方式,也是爲了收緊空值","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Optional","attrs":{}}],"attrs":{}},{"type":"text","text":"的定義,","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Optional","attrs":{}}],"attrs":{}},{"type":"text","text":"將構造函數定義爲","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"private","attrs":{}}],"attrs":{}},{"type":"text","text":"。想要創建","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Optional","attrs":{}}],"attrs":{}},{"type":"text","text":"實例,可以藉助","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"of","attrs":{}}],"attrs":{}},{"type":"text","text":"和","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"ofNullable","attrs":{}}],"attrs":{}},{"type":"text","text":"兩個方法實現。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這兩個方法的區別在於:","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"of","attrs":{}}],"attrs":{}},{"type":"text","text":"方法傳入的參數不能是","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"null","attrs":{}}],"attrs":{}},{"type":"text","text":"的,否則會拋出","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"NullPointerException","attrs":{}}],"attrs":{}},{"type":"text","text":"。所以,對於可能是","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"null","attrs":{}}],"attrs":{}},{"type":"text","text":"的結果,一定使用","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"ofNullable","attrs":{}}],"attrs":{}},{"type":"text","text":"。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"代碼如下:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"Optional.of(students);\nOptional.of(emptyStudents);\nOptional.ofNullable(nullStudents);\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"Optional","attrs":{}}],"attrs":{}},{"type":"text","text":"類中還有一個靜態方法:","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"empty","attrs":{}}],"attrs":{}},{"type":"text","text":",這個方法直接返回了內部定義的一個常量","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Optional> EMPTY = new Optional<>()","attrs":{}}],"attrs":{}},{"type":"text","text":",這個常量的","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"value","attrs":{}}],"attrs":{}},{"type":"text","text":"是","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"null","attrs":{}}],"attrs":{}},{"type":"text","text":"。","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"ofNullable","attrs":{}}],"attrs":{}},{"type":"text","text":"方法也是藉助了","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"empty","attrs":{}}],"attrs":{}},{"type":"text","text":"實現","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"null","attrs":{}}],"attrs":{}},{"type":"text","text":"的包裝:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public static Optional ofNullable(T value) {\n return value == null ? empty() : of(value);\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"所以說,對於","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"null","attrs":{}}],"attrs":{}},{"type":"text","text":"的","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Optional","attrs":{}}],"attrs":{}},{"type":"text","text":"包裝類,指向的都是相同的實例對象,","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Optional.empty() == Optional.ofNullable(null)","attrs":{}}],"attrs":{}},{"type":"text","text":"返回的是","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"true","attrs":{}}],"attrs":{}},{"type":"text","text":"。換句話說,空","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Optional","attrs":{}}],"attrs":{}},{"type":"text","text":"是單例的。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"爲了方便描述,下文中對值爲","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"null","attrs":{}}],"attrs":{}},{"type":"text","text":"的","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Optional","attrs":{}}],"attrs":{}},{"type":"text","text":"統稱爲“","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"空","attrs":{}},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"Optional","attrs":{}}],"attrs":{}},{"type":"text","text":"”。","attrs":{}}]}],"attrs":{}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"獲取數據:get","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"Optional","attrs":{}}],"attrs":{}},{"type":"text","text":"的","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"get","attrs":{}}],"attrs":{}},{"type":"text","text":"方法有些坑人,先看下它的源碼:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public T get() {\n if (value == null) {\n throw new NoSuchElementException(\"No value present\");\n }\n return value;\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"也就是說,","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Optional","attrs":{}}],"attrs":{}},{"type":"text","text":"值爲空時,使用","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"get","attrs":{}}],"attrs":{}},{"type":"text","text":"方法將拋出","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"NoSuchElementException","attrs":{}}],"attrs":{}},{"type":"text","text":"異常。如果不想拋出異常,或者能夠 100%確定不是空","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Optional","attrs":{}}],"attrs":{}},{"type":"text","text":",或者使用","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"isPresent","attrs":{}}],"attrs":{}},{"type":"text","text":"方法判斷。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果能 100%確定不是空","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Optional","attrs":{}}],"attrs":{}},{"type":"text","text":",那就沒有必要使用","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Optional","attrs":{}}],"attrs":{}},{"type":"text","text":"包裝,直接返回即可。如果需要使用","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"isPresent","attrs":{}}],"attrs":{}},{"type":"text","text":"方法,那就和直接判空沒有區別了。所以,無論是第一種情況還是第二種情況,都違背了設計這個類的初衷。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"值爲空判斷:isPresent、ifPresent","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"isPresent","attrs":{}}],"attrs":{}},{"type":"text","text":"用來判斷值是否爲空,類似於","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"obj != null","attrs":{}}],"attrs":{}},{"type":"text","text":",","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"ifPresent","attrs":{}}],"attrs":{}},{"type":"text","text":"可以傳入一個","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Consumer","attrs":{}}],"attrs":{}},{"type":"text","text":"操作,當值不爲空的時候,會執行","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Consumer","attrs":{}}],"attrs":{}},{"type":"text","text":"函數。比如:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"final Optional> nullValue = Optional.ofNullable(nullStudents);\n\nif (nullValue.isPresent()) {\n System.out.println(\"value: \" + nullValue.get());\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"上面的方法等價於:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"nullValue.ifPresent(value -> System.out.println(\"value: \" + value));\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"isPresent","attrs":{}}],"attrs":{}},{"type":"text","text":"判斷的寫法上是不是感覺很熟悉,感覺可以直接寫爲:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"if (nullStudents != null) {\n System.out.println(\"value: \" + nullStudents);\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"對於","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"isPresent","attrs":{}}],"attrs":{}},{"type":"text","text":",如果是在自己可控的代碼範圍內,完全沒有必要將值封裝之後再判空。對於自己不可控的代碼,後續的","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"filter","attrs":{}}],"attrs":{}},{"type":"text","text":"或者","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"map","attrs":{}}],"attrs":{}},{"type":"text","text":"方法可能比","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"isPresent","attrs":{}}],"attrs":{}},{"type":"text","text":"更好用一些。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"對於","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"ifPresent","attrs":{}}],"attrs":{}},{"type":"text","text":",在使用的時候會有一些限制,就是必須是非空","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Optional","attrs":{}}],"attrs":{}},{"type":"text","text":"的時候,在會執行傳入的","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Consumer","attrs":{}}],"attrs":{}},{"type":"text","text":"函數。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"值處理:map、flatMap","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"map","attrs":{}}],"attrs":{}},{"type":"text","text":"和","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"flatMap","attrs":{}}],"attrs":{}},{"type":"text","text":"是對","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Optional","attrs":{}}],"attrs":{}},{"type":"text","text":"的值進行操作的方法,區別在於,","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"map","attrs":{}}],"attrs":{}},{"type":"text","text":"會將結果包裝到","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Optional","attrs":{}}],"attrs":{}},{"type":"text","text":"中返回,","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"flatMap","attrs":{}}],"attrs":{}},{"type":"text","text":"不會。但是兩個方法返回值都是","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Optional","attrs":{}}],"attrs":{}},{"type":"text","text":"類型,這也就要求,","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"flatMap","attrs":{}}],"attrs":{}},{"type":"text","text":"的方法函數返回值需要是","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Optional","attrs":{}}],"attrs":{}},{"type":"text","text":"類型。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們來看看","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"map","attrs":{}}],"attrs":{}},{"type":"text","text":"的實現:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public Optional map(Function super T, ? extends U> mapper) {\n Objects.requireNonNull(mapper);\n if (!isPresent())\n return empty();\n else {\n return Optional.ofNullable(mapper.apply(value));\n }\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"可以看到,如果","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Optional","attrs":{}}],"attrs":{}},{"type":"text","text":"的值爲空,","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"map","attrs":{}}],"attrs":{}},{"type":"text","text":"直接返回","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Optional.EMPTY","attrs":{}}],"attrs":{}},{"type":"text","text":",否則會執行函數結果,並使用","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Optional.ofNullable","attrs":{}}],"attrs":{}},{"type":"text","text":"包裝並返回。也即是說,只要類結構允許,我們可以一直","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"map","attrs":{}}],"attrs":{}},{"type":"text","text":"下去,就像是扒洋蔥,一層一層,直到核心。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"比如,我們要獲取","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"s2","attrs":{}}],"attrs":{}},{"type":"text","text":"所在班級名稱,在定義的時候,我們將","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"s2","attrs":{}}],"attrs":{}},{"type":"text","text":"的","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"clazz","attrs":{}}],"attrs":{}},{"type":"text","text":"屬性定義爲 null,如果以前需要寫爲:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"String clazzNameOld;\nif (s2 != null && s2.getClazz() != null && s2.getClazz().getName() != null) {\n clazzNameOld = s2.getClazz().getName();\n} else {\n clazzNameOld = \"DEFAULT_NAME\";\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"現在藉助","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Optional","attrs":{}}],"attrs":{}},{"type":"text","text":"可以寫爲:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"final String clazzName = Optional.ofNullable(s2)\n .map(Student::getClazz)\n .map(Clazz::getName)\n .orElse(\"DEFAULT_NAME\");\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"從代碼上似乎沒有多大改變,但是如果","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Clazz","attrs":{}}],"attrs":{}},{"type":"text","text":"內部還有類對象。或者,我們在","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"if","attrs":{}}],"attrs":{}},{"type":"text","text":"判斷的時候,少寫一層檢查呢?而且,","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"map","attrs":{}}],"attrs":{}},{"type":"text","text":"的精巧還在於它的返回值永遠是","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Optional","attrs":{}}],"attrs":{}},{"type":"text","text":",這樣,我們可以重複調用","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"map","attrs":{}}],"attrs":{}},{"type":"text","text":"方法,而不需要中間被打斷,增加各種判空邏輯。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"值爲空的處理:orElse、orElseGet、orElseThrow","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這幾個方法可以與","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"map","attrs":{}}],"attrs":{}},{"type":"text","text":"操作結合,一起完成對象操作。當值爲空時,","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"orElse","attrs":{}}],"attrs":{}},{"type":"text","text":"和","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"orElseGet","attrs":{}}],"attrs":{}},{"type":"text","text":"返回默認值,","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"orElseThrow","attrs":{}}],"attrs":{}},{"type":"text","text":"拋出指定的異常。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"orElse","attrs":{}}],"attrs":{}},{"type":"text","text":"和","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"orElseGet","attrs":{}}],"attrs":{}},{"type":"text","text":"的區別是,","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"orElse","attrs":{}}],"attrs":{}},{"type":"text","text":"方法傳入的參數是明確的默認值,","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"orElseGet","attrs":{}}],"attrs":{}},{"type":"text","text":"方法傳入的參數是獲取默認值的函數。如果默認值的構造過程比較複雜,需要經過一系列的運算邏輯,那一定要使用","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"orElseGet","attrs":{}}],"attrs":{}},{"type":"text","text":",因爲","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"orElseGet","attrs":{}}],"attrs":{}},{"type":"text","text":"是在值爲空的時候,纔會執行函數,並返回默認值,如果值不爲空,則不會執行函數,相比於","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"orElse","attrs":{}}],"attrs":{}},{"type":"text","text":"而言,減少了一次構造默認值的過程。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"同樣以上面的例子:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"orElse","attrs":{}}],"attrs":{}},{"type":"text","text":"的寫法:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"final String clazzName = Optional.ofNullable(s2)\n .map(Student::getClazz)\n .map(Clazz::getName)\n .orElse(null);\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"orElseGet","attrs":{}}],"attrs":{}},{"type":"text","text":"的寫法:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"final String clazzName = Optional.of(s2)\n .map(Student::getClazz)\n .map(Clazz::getName)\n .orElseGet(() -> null);\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"clazz","attrs":{}}],"attrs":{}},{"type":"text","text":"屬性一定不爲空,爲空則返回異常,可以使用","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"orElseThrow","attrs":{}}],"attrs":{}},{"type":"text","text":":","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"final String clazzName = Optional.of(s2)\n .map(Student::getClazz)\n .map(Clazz::getName)\n .orElseThrow(() -> new IllegalArgumentException(\"clazz屬性不合法\"));\n","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"條件過濾:filter","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"filter","attrs":{}}],"attrs":{}},{"type":"text","text":"方法提供的是值驗證,如果值驗證爲 true,返回當前值;否則,返回空","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Optional","attrs":{}}],"attrs":{}},{"type":"text","text":"。比如,我們要遍歷","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"students","attrs":{}}],"attrs":{}},{"type":"text","text":",找到班級屬性爲空的,打印學生id:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"for (final Student s : students) {\n Optional.of(s)\n .filter(x -> x.getClazz() == null)\n .ifPresent(x -> System.out.println(x.getId()));\n}\n","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"其他:equals、hashCode、toString","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"Optional","attrs":{}}],"attrs":{}},{"type":"text","text":"重寫了這三個方法。因爲","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Optional","attrs":{}}],"attrs":{}},{"type":"text","text":"可以認爲是包裝類,所以還是圍繞這被包裝的值重寫這三個方法。下面給出這三個方法的源碼:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public boolean equals(Object obj) {\n // 同一對象判斷\n if (this == obj) {\n return true;\n }\n\n // 類型判斷\n if (!(obj instanceof Optional)) {\n return false;\n }\n\n Optional> other = (Optional>) obj;\n // 最終還是值的判斷\n return Objects.equals(value, other.value);\n}\n\npublic int hashCode() {\n // 直接返回值的hashCode\n return Objects.hashCode(value);\n}\n\npublic String toString() {\n return value != null\n ? String.format(\"Optional[%s]\", value) // 用到了值的toString結果\n : \"Optional.empty\";\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"equals","attrs":{}}],"attrs":{}},{"type":"text","text":"方法,","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Optional.of(s1).equals(Optional.of(s2))","attrs":{}}],"attrs":{}},{"type":"text","text":"完全等價於","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"s1.equals(s2)","attrs":{}}],"attrs":{}},{"type":"text","text":"。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"hashCode","attrs":{}}],"attrs":{}},{"type":"text","text":"方法,直接返回的是值的hashCode,如果是空","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Optional","attrs":{}}],"attrs":{}},{"type":"text","text":",返回的是0。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"toString","attrs":{}}],"attrs":{}},{"type":"text","text":"方法,爲了能夠識別是","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Optional","attrs":{}}],"attrs":{}},{"type":"text","text":",將打印數據包裝了一下。如果是空","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Optional","attrs":{}}],"attrs":{}},{"type":"text","text":",返回的是字符串“Optional.empty”;如果是非空,返回是是“Optional[值的toString]”。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"文末總結","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"NPE 之所以討厭,就是隻要出現 NPE,我們就能夠解決。但是一旦出現,都已經是事後,可能已經出現線上故障。偏偏在 Java 語言中,NPE 又很容易出現。","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Optional","attrs":{}}],"attrs":{}},{"type":"text","text":"提供了模板方法,有效且高效的避免 NPE。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"接下來,我們針對上面的使用,總結一下:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":1,"normalizeStart":1},"content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"Optional","attrs":{}}],"attrs":{}},{"type":"text","text":"是一個包裝類,且不可變,不可序列化","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"沒有公共構造函數,創建需要使用","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"of","attrs":{}}],"attrs":{}},{"type":"text","text":"、","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"ofNullable","attrs":{}}],"attrs":{}},{"type":"text","text":"方法","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","text":"空","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Optional","attrs":{}}],"attrs":{}},{"type":"text","text":"是單例,都是引用","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Optional.EMPTY","attrs":{}}],"attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":4,"align":null,"origin":null},"content":[{"type":"text","text":"想要獲取","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Optional","attrs":{}}],"attrs":{}},{"type":"text","text":"的值,可以使用","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"get","attrs":{}}],"attrs":{}},{"type":"text","text":"、","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"orElse","attrs":{}}],"attrs":{}},{"type":"text","text":"、","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"orElseGet","attrs":{}}],"attrs":{}},{"type":"text","text":"、","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"orElseThrow","attrs":{}}],"attrs":{}}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"另外,還有一些實踐上的建議:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":1,"normalizeStart":1},"content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"使用","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"get","attrs":{}}],"attrs":{}},{"type":"text","text":"方法前,必須使用","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"isPresent","attrs":{}}],"attrs":{}},{"type":"text","text":"檢查。但是使用","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"isPresent","attrs":{}}],"attrs":{}},{"type":"text","text":"前,先思考下是否可以使用","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"orElse","attrs":{}}],"attrs":{}},{"type":"text","text":"、","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"orElseGet","attrs":{}}],"attrs":{}},{"type":"text","text":"等方法代替實現。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"orElse","attrs":{}}],"attrs":{}},{"type":"text","text":"和","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"orElseGet","attrs":{}}],"attrs":{}},{"type":"text","text":",優先選擇","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"orElseGet","attrs":{}}],"attrs":{}},{"type":"text","text":",這個是惰性計算","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"Optional","attrs":{}}],"attrs":{}},{"type":"text","text":"不要作爲參數或者類屬性,可以作爲返回值","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":4,"align":null,"origin":null},"content":[{"type":"text","text":"儘量將","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"map","attrs":{}}],"attrs":{}},{"type":"text","text":"、","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"filter","attrs":{}}],"attrs":{}},{"type":"text","text":"的函數參數抽出去作爲單獨方法,這樣能夠保持鏈式調用","attrs":{}}]}]}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"推薦閱讀","attrs":{}}]},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://xie.infoq.cn/article/50324deb1efcf3845113e341f","title":"","type":null},"content":[{"type":"text","text":"https://xie.infoq.cn/article/50324deb1efcf3845113e341f","attrs":{}}]}]}]}],"attrs":{}},{"type":"horizontalrule","attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/9e/9e305acd1cca75053c144cb28adc6061.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章