CH12:Lambda

Justin Lin
Justin LinTechnology / Community Evangelist at Free lancer
1
Lambda
學習目標
• 認識Lambda語法
• 運用方法參考
• 瞭解介面預設方法
• 善用Functional與Stream API
• Lambda、平行化與非同步處理
2
Lambda語法概覽
• 使用者名稱依長度排序
3
• 使用Lambda表示式
4
方法參考
5
• 依名稱長度排序
• 重用既有方法實作,可避免到處寫下Lambda
運算式
6
• 依字典順序排序名稱呢?
7
• 按照字典順序排序,但忽略大小寫差異
• 方法參考不僅避免了重複撰寫Lambda運算式
,還能讓程式碼更為清楚
8
Lambda表示式與函式介面
• Lambda表示式(Expression)
9
• 目標型態(Target type)
• Lambda表示式本身不代表任何型態的實例
10
• Lambda沒有導入新型態來作為Lambda表示
式的型態
• 基於interface語法定義函式介面,作為表
示式的目標型態
– 要求僅具單一抽象方法
11
• 匿名類別不是不好,只不過有其應用的場合
• 只想關心參數及實作本體
12
• 只關心方法簽署的參數與回傳定義,然而忽
略方法名稱
13
• interface語法做了演進,允許有預設方法
• 新標註@FunctionalInterface被引入
• 編譯錯誤
14
Lambda遇上this與final
15
• Lambda 表示式本體中this,來自Lambda
的周圍環境(Context)
• 顯示兩次"Hello, World!"
16
• 以下在JDK8以後不會有錯:
• Lambda表示式不能修改捕獲的區域變數值
17
方法與建構式參考
• 只要靜態方法的方法簽署中,參數與傳回值
定義相同,也可以使用靜態方法來定義函式
介面實作
18
• 避免到處寫下Lambda表示式,儘量運用既有
的API實作,也可以改善可讀性
19
• 函式介面實作可以參考靜態方法之外,還可
以參考特定物件的實例方法
20
• 函式介面實作也可以參考類別定義的非靜態
方法,函式介面會試圖用第一個參數作為方
法接收者
21
• 建構式參考可重用既有API的類別建構式
22
介面預設方法
• 在JDK8以後,interface定義時可以有預
設實作
23
• 預設方法必須使用default關鍵字修飾,預
設權限為public
• 預設方法不能使用資料成員
24
• 共用相同實作
25
• 從JDK9開始,介面可以定義private方法
26
辨別方法的實作版本
• 實作介面是廣義的多重繼承
• 介面也可以被繼承,而抽象方法或預設方法
都會繼承下來
• 子介面再以抽象方法重新定義父介面已定義
的抽象方法,通常是為了文件化
27
• 父介面有個預設方法,子介面再度宣告與父
介面相同的方法簽署,但沒有寫出default
• 子介面就直接重新定義該方法為抽象方法了
• BiIterable的forEach()方法沒有實作了
28
• 兩個父介面定義相同方法簽署的預設方法,
就會引發衝突
• 解決的方式是明確重新定義
– 假設Part與Canvas介面都定義了default的
draw()方法
29
• 如果類別實作的兩個介面擁有相同的父介面
,其中一個介面重新定義了父介面的預設方
法,而另一個介面沒有
– 實作類別會採用重新定義的版本
• 若子類別繼承了父類別同時又實作某介面,
而父類別的方法與介面中的預設方法具有相
同方法簽署
– 採用父類別的方法定義。
30
• 簡單來說,類別的定義優先於介面的定義,
若有重新定義,就以重新定義為主,必要時
使用介面與super指定預設方法
31
• JDK8除了讓介面可以定義預設方法,也允許
在介面中定義靜態方法
• 從JDK9開始,可以定義為private的靜態
方法
32
Iterable
• forEach()預設方法
33
Iterator
• forEachRemaining()的預設實作
34
Comparator
35
使用Optional取代null
• Java Collection API及JSR166參與者之一Doug
Lea的話:
• 圖靈獎得主、快速排序發明者Tony Hoare:
36
"Null sucks."
"I call it my billion-dollar mistake."
NullPointerException
• null的根本問題在於語意含糊不清
– 可以是「不存在」、「沒有」、「無」或「空」
的概念
• 當開發者想到「嘿!這邊可以沒有東西…」
、「嗯!沒什麼東西可以傳回…」,就不假
思索地傳回null
• 然後使用者就總是忘了檢查null,引發各種
可能的錯誤。
37
• 確認使用null的時機與目的,使用明確語義
• 開發者於方法中傳回null,通常代表著客戶
端必須檢查是否為null,並在null的情況
下使用預設值
38
39
• 如果呼叫getNickName()時忘了檢查傳回
值,執行結果就會直接顯示null
• 若後續的執行流程牽涉到至關重要的結果,
程式快樂地繼續執行下去,錯誤可能到某個
執行環節才會發生,造成不可預期的結果
40
• getNickName()傳回Optional<String>
• 方法若傳回Optional實例,表示該實例可
能包裹也可能不包裹值
41
• 客戶端要意識到必須進行檢查,若不檢查就
直接呼叫Optional的get()方法:
• java.util.NoSuchElementException
,這實現了速錯(Fail fast)的概念
42
• 使用orElse()方法,指定值不存在時的替
代值:
43
• 程式庫無法說改就改,可使用Optional的
ofNullable()來銜接程式庫會傳回null
的方法
44
標準API的函式介面
• Consumer
• Function
• Predicate
• Supplier
45
Consumer函式介面
• 接受一個引數,處理後不傳回值
46
• Iterable的forEach方法:
47
• 對於基本型態有IntConsumer、
LongConsumer、DoubleConsumer
• BiConsumer接受兩個物件作為引數的介面
• 還有ObjIntConsumer、
ObjLongConsumer、
ObjDoubleConsumer
48
Function函式介面
• 接受一個引數,執行後傳回結果
• 就像數學函數y=f(x)
49
• 子介面UnaryOperator,參數與傳回值都
是相同型態
• 基本型態的函式轉換,有IntFunction、
LongFunction、DoubleFunction、
IntToDoubleFunction、
IntToLongFunction…
50
• 接受兩個引數、傳回一個結果
51
Predicate函式介面
• 接受一個引數、傳回boolean值
52
• BiPredicate接受兩個引數,傳回
boolean值
• 基本型態對應的函式介面,則有
IntPredicate、LongPredicate、
DoublePredicate
53
Supplier函式介面
• 不接受任何引數就傳回值
54
使用Stream進行管線操作
55
• 管線(Pipeline)操作風格
• 惰性求值(Lazy evaluation)
– 外部迭代(External iteration)
– 內部迭代(Internal iteration)
56
• 絕大多數Stream不需要呼叫close()方法
• 除了一些I/O操作,例如Files.lines()、
Files.list()與Files.walk()方法
• 建議這類操作可以搭配嘗試關閉資源語法
57
• 管線基本上包括了幾個部份:
– 來源(Source)
– 零或多個中介操作(Intermediate operation)
– 一個最終操作(Terminal operation)
58
59
• 每個中介操作隱藏了細節,隱含了效率改進
的空間,也鼓勵開發者多利用這類風格
– 避免撰寫一些重複流程
– 思考目前的複雜演算中,實際上會是由哪些小任
務完成
60
61
• for迴圈若滲雜了許多小任務,會使for迴圈
中的程式碼艱澀難懂
• Stream只能迭代一次
62
Stream的reduce與collect
• 求得一組員工的男性平均年齡:
63
• 求得一組員工的男性最大年齡:
64
• 類似的流程結構
• 從閱讀程式碼角度來看,無法一眼察覺程式
意圖
65
66
67
68
• 將男性員工收集至另一個List<Employee>
• Collector主要的四個方法:
– supplier()
– accumulator()
– combiner()
– finisher()
69
70
71
72
73
關於flatMap()方法
• 巢狀或瀑布式的流程
74
75
• 將Optional<T>轉換為Optional<U>的方
式可由外部指定
76
77
• flatMap()就像從盒子取出另一盒子(flat
是平坦化的意思)
• Lambda表示式指定了前一盒子的值與下個盒
子間的轉換關係
• 避免巢狀或瀑布式的複雜檢查流程
78
• 若沒辦法修改傳回Optional型態怎麼辦?
79
80
81
Stream相關API
• Arrays的stream()
• Stream、IntStream、DoubleStream等
都有of()靜態方法,也各有generate()與
iterate()靜態方法
• IntStream有range()與rangeClosed()
82
• CharSequence新增chars()與
codePoints()
• java.util.Random類別
83
Optional的API增強
84
85
86
Stream的API增強
87
• 若試著運行底下程式,會從x為4開始迭代:
• 若想設定迭代中止的條件:
88
• takeWhile()與dropWhile()方法
89
Stream與平行化
• 想獲得平行處理能力
• Java在API的設計上,希望你進行平行處理
時,能有明確的語義
90
• 不代表就會平行處理而使得執行必然變快
• 必須思考處理過程是否能夠分而治之而後合
併結果
91
• Stream實例若具有平行處理能力,處理過程
會試著分而治之
92
• 希望最後順序是照著原來Stream來源的順序
• 使用forEachOrdered()這類有序處理時
,可能會失去平行化部份(甚至全部)優勢
• 有些中介操作亦有可能發生類似情況,例如
sorted()方法 93
• API文件基本上會記載終結操作時是否依來
源順序
94
• 在collect()操作時若想有平行效果,必須
符合以下三個條件:
– Stream必須有平行處理能力。
– Collector必須有
Collector.Characteristics.CONCURREN
T特性。
– Stream是無序或者Collector具有
Collector.Characteristics.UNORDERED
特性。
95
• API在處理小任務時,不應該進行干擾
• 會引發ConcurrentModifiedException
96
• 思考目前任務是由哪些小任務組成
97
• 一次只做一件事
98
• 避免寫出以下的程式:
99
• 這樣的程式不僅不易理解,若試圖進行平行
化處理時:
• added10s的順序並不照著numbers的順序
100
• 一次處理一個任務的版本,可以簡單地改為
平行化版本,又沒有順序問題:
101
Arrays與平行化
• 針對超長陣列的平行化操作,Arrays新增:
– parallelPrefix()
– parallelSetAll()
– parallelSort()
102
CompletableFuture
103
• 回呼(Callback)風格
• 回呼地獄(Callback hell)
104
105
• 若第一個CompletableFuture任務完成後
,想繼續以非同步方式處理結果
106
• CompletableFuture也有個
thenCompose()(以及非同步的
thenComposeAsnyc()版本),作用就類
似flatMap()
107
CompletableFuture增強
108
109
110
1 of 110

