java8 lambda

103
Java 8 @kojilin 2014/03/29@TWJUG

Upload: koji-lin

Post on 10-Jun-2015

4.081 views

Category:

Technology


9 download

TRANSCRIPT

Page 1: Java8 lambda

Java 8@kojilin

2014/03/29@TWJUG

Page 2: Java8 lambda
Page 3: Java8 lambda

•務必確認 Release Notes

•What’s New

•Known Issue

•Compatibility Guide

Page 4: Java8 lambda

Java SE 8 新功能

•JSR 308 Annotations on Java Types

•JSR 310 Date and Time API

•JSR 335 Lambda Expressions for the Java Programming Language

Page 5: Java8 lambda

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

Page 6: Java8 lambda

JEP

Page 7: Java8 lambda

Lambda

Page 8: Java8 lambda

Why Lambda?•Multicore 時代,撰寫能善加利用此環境的應

用程式

•Java 已經有各種支援多執行緒的 API

•平行和非平行的寫法差異大

•Fork-join 仍不夠

•需要一些語法上的改進,以便設計更好的函式庫

Page 9: Java8 lambda

int highScore = 0;

for (Student student : students) {

if (student.age != 20) {

continue;

}

if (student.score > highScore) {

highScore = student.score;

}

}

Page 10: Java8 lambda

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();

Page 11: Java8 lambda

!

students.stream()

.filter( student -> student.age == 20)

.mapToInt( student -> student.score )

.max();

Page 12: Java8 lambda

Functional Interface•在 Java API 中常見到只有一個需要實作的抽

象方法的 interface

•Runnable, Callable, Comparator ...

Page 13: Java8 lambda

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); } });

Page 14: Java8 lambda

Lambda ExpressionList<String> list = new ArrayList<>();

...

list.stream().sorted((o1, o2) -> {

return o1.compareTo(o2);

});

Page 15: Java8 lambda

list.stream().sorted( new Comparator<String>(){ @Override public void compare(String o1, String o2) { return o1.compareTo(o2); } });

•可以從 sorted 方法知道是 Comparator

Page 16: Java8 lambda

list.stream().sorted( new Comparator<String>(){ @Override public void compare(String o1, String o2) { return o1.compareTo(o2); } });

•因為是 Comparator 所以知道要實作 compare

Page 17: Java8 lambda

list.stream().sorted( new Comparator<String>(){ @Override public void compare(String o1, String o2) { return o1.compareTo(o2); } });

•可以知道 compare 方法的參數是兩個字串

Page 18: Java8 lambda

list.stream().sorted( (o1, o2) -> { return o1.compareTo(o2); });

Page 19: Java8 lambda

•Anonymous functions

•Lambda expression 會轉換成 functional interface 的實體

•因此 lambda 必須放在要能推論的地方

•編譯器會依照 lambda 對應的 functional interface,推斷 parameter type

Page 20: Java8 lambda

new Comparator<String>(){ @Override public void compare(String o1, String o2) { return o1.compareTo(o2); }}

!

(o1, o2) -> {

return o1.compareTo(o2);

}

Page 21: Java8 lambda

Lambda Syntax

(int x, int y) -> x + y

Page 22: Java8 lambda

(int x, int y) -> x + yArgument List

Page 23: Java8 lambda

(int x, int y) -> x + yArrow Token

Page 24: Java8 lambda

(int x, int y) -> x + yBody

Page 25: Java8 lambda

(int x, int y) -> x + y

() -> 42

(String s) -> { System.out.println(s); }

•Body is expression

•Body is statement block

Page 26: Java8 lambda

(String s) -> System.out.println(s)

•Body is a void method invocation

Page 27: Java8 lambda

!

(o1, o2) -> {

return o1.compareTo(o2);

}

!

!

!

(o1, o2) -> o1.compareTo(o2)

Page 28: Java8 lambda

!

(o1, o2) -> {

return o1.compareTo(o2);

}

!

!

!

(o1, o2) -> o1.compareTo(o2)

•lambda 參數名稱不能使用 _ ,被當作保留字

Page 29: Java8 lambda

java.util.function.*•Predicate

•Function

•Supplier

•UnaryOperator

•BinaryOperator

•Optional

•etc.

Page 30: Java8 lambda

