java 8(三)--用Optional取代null
一、引言
先假設有三個類,Student, Bag, Book:
class Student{ private Bag bag; public Student(Bag bag) { this.bag = bag; } public Bag getBag() { return bag; } public void setBag(Bag bag) { this.bag = bag; }}class Bag{ private Book book; public Bag(Book book) { this.book = book;現想要查詢某個學生的書包中的書的名字,在java 8前是這樣的:
public String getBookName(Student student){ if (student != null){ Bag bag = student.getBag(); if (bag != null){ Book book = bag.getBook(); if (book != null){ return book.getName();可以看到一堆判斷,程式碼可讀性很差,而且擴充套件性也不好。
再看看java 8中的改造和實現:
class Student{ private Optional<Bag> bag; public Student(Optional<Bag> bag) { this.bag = bag; } public Optional<Bag> getBag() { return bag; } public void setBag(Optional<Bag> bag) { this.bag = bag; }}class Bag{ private Optional<Book> book; public Bag(Optional<Book> book) { this.book = book; } public Optional<Book> getBook() { return book; } public void setBook(Optional<Book> book) { this.book = book; }}class Book{ private String name; public Book(String name) { this.name = name; } public String getName() { } public void setName(String name) { this.name = name; }}接下來這樣就可以了:
public String getBookName(Optional<Student> student){ return student .flatMap(Student::getBag) .flatMap(Bag::getBook) .map(Book::getName) .orElse("Unknown"); }這裡有兩個明顯的好處:
用Optional明確說明了可能為空,比java 8之前更具表述性;
另外程式碼的可讀性也更強。
PS:這裡需要注意的是,student .flatMap(Student::getBag)....中用了flatMap代替map,是因為map只是把流中的元素對映為流中另一個元素,而這裡Student.getBag()返回的是一個Optional<Bag>,如果這裡用map,就會報錯,無法編譯,getBook()是Bag的方法,Optional顯然沒有。
flatMap將流扁平化為一個流的特性在這裡則正好合適,不理解flatMap的可以再看看上一章節。
二、簡介
看了上面的例子,對Optional應該有了一個簡單的瞭解。
Optional是java 8引入的用於代替null的一個工具類,目的是為了改善程式碼的可讀性,同時減少煩人的NullPointException。
三、使用Optional
3.1、建立Optional物件
3.1.1、Optional.empty
建立一個空的Optional物件。
3.1.2、Optional.of
建立一個非空的Optional物件,如下:
Optional<Bag> optBag = Optional.of(bag);
如果bag是一個null,這段程式碼會立即丟擲一個NullPointerException,而不是等到訪問car的屬性值時才返回一個錯誤。
3.1.3、Optional.ofNullable
建立一個允許為空的Optional物件,如下:
Optional<Bag> optBag = Optional.of(bag);
如果bag是null,那麼得到的Optional物件就是個空物件。這時如果試圖用optBag .get()方法會報錯,可見Optional的使用是有技巧的。
3.2、Optional的常用方法
3.2.1、get
如果Optional有值則將其返回,否則丟擲NoSuchElementException。
3.2.2、ifPresent
該方法接收一個Consumer(Consumer是一個函式式介面,它有個方法,接受一個引數,不返回值),當Optional物件中存在值則呼叫Consumer,否則不做處理。
Optional<Book> optBook = Optional.of(book);optBook.ifPresent(book -> System.out.println(book.getName()));3.2.3、orElse
如果Optional有值則將其返回,否則返回指定的其它值。
Optional<Book> optBook = Optional.of(book);String bookName = optBook.orElse("Unknown");3.2.4、orElseGet
orElseGet與orElse方法類似,區別在於得到的預設值。orElse方法將傳入的字串作為預設值,orElseGet方法可以接受
Supplier介面的實現用來生成預設值。
Optional<Book> optBook = Optional.of(book);String bookName = optBook.orElseGet(() -> "Unknown");3.2.5、orElseThrow
如果有值則將其返回,否則丟擲supplier介面建立的異常。
try{ empty.orElseThrow(ValueAbsentException::new);}catch (Throwable ex) { //輸出: No value present in the Optional instance System.out.println(ex.getMessage());}3.2.6、map
如果有值,則對其執行呼叫mapping函式得到返回值。如果返回值不為null,則建立包含mapping返回值的Optional作為map方法返回值,否則返回空Optional。
3.2.7、flatMap
如果有值,為其執行mapping函式返回Optional型別返回值,否則返回空Optional。flatMap與map(Funtion)方法類似,區別在於flatMap中的mapping返回值必須是Optional。呼叫結束時,flatMap不會對結果用Optional封裝。
3.2.8、filter
如果有值並且滿足斷言條件返回包含該值的Optional,否則返回空Optional。
3.3、正確使用Optional
3.3.1、不要這樣用
Optional<User> user = ……if (user.isPresent()) { return user.getOrders();} else { return Collections.emptyList();}這和之前沒有區別。