Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

CH12:Lambda

認識Lambda語法
運用方法參考
瞭解介面預設方法
善用Functional與Stream API
Lambda、平行化與非同步處理

  • Be the first to comment

  • Be the first to like this

CH12:Lambda

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

    Be the first to comment

認識Lambda語法 運用方法參考 瞭解介面預設方法 善用Functional與Stream API Lambda、平行化與非同步處理

Views

Total views

241

On Slideshare

0

From embeds

0

Number of embeds

0

Actions

Downloads

28

Shares

0

Comments

0

Likes

0

×