Predicateinterface Predicate<T> { boolean test(T t);}age -> age >= 18

!

Arrays.asList(12,25,37) .stream() .filter(age -> age >= 18);

Page 31: Java8 lambda

Functioninterface Function<T, R> { R apply(T t);}

title -> title.length()

!

Arrays.asList("a","b","c") .stream() .map(title -> title.length());

Page 32: Java8 lambda

Supplierinterface Supplier<T> { T get();}

() -> new Random().nextInt()

!

Stream.generate(() -> new Random().nextInt());

Page 33: Java8 lambda

UnaryOperatorinterface UnaryOperator<T> extends Function<T,T>{

}

t -> t * 10

!

Stream.iterate(1, i -> i * 10);

Page 34: Java8 lambda

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));

Page 35: Java8 lambda

BinaryOperatorinterface BinaryOperator<T> extends BiFunction<T,T,T>{

}

i, j -> i + j

!

Arrays.asList(12,25,37) .stream() .reduce(i, j -> i + j);

Page 36: Java8 lambda

自己寫一個interface Hoge {

void hogehoge();

}

Page 37: Java8 lambda

!

!

•怎麼判斷自己有沒有寫對?

•加上 @FunctionalInterface,編譯時幫你檢查是否為 Functional Interface

interface Hoge {

void hogehoge();

}

@FunctionalInterface

interface Hoge {…}

自己寫一個

Page 38: Java8 lambda

•Object 的 public method 不能當唯一的抽象方法

@FunctionalInterface

interface Hoge {

boolean equals(Object o); // compile error

}

Page 39: Java8 lambda

•很多時候 lambda expression 只會呼叫一個方法

(o1, o2) -> o1.compareTo(o2)

!

item -> item.name

!

param -> new Item(param)

Method Reference

Page 40: Java8 lambda

•可以改寫的更加簡潔!

list.stream().sorted((o1, o2) -> o1.compareTo(o2));

!

!

!

list.stream().sorted(String::compareTo);

Page 41: Java8 lambda

•可以改寫的更加簡潔!

list.stream().sorted((o1, o2) -> o1.compareTo(o2));

!

!

!

list.stream().sorted(String::compareTo);

Page 42: Java8 lambda

•Static 方法

•特定物件的 Instance 方法

•特定類型的任意物件的 Instance 方法

•建構子

Method References 的種類

Page 43: Java8 lambda

•Static method

class ItemTool {

static int compareByPrice(Item a, Item b) { ... }

}Arrays.asList(item1,item2,item3) .sort(ItemTool::compareByPrice);

Page 44: Java8 lambda

•特定物件的 Instance method

class ItemTool {

int compareByPrice(Item a, Item b) { ... }

}

ItemTool tool = new ItemTool(); Arrays.asList(item1,item2,item3) .sort(tool::compareByPrice);

Page 45: Java8 lambda

•特定類型的任意物件的 instance method

class Item {

int compareTo(Item b) { ... }

}

Arrays.asList(item1,item2,item3) .sort(Item::compareTo);

Page 46: Java8 lambda

•建構子

<T, R extends Collection<T>> R copy(List<T> from, Supplier<R> toFactory){…}

!

List<String> sourceList = ...;

HashSet<String> result = copy(sourceList, HashSet::new);

Page 47: Java8 lambda

class Item {

public Item() {…}

public Item(String code) {…}

}

!

interface ItemFactory {

Item create(String code);

}

!

ItemFactory factory = Item::new;

Item item = factory.create("a");

Page 48: Java8 lambda

void someWork(ItemFactory factory){

Item item = factory.create("a");

}

someWork(Item::new);

Page 49: Java8 lambda

!

!

ItemFactory factory = Item::new;

!

!

Function<String, Item> factory = Item::new;

Page 50: Java8 lambda

Comparator<Item> comparator = Item::compareByPrice;

!

Comparator<Item> comparator = itemTool::compareByPrice;

!

Comparator<Item> comparator = Item::compareTo;

!

Supplier<String> factory = HashSet::new;

Page 51: Java8 lambda

Lambda

Anonymous Class

不是

Page 52: Java8 lambda

Accessing Local Variables

List<String> list = new ArrayList<>();

...

int max = 10;

