在程式設計中有時會出現巢狀或瀑布式的流程,就結構來看每一層運算極為類似,只是傳回的型態不同,很難抽取流程重用。舉例來說,如果你的方法可能傳回null,你可能會設計出某個流程如下:
Customer customer = order.getCustomer();
          if(customer != null) {
              String address = customer.getAddress();
              if(address != null) {
                  return address;
              }
          }
          return "n.a.";
巢狀的層次可能還會更深,像是 ...
Customer customer = order.getCustomer();
          if(customer != null) {
              Address address = customer.getAddress();
              if(address != null) {
                  City city = address.getCity();
                  if(city != null) {
                      ....
                  }
              }
          }
          return "n.a.";
就 使用
Optional 取代 null 中的說明,null本身就不建議使用,如果讓getCustomer()傳回Optional<Customer>、讓getAddress()傳回Optional<String>,那一開始的程式片段可以先改為:String addr = "n.a.";
          Optional<Customer> customer = order.getCustomer();
          if(customer.isPresent()) {
              Optional<String> address = customer.get().getAddress();
              if(address.isPresent()) {
                  addr = address.get();
              }
          }
          return addr;看來好像沒有高明到哪去,不過至少每一層都是
Optional型態了,而每一層都是有無的判斷,然後將Optional<T>轉換為Optional<U>,如果將Optional<T>轉換為Optional<U>的方式可以由外部指定,那你就可以重用有無的判斷了,實際上Optional有個flatMap()方法,已經幫你寫好這個邏輯了:    public<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper) {
                Objects.requireNonNull(mapper);
                if (!isPresent())
                    return empty();
                else {
                    return Objects.requireNonNull(mapper.apply(value));
                }
            }所以,你大可以如下直接使用
Optional的flatMap()方法:return order.getCustomer()            .flatMap(Customer::getAddress)            .orElse("n.a.");如果層次不深,也許看不出使用這個的好處,若層次深比較有益處時,像是一開始第二個程式片段,改寫為以下就清楚多了…
return order.getCustomer()
                      .flatMap(Customer::getAddress)
                      .flatMap(Address::getCity)
                      .orElse("n.a.");
        Optional的flatMap()這個名稱令人困惑,可從Optional<T>呼叫flatMap後會得到Optional<U>來想像一下,flatMap()就像是從盒子取出另一盒子置放一旁(flat就是平坦化的意思),過程中依指定之Lambda將前盒的T映射(map)為U再放入後盒,因為判斷是否有值的運算情境被隱藏了,使用者因此可明確指定感興趣的特定運算,從而使程式碼意圖顯露出來,又可接暢地接續運算,以避免巢狀或瀑布式的複雜檢查流程。那麼如果你沒辦法修改程式,讓
getCustomer()、getAddress()、getCity()等傳回Optional型態怎麼辦?Optional是還有個map()方法,例如,若參數order是Order型態,有null的可能性,getCustomer()、getAddress()、getCity()等分別的傳回型態是Customer、Address、City,且有可能傳回null,那麼就可以這麼做:return Optional.ofNullable(order)               .map(Order::getCustomer)               .map(Customer::getAddress)               .map(Address::getCity)               .orElse("n.a.");與
flatMap()的差別在於,map()方法實作中,對mapper.apply(value)的結果使用了Optional.ofNullable()方法(flatMap中使用的是Objects.requireNonNull()),因此有辦法持續處理null的情況:    public<U> Optional<U> map(Function<? super T, ? extends U> mapper) {
                Objects.requireNonNull(mapper);
                if (!isPresent())
                    return empty();
                else {
                    return Optional.ofNullable(mapper.apply(value));
                }
            }如果之前的
Order有個getLineItems()方法,可取得訂單中的產品項目List<LineItem>,想要取得LineItem的名稱,可以透過getName來取得,若你有個List<Order>,想取得所有的產品項目名稱會怎麼寫?直覺的寫法應該是用迴圈…List<String> itemNames = new ArrayList<>();
          for(Order order : orders) {
              for(LineItem lineItem : order.getLineItems()) {
                  itemNames.add(lineItem.getName());
              }
          }
        當然,層次不深時這樣寫很直覺也還好閱讀,不過如果層次深時,例如,想進一步取得
LineItem的贈品名稱的話,你又得多一層for迴圈,如果還要繼續取下去呢?...你可以用
List的stream()方法取得Stream之後,使用flatMap()方法如下改寫:List<String> itemNames = orders.stream()                .flatMap(order -> order.getLineItems().stream())                .map(LineItem::getName)                .collect(toList());就程式碼閱讀來說,
stream()方法會傳回Stream<Order>,把Stream當成是盒子,stream()就是將一群Order物件全部放入盒中,flatMap()指定的Lambda運算是order.getLineItems().stream(),意思就是從盒中那群Order物件逐一取得List<LineItem>,然後再用一個Stream將所有LineItem裝起來,也就是說,Stream<Order>經由flatMap方法後映射為Stream<LineItem>,這類操作一個盒子一個盒子(一個Stream一個Stream)接續下去,例如,想進一步取得LineItem的贈品名稱可以如下:List<String> itemNames = orders.stream()                .flatMap(order -> order.getLineItems().stream())                .flatMap(lineItem -> lineItem.getPremiums().stream())                .map(LineItem::getName)
                          .collect(toList());基本上,如果瞭解
Optinal、Stream(或其他型態)的flatMap()方法,在一層一層盒子剝開後做了哪些運算,撰寫與閱讀程式碼時,忽略掉flatMap這個名稱,就能比較清楚程式碼的主要意圖。

