1. 程式人生 > >java 8(三)--用Optional取代null

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;
} public Book getBook() { return book; } public void setBook(Book book) { this.book = book; }}class Book{ private String name; public Book(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) {
this.name = name; }}

現想要查詢某個學生的書包中的書的名字,在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();
} } } return "Unknown"; }

可以看到一堆判斷,程式碼可讀性很差,而且擴充套件性也不好。

再看看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();}

這和之前沒有區別。

3.3.2、這樣用

/存在即返回,無則提供預設值return user.orElse(UNKNOWN_USER); //而不是 return user.isPresent() ? user.get() : null;//存在即返回, 無則由函式來產生return user.orElseGet(() -> fetchAUserFromDatabase()); //使用mapreturn user.map(u -> u.getOrders()).orElse(Collections.emptyList())//上面避免了我們類似 Java 8 之前的做法if(user.isPresent()) { return user.get().getOrders();} else { return Collections.emptyList();}