list.stream().filter( (s) -> s.length() < max );

Page 53: Java8 lambda

•不會製造新的 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;});

Page 54: Java8 lambda

//Outer$1@78308db1Runnable r1 = new Runnable() {

@Override public void run() {

System.out.println(this);

}

}

!

//Outer@776ec8dfRunnable r2 = () -> System.out.println(this);

Page 55: Java8 lambda

•能存取 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

Page 56: Java8 lambda

•Illegal forward reference

class Foo {

Runnable job = () -> {

// compile error!! System.out.println(x); });

int x = 10;

}

Page 57: Java8 lambda

•Illegal forward reference

class Foo {

Runnable job = new Runnable {

public void run(){ System.out.println(x);

} };

int x = 10;

} ◎

Page 58: Java8 lambda

(s) -> System.out.println(s)1. void forEach(Consumer<T>)2. void onClick(Listener)

Target Typing•編譯器會依照 lambda expression 的所在場

合或環境的目標型態決定該 lambda expression 的型態

Page 59: Java8 lambda

@FunctionalInterface

interface Foo {

void bar(String arg);

}

// Comsumer<String>Arrays.asList("a","b","c") .stream() .foreach(o -> …);

// FooFoo foo = o -> …;

Page 60: Java8 lambda

•所以 lambda expression 必須放在編譯器可以推斷的場合

•Variable declarations

•Assignments

•Return statements

•Array initializers

•Method or constructor arguments

•Lambda expression bodies

•Conditional expressions, ? :

•Cast expressions

Page 61: Java8 lambda

void invoke(Runnable runnable);

<T> void invoke(Callable<T> callable);

// Callable<Integer>invoke(() -> 3);

// Runnableinvoke(() -> System.out.println(3));

// ???invoke(() -> { while(true){ … } });

Page 62: Java8 lambda

void invoke(Runnable runnable);

<T> void invoke(Callable<T> callable);

// Callable<Integer>invoke(() -> 3);

// Runnableinvoke(() -> System.out.println(3));

// ???invoke((Runnable)() -> { while(true){ … } });

Page 63: Java8 lambda

型推論的強化•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();

Page 64: Java8 lambda

•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();

Page 65: Java8 lambda

Default Methods•幫 interface 增加新方法會造成尚未升級的實

作編譯錯誤

•例如幫 List 增加新方法,所有實作都得修改

•連帶使利用該 API 者無法升級

Page 66: Java8 lambda

•Java 8 新增了 Default Methods

•就算新增方法也能保持相容性

•實作在 interface 且會是 public 方法

•當實作類別沒有實作時會使用此預設方法interface Foo{ … default int sum(int a, int b){ return a + b; }}

Page 67: Java8 lambda

class Bar implements Foo{ …

//有沒有實作都沒關係 int sum(int a, int b){ return a * b; }}

Page 68: Java8 lambda

•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!!};

Page 69: Java8 lambda

•預設方法不能是 Object 上的方法

interface Foo{ int bar();

// compile error!! default String toString(){ return …; }}

Page 70: Java8 lambda

•多重繼承?

•父類別和實作的介面有一樣的方法 interface A{ default String bar(){ return …; }}

abstract class B{ String bar(){ return …; }}

class C extends B implements A{

…}new C().bar();

Page 71: Java8 lambda

•多重繼承?

•父類別和實作的介面有一樣的方法 interface A{ default String bar(){ return …; }}

abstract class B{ String bar(){ return …; }}

class C extends B implements A{

…}new C().bar();//類別優先,會使用 B 的 bar

Page 72: Java8 lambda

•多重繼承?

•兩個介面有一樣的方法 interface A{ default String bar(){ return …; }}

interface B{ default String bar(){ return …; }}

// Compile error!!

class C extends B, A{

}

Page 73: Java8 lambda

•多重繼承?

•兩個介面有一樣的方法 interface A{ default String bar(){ return …; }}

interface B{ default String bar(){ return …; }}

// 需要自己再定義

class C extends B, A{

default String bar(){...}}

Page 74: Java8 lambda

Static interface method•在 interface 中定義 static method

interface Foo{ static int sum(int a, int b){ return a + b; }}

Foo.sum(1, 2);

Page 75: Java8 lambda

•不能繼承,只能透過定義的 interface 呼叫

•此點和類別中的 static method 不同

interface Bar extends Foo{}

Bar.sum(1, 2); // compile error!!

!

class Hoge implements Foo{}

Hoge.sum(1, 2); // compile error!!

Page 76: Java8 lambda

Lambda 與 新 API

Page 77: Java8 lambda

!

students.stream()

.filter( student -> student.age == 20)

.mapToInt( student -> student.score )

.max();

Stream

Page 78: Java8 lambda

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

Page 79: Java8 lambda

CompletableFuture.supplyAsync(() -> …)

.thenApplyAsync((t) -> … )

.thenCompose((t) -> …)

...

CompletableFuture

Page 80: Java8 lambda

MapMap<String, List<String>> map = …;

map.computeIfAbsent("key",

o -> new ArrayList<String>())

.add("value");

Page 81: Java8 lambda

Comparator

Comparator<Item> compareByPrice =

Comparator.comparing(item ->

item.price);

Page 82: Java8 lambda

已知問題

Page 83: Java8 lambda

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

Page 84: Java8 lambda

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

Page 85: Java8 lambda

Appendix

Page 86: Java8 lambda

How lambda translated?•Inner class

•MethodHandle

•Dynamic proxies

•...etc.

Page 87: Java8 lambda

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); }}

