Java8 lambda

4,731 views

Published on

Published in: Technology
0 Comments
24 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
4,731
On SlideShare
0
From Embeds
0
Number of Embeds
11
Actions
Shares
0
Downloads
97
Comments
0
Likes
24
Embeds 0
No embeds

No notes for slide

Java8 lambda

  1. 1. Java 8 @kojilin 2014/03/29@TWJUG
  2. 2. •務必確認 Release Notes •What s New •Known Issue •Compatibility Guide
  3. 3. Java SE 8 新功能 •JSR 308 Annotations on Java Types •JSR 310 Date and Time API •JSR 335 Lambda Expressions for the Java Programming Language
  4. 4. Java SE 8 更新 •JSR 114 JDBC Rowsets •JSR 160 JMX Remote API •JSR 173 Java Compiler API •JSR 199 Streaming API for XML •JSR 221 JDBC 4.0 •JSR 269 Pluggable Annotation-Processing API
  5. 5. JEP
  6. 6. Lambda
  7. 7. Why Lambda? •Multicore 時代, 寫能善加利用此環境的應 用程式 •Java 已經有各種支援多執行緒的 API •平行和非平行的寫法差異大 •Fork-join 仍不夠 •需要一些語法上的改進,以便設計更好的函式 庫
  8. 8. int highScore = 0; for (Student student : students) { if (student.age != 20) { continue; } if (student.score > highScore) { highScore = student.score; } }
  9. 9. students.stream() .filter(new Predicate<Student>() { @Override 
 public boolean test(final Student student) { return student.age == 20; } }) .mapToInt(new ToIntFunction<Student>() { @Override 
 public int applyAsInt(final Student student) { return student.score; } }) .max();
  10. 10. ! students.stream() .filter( student -> student.age == 20) .mapToInt( student -> student.score ) .max();
  11. 11. Functional Interface •在 Java API 中常見到只有一個需要實作的抽 象方法的 interface •Runnable, Callable, Comparator ...
  12. 12. Anonymous Inner Class List<String> list = new ArrayList<>(); ... list.stream().sorted(
 new Comparator<String>(){
 @Override 
 public void compare(String o1, 
 String o2) {
 return o1.compareTo(o2);
 }
 });
  13. 13. Lambda Expression List<String> list = new ArrayList<>(); ... list.stream().sorted((o1, o2) -> { return o1.compareTo(o2); });
  14. 14. list.stream().sorted(
 new Comparator<String>(){
 @Override 
 public void compare(String o1, 
 String o2) {
 return o1.compareTo(o2);
 }
 }); •可以從 sorted 方法知道是 Comparator
  15. 15. list.stream().sorted(
 new Comparator<String>(){
 @Override 
 public void compare(String o1, 
 String o2) {
 return o1.compareTo(o2);
 }
 }); •因為是 Comparator 所以知道要實作 compare
  16. 16. list.stream().sorted(
 new Comparator<String>(){
 @Override 
 public void compare(String o1, 
 String o2) {
 return o1.compareTo(o2);
 }
 }); •可以知道 compare 方法的參數是兩個字串
  17. 17. list.stream().sorted(
 (o1, o2) -> {
 return o1.compareTo(o2);
 });
  18. 18. •Anonymous functions •Lambda expression 會轉換成 functional interface 的實體 •因此 lambda 必須放在要能推論的地方 •編譯器會依照 lambda 對應的 functional interface,推斷 parameter type
  19. 19. new Comparator<String>(){
 @Override 
 public void compare(String o1, 
 String o2) {
 return o1.compareTo(o2);
 }
 } ! (o1, o2) -> { return o1.compareTo(o2); }
  20. 20. Lambda Syntax (int x, int y) -> x + y
  21. 21. (int x, int y) -> x + y Argument List
  22. 22. (int x, int y) -> x + y Arrow Token
  23. 23. (int x, int y) -> x + y Body
  24. 24. (int x, int y) -> x + y () -> 42 (String s) -> { 
 System.out.println(s); 
 } •Body is expression •Body is statement block
  25. 25. (String s) -> 
 System.out.println(s) •Body is a void method invocation
  26. 26. ! (o1, o2) -> { return o1.compareTo(o2); } ! ! ! (o1, o2) -> o1.compareTo(o2)
  27. 27. ! (o1, o2) -> { return o1.compareTo(o2); } ! ! ! (o1, o2) -> o1.compareTo(o2) •lambda 參數名稱不能使用 _ ,被當作保留字
  28. 28. java.util.function.* •Predicate •Function •Supplier •UnaryOperator •BinaryOperator •Optional •etc.
  29. 29. Predicate interface Predicate<T> {
 boolean test(T t);
 }
 
 age -> age >= 18 ! Arrays.asList(12,25,37)
 .stream()
 .filter(age -> age >= 18);
  30. 30. Function interface Function<T, R> {
 R apply(T t);
 } 
 title -> title.length() ! Arrays.asList("a","b","c")
 .stream()
 .map(title -> title.length());
  31. 31. Supplier interface Supplier<T> {
 T get();
 } 
 () -> new Random().nextInt() ! Stream.generate(() -> 
 new Random().nextInt());
  32. 32. UnaryOperator interface UnaryOperator<T> extends 
 Function<T,T>{ } 
 t -> t * 10 ! Stream.iterate(1, i -> i * 10);
  33. 33. BiFunction interface BiFunction<T,U,R>{ R apply(T t, U u); } 
 k, v -> v + "a" ! map.compute(key, 
 (k, v) -> (v == null) ? msg 
 : v.concat(msg));
  34. 34. BinaryOperator interface BinaryOperator<T> extends 
 BiFunction<T,T,T>{ } 
 i, j -> i + j ! Arrays.asList(12,25,37)
 .stream()
 .reduce(i, j -> i + j);
  35. 35. 自己寫一個 interface Hoge { void hogehoge(); }
  36. 36. ! ! •怎麼判斷自己有沒有寫對? •加上 @FunctionalInterface,編譯時幫你 檢查是否為 Functional Interface interface Hoge { void hogehoge(); } @FunctionalInterface interface Hoge {…} 自己寫一個
  37. 37. •Object 的 public method 不能當唯一的抽 象方法 @FunctionalInterface interface Hoge { boolean equals(Object o); // compile error }
  38. 38. •很多時候 lambda expression 只會呼叫一個 方法 (o1, o2) -> o1.compareTo(o2) ! item -> item.name ! param -> new Item(param) Method Reference
  39. 39. •可以改寫的更加簡潔 ! list.stream().sorted((o1, o2) -> 
 o1.compareTo(o2)); ! ! ! list.stream().sorted(String::compareTo);
  40. 40. •可以改寫的更加簡潔 ! list.stream().sorted((o1, o2) -> 
 o1.compareTo(o2)); ! ! ! list.stream().sorted(String::compareTo);
  41. 41. •Static 方法 •特定物件的 Instance 方法 •特定類型的任意物件的 Instance 方法 •建構子 Method References 的種類
  42. 42. •Static method class ItemTool { static int compareByPrice(Item a, 
 Item b) {
 ...
 } }
 
 Arrays.asList(item1,item2,item3)
 .sort(ItemTool::compareByPrice);
  43. 43. •特定物件的 Instance method class ItemTool { int compareByPrice(Item a, 
 Item b) {
 ...
 } } ItemTool tool = new ItemTool();
 Arrays.asList(item1,item2,item3)
 .sort(tool::compareByPrice);
  44. 44. •特定類型的任意物件的 instance method class Item { int compareTo(Item b) {
 ...
 } } 
 Arrays.asList(item1,item2,item3)
 .sort(Item::compareTo);
  45. 45. •建構子 <T, R extends Collection<T>>
 R copy(List<T> from, 
 Supplier<R> toFactory){…} ! List<String> sourceList = ...; HashSet<String> result = 
 copy(sourceList, HashSet::new);

  46. 46. class Item { public Item() {…} public Item(String code) {…} } ! interface ItemFactory { Item create(String code); } ! ItemFactory factory = Item::new; Item item = factory.create("a");
  47. 47. void someWork(ItemFactory factory){ Item item = factory.create("a"); … } 
 someWork(Item::new);
  48. 48. ! ! ItemFactory factory = Item::new; ! ! Function<String, Item> factory = 
 Item::new;
  49. 49. Comparator<Item> comparator = 
 Item::compareByPrice; ! Comparator<Item> comparator = 
 itemTool::compareByPrice; ! Comparator<Item> comparator = 
 Item::compareTo; ! Supplier<String> factory = 
 HashSet::new;
  50. 50. Lambda Anonymous Class 不是
  51. 51. Accessing Local Variables List<String> list = new ArrayList<>(); ... int max = 10; list.stream().filter(
 (s) -> s.length() < max
 );
  52. 52. •不會製造新的 Scope •this 是 enclosing class •沒有 Shadowing 問題 •和 local class 或 anonymous class 不同 int max = 10; list.stream().filter((s) -> {
 int max = 12; // compile error
 return s.length() < this.delta 
 + max;
 });
  53. 53. //Outer$1@78308db1
 Runnable r1 = new Runnable() { @Override public void run() { System.out.println(this); } } ! //Outer@776ec8df
 Runnable r2 = () -> 
 System.out.println(this);
  54. 54. •能存取 final & effectively final 的變數 int max = 10; final int min = 2; list.stream().filter((s) -> {
 max = 12; // compile error
 return s.length() < max 
 && s.length() > min;
 }); max = 15; // compile error
  55. 55. •Illegal forward reference class Foo { Runnable job = () -> { // compile error!!
 System.out.println(x);
 }); int x = 10; } ❌
  56. 56. •Illegal forward reference class Foo { Runnable job = new Runnable { public void run(){
 System.out.println(x); }
 }; int x = 10; } ◎
  57. 57. (s) -> System.out.println(s)
 
 1. void forEach(Consumer<T>)
 
 2. void onClick(Listener) Target Typing •編譯器會依照 lambda expression 的所在場 合或環境的目標型態決定該 lambda expression 的型態
  58. 58. @FunctionalInterface interface Foo { void bar(String arg); } // Comsumer<String>
 Arrays.asList("a","b","c")
 .stream()
 .foreach(o -> …); // Foo
 Foo foo = o -> …;
  59. 59. •所以 lambda expression 必須放在編譯器可 以推斷的場合 •Variable declarations •Assignments •Return statements •Array initializers •Method or constructor arguments •Lambda expression bodies •Conditional expressions, ? : •Cast expressions
  60. 60. void invoke(Runnable runnable); <T> void invoke(Callable<T> callable);
 // Callable<Integer>
 invoke(() -> 3); // Runnable
 invoke(() -> System.out.println(3)); // ???
 invoke(() -> {
 while(true){ … } 
 });
  61. 61. void invoke(Runnable runnable); <T> void invoke(Callable<T> callable);
 // Callable<Integer>
 invoke(() -> 3); // Runnable
 invoke(() -> System.out.println(3)); // ???
 invoke((Runnable)() -> {
 while(true){ … } 
 });
  62. 62. 型推論的強化 •Java 7 的 <> ◎ List<Integer> l1 = new ArrayList<>(); ❌ List<Number> l2 = new ArrayList<>(1, 
 2); ❌ calculate(new ArrayList<>()); ❌ List<String> result = a == 10 
 ? new ArrayList<>()
 : Collections.emptyList();
  63. 63. •Java 8 的 <> ◎ List<Integer> ages = new ArrayList<>(); ◎ List<Number> l2 = new ArrayList<>(1, 
 2); ◎ calculate(new ArrayList<>()); ◎ List<String> result = a == 10 
 ? new ArrayList<>()
 : Collections.emptyList();
  64. 64. Default Methods •幫 interface 增加新方法會造成尚未升級的實 作編譯錯誤 •例如幫 List 增加新方法,所有實作都得修 改 •連帶使利用該 API 者無法升級
  65. 65. •Java 8 新增了 Default Methods •就算新增方法也能保持相容性 •實作在 interface 且會是 public 方法 •當實作類別沒有實作時會使用此預設方法 interface Foo{
 …
 default int sum(int a, int b){
 return a + b;
 
 }
 }
  66. 66. class Bar implements Foo{
 … //有沒有實作都沒關係
 int sum(int a, int b){
 return a * b;
 }
 }
  67. 67. •lambda expression 不能呼叫預設方法 @FunctionalInterface
 interface Foo{
 int bar();
 default int sum(int a, int b){
 return a + b;
 }
 }
 
 Foo foo = () -> {
 return 3 + sum(1, 2); // compile error!!
 };
  68. 68. •預設方法不能是 Object 上的方法 interface Foo{
 
 int bar();
 // compile error!!
 default String toString(){
 return …;
 }
 }
  69. 69. •多重繼承? •父類別和實作的介面有一樣的方法 interface A{
 
 default String bar(){
 return …;
 }
 } abstract class B{
 
 String bar(){
 return …;
 }
 } class C extends B implements A{ …
 }
 
 new C().bar();
  70. 70. •多重繼承? •父類別和實作的介面有一樣的方法 interface A{
 
 default String bar(){
 return …;
 }
 } abstract class B{
 
 String bar(){
 return …;
 }
 } class C extends B implements A{ …
 }
 
 new C().bar();//類別優先,會使用 B 的 bar
  71. 71. •多重繼承? •兩個介面有一樣的方法 interface A{
 
 default String bar(){
 return …;
 }
 } interface B{
 
 default String bar(){
 return …;
 }
 } // Compile error!! class C extends B, A{ 
 }
  72. 72. •多重繼承? •兩個介面有一樣的方法 interface A{
 
 default String bar(){
 return …;
 }
 } interface B{
 
 default String bar(){
 return …;
 }
 } // 需要自己再定義
 class C extends B, A{ default String bar(){...}
 }
  73. 73. Static interface method •在 interface 中定義 static method interface Foo{
 static int sum(int a, int b){
 return a + b;
 }
 } Foo.sum(1, 2);
  74. 74. •不能繼承,只能透過定義的 interface 呼叫 •此點和類別中的 static method 不同 interface Bar extends Foo{
 } Bar.sum(1, 2); // compile error!! ! class Hoge implements Foo{
 } Hoge.sum(1, 2); // compile error!!

  75. 75. Lambda 與 新 API
  76. 76. ! students.stream() .filter( student -> student.age == 20) .mapToInt( student -> student.score ) .max(); Stream
  77. 77. Optional<Bitmap> filter(Bitmap bitmap){…} Optional<Bitmap> scale(Bitmap bitmap){…} ! Optional<Bitmap> maybeBitmap = readFromFile(…); maybeBitmap.flatMap(bitmap -> filter(bitmap)) .flatMap(bitmap -> scale(bitmap)) .ifPresent(bitmap -> view.setImage(bitmap)); Optional
  78. 78. CompletableFuture.supplyAsync(() -> …) .thenApplyAsync((t) -> … ) .thenCompose((t) -> …) ... CompletableFuture
  79. 79. Map Map<String, List<String>> map = …; map.computeIfAbsent("key", o -> new ArrayList<String>()) .add("value");
  80. 80. Comparator Comparator<Item> compareByPrice = Comparator.comparing(item -> item.price);
  81. 81. 已知問題
  82. 82. public static void main(String[] args) { Runnable r = () -> { try { Object o = null; o.getClass(); throw new IOException(); } catch(IOException | IllegalArgumentException e) { System.out.println("KO !"); } catch(RuntimeException e) { System.out.println("OK !"); } }; r.run(); } http://mail.openjdk.java.net/pipermail/lambda-dev/2014-March/011940.html
  83. 83. Function<Integer, Integer> s1 = new 
 Function<Integer, Integer>() { @Override public Integer apply(Integer 
 t) { return t++; } };
 Function<Integer, Integer> s2 = (t) -> t++; ! System.out.println(s1.apply(2)); // 2 System.out.println(s2.apply(2)); // 3 https://bugs.openjdk.java.net/browse/JDK-8038420
  84. 84. Appendix
  85. 85. How lambda translated? •Inner class •MethodHandle •Dynamic proxies •...etc.
  86. 86. Lambda is Inner Class ? •看來只要用 inner class 就可以實作了? class Hoge$1 implements Consumer<String>{
 private final Logger logger;
 Hoge$1(Logger logger){
 this.logger = logger;
 }
 @Override
 public void accept(String t){
 logger.log(t);
 }
 }
  87. 87. // Method Hoge$1."<init>":(I)V
 invokespecial #3
 
 // java/util/List.forEach:(Ljava/ util/functions/Consumer;)Z
 invokeinterface #4 •如果使用 inner class,bytecode 會變成
  88. 88. Why not ? •一個 lambda expression 要一個類別 •一開始就選死方法,未來如果有新實作方式該 怎麼辦 ?
  89. 89. MethodHandle •與 invokedynamic 一起從 JSR-292
 (Java 7 )新增 •java.util.invoke •A reference to method(or field, constructor or other bit of functionality) that can be executed
  90. 90. MethodHandle •把 lambda expression 轉換成方法 •在 bytecode 中把 lambda 轉換成 MethodHandle
  91. 91. stream.filter(item -> 
 item.getPrice() > 10); ! ! private static boolean lambda$1(Item item)
 {
 return item.getPrice() > 10;
 } ! MethodHandle mh = LDC[lambda$1]; stream.filter(mh);
  92. 92. Why not ? •Stream#filter 的參數變成 MethodHandle •Overloading 怎麼辦 ? •型態資 還是得找地方放 •MH 會有比較好的效能 ? •未來如果有更好的實作方式該怎辦 ?
  93. 93. invokedynamic •From JSR-292 •為了 JRuby 等 JVM 上的其他語言而出現 •可以動態決定要呼叫的實體
  94. 94. invokedynamic Client Bootstrap Callsite MethodHandle Target Method
  95. 95. Client Bootstrap CallSite MethodHandle Target Method private static void printArgs(Object... args) {
 System.out.println(Arrays.deepToString(args));
 }
  96. 96. Client Bootstrap CallSite MethodHandle Target Method InvokeDynamic[#bootstrapDynamic].baz("baz arg", 2, 3.14); private static CallSite bootstrapDynamic( 
 MethodHandles.Lookup caller, 
 String name, 
 MethodType type)
  97. 97. Client Bootstrap CallSite MethodHandle Target Method MethodHandles.Lookup lookup = MethodHandles.lookup(); return new ConstantCallSite(
 lookup.findStatic(Hoge.class, "printArgs", 
 MethodType
 .methodType(void.class, Object[].class)
 .asType(type));
  98. 98. 透過 indy 轉換 lambda •將 lambda 轉換成和 functional interface 相同 signature 的方法(依情況爲靜態或非靜 態)
 
 
 
 item -> item.getPrice() > 10 ! 
 private static boolean lambda$1(Item item) {
 return item.getPrice() > 10;
 }
  99. 99. •產生 indy call site,呼叫時回傳 lambda, 也就是 lambda factory
 
 
 
 item -> item.getPrice() > 10 ! ! Predicate $p = 
 indy[bootstrap=LambdaMetafactory, 
 staticargs=[Predicate, lambda$1]) stream.filter($p);
  100. 100. •Bootstrap 選擇轉換策略來建立 lambda factory • 裡的 Bootstrap 被稱為 lambda metafactory •因為是提供的類別,所以爲 runtime 的一 部份
  101. 101. 轉換策略 •Runtime 時產生 inner class •就像之前編譯器產生的一樣,裡面透過 invokestatic 和 invokevirtual 直接呼叫 lambda 產生的方法 •Runtime 時依照 interface 建立 wrapper class •建構子傳入MH •Dynamic proxies or MH proxy •VM private api, or ...
  102. 102. 透過 indy 的好處 •延遲到第一次使用時才產生和存取需要的類別 •因為延遲初始和間接產生,例如無狀態(沒有 抓取變數)的 lambda 可以使用同一個實體 •對效能有幫助 •未來還能切換實作方式

×