Ce diaporama a bien été signalé.
Nous utilisons votre profil LinkedIn et vos données d’activité pour vous proposer des publicités personnalisées et pertinentes. Vous pouvez changer vos préférences de publicités à tout moment.

CH12:Lambda

189 vues

Publié le

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

Publié dans : Technologie
  • Soyez le premier à commenter

  • Soyez le premier à aimer ceci

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

×