Page 88: Java8 lambda

// Method Hoge$1."<init>":(I)Vinvokespecial #3// java/util/List.forEach:(Ljava/util/functions/Consumer;)Zinvokeinterface #4

•如果使用 inner class,bytecode 會變成

Page 89: Java8 lambda

Why not ?•一個 lambda expression 要一個類別

•一開始就選死方法,未來如果有新實作方式該怎麼辦 ?

Page 90: Java8 lambda

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

Page 91: Java8 lambda

MethodHandle•把 lambda expression 轉換成方法

•在 bytecode 中把 lambda 轉換成 MethodHandle

Page 92: Java8 lambda

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);

Page 93: Java8 lambda

Why not ?•Stream#filter 的參數變成 MethodHandle

•Overloading 怎麼辦 ?

•型態資訊還是得找地方放

•MH 會有比較好的效能嗎 ?

•未來如果有更好的實作方式該怎辦 ?

Page 94: Java8 lambda

invokedynamic•From JSR-292

•為了 JRuby 等 JVM 上的其他語言而出現

•可以動態決定要呼叫的實體

Page 95: Java8 lambda

invokedynamicClient

Bootstrap Callsite

MethodHandle Target Method

Page 96: Java8 lambda

Client

Bootstrap CallSite

MethodHandle Target Method

private static void printArgs(Object... args) { System.out.println(Arrays.deepToString(args));}

Page 97: Java8 lambda

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)

Page 98: Java8 lambda

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));

Page 99: Java8 lambda

透過 indy 轉換 lambda•將 lambda 轉換成和 functional interface

相同 signature 的方法(依情況爲靜態或非靜態)

item -> item.getPrice() > 10

!

private static boolean lambda$1(Item item){ return item.getPrice() > 10;}

Page 100: Java8 lambda

•產生 indy call site,呼叫時回傳 lambda,也就是 lambda factory

item -> item.getPrice() > 10

!

!

Predicate $p = indy[bootstrap=LambdaMetafactory, staticargs=[Predicate, lambda$1])

stream.filter($p);

Page 101: Java8 lambda

•Bootstrap 選擇轉換策略來建立 lambda factory

•這裡的 Bootstrap 被稱為 lambda metafactory

•因為是提供的類別,所以爲 runtime 的一部份

Page 102: Java8 lambda

轉換策略•Runtime 時產生 inner class

•就像之前編譯器產生的一樣,裡面透過 invokestatic 和 invokevirtual 直接呼叫 lambda 產生的方法

•Runtime 時依照 interface 建立 wrapper class

•建構子傳入MH

•Dynamic proxies or MH proxy

•VM private api, or ...

Page 103: Java8 lambda

透過 indy 的好處•延遲到第一次使用時才產生和存取需要的類別

•因為延遲初始和間接產生,例如無狀態(沒有抓取變數)的 lambda 可以使用同一個實體

•對效能有幫助

•未來還能切換實作方式