More Related Content

Similar to CH12:Lambda(7)

Java functional apiJava functional api
Java functional api
javakidxxx290 views
C#版本3~5的新特性C#版本3~5的新特性
C#版本3~5的新特性
國昭 張1.5K views
Dpl in actionDpl in action
Dpl in action
Pu Shiming649 views

More from Justin Lin

Ch12 Spring 起步走Ch12 Spring 起步走
Ch12 Spring 起步走Justin Lin
273 views31 slides
Ch11 簡介 JavaMailCh11 簡介 JavaMail
Ch11 簡介 JavaMailJustin Lin
157 views8 slides
Ch09 整合資料庫Ch09 整合資料庫
Ch09 整合資料庫Justin Lin
233 views92 slides

More from Justin Lin(20)

Ch14 簡介 Spring BootCh14 簡介 Spring Boot
Ch14 簡介 Spring Boot
Justin Lin872 views
Ch13 整合 Spring MVC/SecurityCh13 整合 Spring MVC/Security
Ch13 整合 Spring MVC/Security
Justin Lin280 views
Ch12 Spring 起步走Ch12 Spring 起步走
Ch12 Spring 起步走
Justin Lin273 views
Ch11 簡介 JavaMailCh11 簡介 JavaMail
Ch11 簡介 JavaMail
Justin Lin157 views
Ch10 Web 容器安全管理Ch10 Web 容器安全管理
Ch10 Web 容器安全管理
Justin Lin153 views
Ch09 整合資料庫Ch09 整合資料庫
Ch09 整合資料庫
Justin Lin233 views
Ch08 自訂標籤Ch08 自訂標籤
Ch08 自訂標籤
Justin Lin133 views
Ch07 使用 JSTLCh07 使用 JSTL
Ch07 使用 JSTL
Justin Lin161 views
Ch06 使用 JSPCh06 使用 JSP
Ch06 使用 JSP
Justin Lin250 views
Ch04 會話管理Ch04 會話管理
Ch04 會話管理
Justin Lin238 views
Ch03 請求與回應Ch03 請求與回應
Ch03 請求與回應
Justin Lin236 views
Ch02 撰寫與設定 ServletCh02 撰寫與設定 Servlet
Ch02 撰寫與設定 Servlet
Justin Lin352 views
CH1. 簡介 Web 應用程式CH1. 簡介 Web 應用程式
CH1. 簡介 Web 應用程式
Justin Lin1.2K views
14. 進階主題14. 進階主題
14. 進階主題
Justin Lin404 views
13.並行、平行與非同步13.並行、平行與非同步
13.並行、平行與非同步
Justin Lin237 views
12. 除錯、測試與效能12. 除錯、測試與效能
12. 除錯、測試與效能
Justin Lin153 views
11. 常用內建模組11. 常用內建模組
11. 常用內建模組
Justin Lin149 views
10. 資料永續與交換10. 資料永續與交換
10. 資料永續與交換
Justin Lin156 views
9. 資料結構9. 資料結構
9. 資料結構
Justin Lin292 views

CH12:Lambda