Download - Java8 lambda
Java 8@kojilin
2014/03/29@TWJUG
•務必確認 Release Notes
•What’s New
•Known Issue
•Compatibility Guide
Java SE 8 新功能
•JSR 308 Annotations on Java Types
•JSR 310 Date and Time API
•JSR 335 Lambda Expressions for the Java Programming Language
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
JEP
Lambda
Why Lambda?•Multicore 時代,撰寫能善加利用此環境的應
用程式
•Java 已經有各種支援多執行緒的 API
•平行和非平行的寫法差異大
•Fork-join 仍不夠
•需要一些語法上的改進,以便設計更好的函式庫
int highScore = 0;
for (Student student : students) {
if (student.age != 20) {
continue;
}
if (student.score > highScore) {
highScore = student.score;
}
}
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();
!
students.stream()
.filter( student -> student.age == 20)
.mapToInt( student -> student.score )
.max();
Functional Interface•在 Java API 中常見到只有一個需要實作的抽
象方法的 interface
•Runnable, Callable, Comparator ...
Anonymous Inner ClassList<String> list = new ArrayList<>();
...
list.stream().sorted( new Comparator<String>(){ @Override public void compare(String o1, String o2) { return o1.compareTo(o2); } });
Lambda ExpressionList<String> list = new ArrayList<>();
...
list.stream().sorted((o1, o2) -> {
return o1.compareTo(o2);
});
list.stream().sorted( new Comparator<String>(){ @Override public void compare(String o1, String o2) { return o1.compareTo(o2); } });
•可以從 sorted 方法知道是 Comparator
list.stream().sorted( new Comparator<String>(){ @Override public void compare(String o1, String o2) { return o1.compareTo(o2); } });
•因為是 Comparator 所以知道要實作 compare
list.stream().sorted( new Comparator<String>(){ @Override public void compare(String o1, String o2) { return o1.compareTo(o2); } });
•可以知道 compare 方法的參數是兩個字串
list.stream().sorted( (o1, o2) -> { return o1.compareTo(o2); });
•Anonymous functions
•Lambda expression 會轉換成 functional interface 的實體
•因此 lambda 必須放在要能推論的地方
•編譯器會依照 lambda 對應的 functional interface,推斷 parameter type
new Comparator<String>(){ @Override public void compare(String o1, String o2) { return o1.compareTo(o2); }}
!
(o1, o2) -> {
return o1.compareTo(o2);
}
Lambda Syntax
(int x, int y) -> x + y
(int x, int y) -> x + yArgument List
(int x, int y) -> x + yArrow Token
(int x, int y) -> x + yBody
(int x, int y) -> x + y
() -> 42
(String s) -> { System.out.println(s); }
•Body is expression
•Body is statement block
(String s) -> System.out.println(s)
•Body is a void method invocation
!
(o1, o2) -> {
return o1.compareTo(o2);
}
!
!
!
(o1, o2) -> o1.compareTo(o2)
!
(o1, o2) -> {
return o1.compareTo(o2);
}
!
!
!
(o1, o2) -> o1.compareTo(o2)
•lambda 參數名稱不能使用 _ ,被當作保留字
java.util.function.*•Predicate
•Function
•Supplier
•UnaryOperator
•BinaryOperator
•Optional
•etc.
Predicateinterface Predicate<T> { boolean test(T t);}age -> age >= 18
!
Arrays.asList(12,25,37) .stream() .filter(age -> age >= 18);
Functioninterface Function<T, R> { R apply(T t);}
title -> title.length()
!
Arrays.asList("a","b","c") .stream() .map(title -> title.length());
Supplierinterface Supplier<T> { T get();}
() -> new Random().nextInt()
!
Stream.generate(() -> new Random().nextInt());
UnaryOperatorinterface UnaryOperator<T> extends Function<T,T>{
}
t -> t * 10
!
Stream.iterate(1, i -> i * 10);
BiFunctioninterface 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));
BinaryOperatorinterface BinaryOperator<T> extends BiFunction<T,T,T>{
}
i, j -> i + j
!
Arrays.asList(12,25,37) .stream() .reduce(i, j -> i + j);
自己寫一個interface Hoge {
void hogehoge();
}
!
!
•怎麼判斷自己有沒有寫對?
•加上 @FunctionalInterface,編譯時幫你檢查是否為 Functional Interface
interface Hoge {
void hogehoge();
}
@FunctionalInterface
interface Hoge {…}
自己寫一個
•Object 的 public method 不能當唯一的抽象方法
@FunctionalInterface
interface Hoge {
boolean equals(Object o); // compile error
}
•很多時候 lambda expression 只會呼叫一個方法
(o1, o2) -> o1.compareTo(o2)
!
item -> item.name
!
param -> new Item(param)
Method Reference
•可以改寫的更加簡潔!
list.stream().sorted((o1, o2) -> o1.compareTo(o2));
!
!
!
list.stream().sorted(String::compareTo);
•可以改寫的更加簡潔!
list.stream().sorted((o1, o2) -> o1.compareTo(o2));
!
!
!
list.stream().sorted(String::compareTo);
•Static 方法
•特定物件的 Instance 方法
•特定類型的任意物件的 Instance 方法
•建構子
Method References 的種類
•Static method
class ItemTool {
static int compareByPrice(Item a, Item b) { ... }
}Arrays.asList(item1,item2,item3) .sort(ItemTool::compareByPrice);
•特定物件的 Instance method
class ItemTool {
int compareByPrice(Item a, Item b) { ... }
}
ItemTool tool = new ItemTool(); Arrays.asList(item1,item2,item3) .sort(tool::compareByPrice);
•特定類型的任意物件的 instance method
class Item {
int compareTo(Item b) { ... }
}
Arrays.asList(item1,item2,item3) .sort(Item::compareTo);
•建構子
<T, R extends Collection<T>> R copy(List<T> from, Supplier<R> toFactory){…}
!
List<String> sourceList = ...;
HashSet<String> result = copy(sourceList, HashSet::new);
class Item {
public Item() {…}
public Item(String code) {…}
}
!
interface ItemFactory {
Item create(String code);
}
!
ItemFactory factory = Item::new;
Item item = factory.create("a");
void someWork(ItemFactory factory){
Item item = factory.create("a");
…
}
someWork(Item::new);
!
!
ItemFactory factory = Item::new;
!
!
Function<String, Item> factory = Item::new;
Comparator<Item> comparator = Item::compareByPrice;
!
Comparator<Item> comparator = itemTool::compareByPrice;
!
Comparator<Item> comparator = Item::compareTo;
!
Supplier<String> factory = HashSet::new;
Lambda
Anonymous Class
不是
Accessing Local Variables
List<String> list = new ArrayList<>();
...
int max = 10;
list.stream().filter( (s) -> s.length() < max );
•不會製造新的 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;});
//Outer$1@78308db1Runnable r1 = new Runnable() {
@Override public void run() {
System.out.println(this);
}
}
!
//Outer@776ec8dfRunnable r2 = () -> System.out.println(this);
•能存取 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
•Illegal forward reference
class Foo {
Runnable job = () -> {
// compile error!! System.out.println(x); });
int x = 10;
}
❌
•Illegal forward reference
class Foo {
Runnable job = new Runnable {
public void run(){ System.out.println(x);
} };
int x = 10;
} ◎
(s) -> System.out.println(s)1. void forEach(Consumer<T>)2. void onClick(Listener)
Target Typing•編譯器會依照 lambda expression 的所在場
合或環境的目標型態決定該 lambda expression 的型態
@FunctionalInterface
interface Foo {
void bar(String arg);
}
// Comsumer<String>Arrays.asList("a","b","c") .stream() .foreach(o -> …);
// FooFoo foo = o -> …;
•所以 lambda expression 必須放在編譯器可以推斷的場合
•Variable declarations
•Assignments
•Return statements
•Array initializers
•Method or constructor arguments
•Lambda expression bodies
•Conditional expressions, ? :
•Cast expressions
void invoke(Runnable runnable);
<T> void invoke(Callable<T> callable);
// Callable<Integer>invoke(() -> 3);
// Runnableinvoke(() -> System.out.println(3));
// ???invoke(() -> { while(true){ … } });
void invoke(Runnable runnable);
<T> void invoke(Callable<T> callable);
// Callable<Integer>invoke(() -> 3);
// Runnableinvoke(() -> System.out.println(3));
// ???invoke((Runnable)() -> { while(true){ … } });
型推論的強化•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();
•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();
Default Methods•幫 interface 增加新方法會造成尚未升級的實
作編譯錯誤
•例如幫 List 增加新方法,所有實作都得修改
•連帶使利用該 API 者無法升級
•Java 8 新增了 Default Methods
•就算新增方法也能保持相容性
•實作在 interface 且會是 public 方法
•當實作類別沒有實作時會使用此預設方法interface Foo{ … default int sum(int a, int b){ return a + b; }}
class Bar implements Foo{ …
//有沒有實作都沒關係 int sum(int a, int b){ return a * b; }}
•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!!};
•預設方法不能是 Object 上的方法
interface Foo{ int bar();
// compile error!! default String toString(){ return …; }}
•多重繼承?
•父類別和實作的介面有一樣的方法 interface A{ default String bar(){ return …; }}
abstract class B{ String bar(){ return …; }}
class C extends B implements A{
…}new C().bar();
•多重繼承?
•父類別和實作的介面有一樣的方法 interface A{ default String bar(){ return …; }}
abstract class B{ String bar(){ return …; }}
class C extends B implements A{
…}new C().bar();//類別優先,會使用 B 的 bar
•多重繼承?
•兩個介面有一樣的方法 interface A{ default String bar(){ return …; }}
interface B{ default String bar(){ return …; }}
// Compile error!!
class C extends B, A{
}
•多重繼承?
•兩個介面有一樣的方法 interface A{ default String bar(){ return …; }}
interface B{ default String bar(){ return …; }}
// 需要自己再定義
class C extends B, A{
default String bar(){...}}
Static interface method•在 interface 中定義 static method
interface Foo{ static int sum(int a, int b){ return a + b; }}
Foo.sum(1, 2);
•不能繼承,只能透過定義的 interface 呼叫
•此點和類別中的 static method 不同
interface Bar extends Foo{}
Bar.sum(1, 2); // compile error!!
!
class Hoge implements Foo{}
Hoge.sum(1, 2); // compile error!!
Lambda 與 新 API
!
students.stream()
.filter( student -> student.age == 20)
.mapToInt( student -> student.score )
.max();
Stream
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
CompletableFuture.supplyAsync(() -> …)
.thenApplyAsync((t) -> … )
.thenCompose((t) -> …)
...
CompletableFuture
MapMap<String, List<String>> map = …;
map.computeIfAbsent("key",
o -> new ArrayList<String>())
.add("value");
Comparator
Comparator<Item> compareByPrice =
Comparator.comparing(item ->
item.price);
已知問題
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
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
Appendix
How lambda translated?•Inner class
•MethodHandle
•Dynamic proxies
•...etc.
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); }}
// Method Hoge$1."<init>":(I)Vinvokespecial #3// java/util/List.forEach:(Ljava/util/functions/Consumer;)Zinvokeinterface #4
•如果使用 inner class,bytecode 會變成
Why not ?•一個 lambda expression 要一個類別
•一開始就選死方法,未來如果有新實作方式該怎麼辦 ?
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
MethodHandle•把 lambda expression 轉換成方法
•在 bytecode 中把 lambda 轉換成 MethodHandle
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);
Why not ?•Stream#filter 的參數變成 MethodHandle
•Overloading 怎麼辦 ?
•型態資訊還是得找地方放
•MH 會有比較好的效能嗎 ?
•未來如果有更好的實作方式該怎辦 ?
invokedynamic•From JSR-292
•為了 JRuby 等 JVM 上的其他語言而出現
•可以動態決定要呼叫的實體
invokedynamicClient
Bootstrap Callsite
MethodHandle Target Method
Client
Bootstrap CallSite
MethodHandle Target Method
private static void printArgs(Object... args) { System.out.println(Arrays.deepToString(args));}
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)
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));
透過 indy 轉換 lambda•將 lambda 轉換成和 functional interface
相同 signature 的方法(依情況爲靜態或非靜態)
item -> item.getPrice() > 10
!
private static boolean lambda$1(Item item){ return item.getPrice() > 10;}
•產生 indy call site,呼叫時回傳 lambda,也就是 lambda factory
item -> item.getPrice() > 10
!
!
Predicate $p = indy[bootstrap=LambdaMetafactory, staticargs=[Predicate, lambda$1])
stream.filter($p);
•Bootstrap 選擇轉換策略來建立 lambda factory
•這裡的 Bootstrap 被稱為 lambda metafactory
•因為是提供的類別,所以爲 runtime 的一部份
轉換策略•Runtime 時產生 inner class
•就像之前編譯器產生的一樣,裡面透過 invokestatic 和 invokevirtual 直接呼叫 lambda 產生的方法
•Runtime 時依照 interface 建立 wrapper class
•建構子傳入MH
•Dynamic proxies or MH proxy
•VM private api, or ...
透過 indy 的好處•延遲到第一次使用時才產生和存取需要的類別
•因為延遲初始和間接產生,例如無狀態(沒有抓取變數)的 lambda 可以使用同一個實體
•對效能有幫助
•未來還能切換實作方式