Share
Embed
今こそ、ラムダ式を考える - ラムダ式はどうやって動くのか
Burikaigi ブリ会議 2024
発表資料
Java, Java SE, Project Lambda, Lambda Expression, ラムダ式
Bytecode, バイトコード, javap, InvokeDynamic, indy
Yuichi.Sakuraba
January 19, 2024
Transcript
21 slides
-
ラムダ式 = (無名)関数 ≠ 言語仕様的には Java では、関数は 1st Class Citizen
ではない 関数型は提供されていない ラムダ式 : 型システムを変更することなく 現行の型システムの範囲内で 関数のようなものを表したもの -
ラムダ式 = (無名)関数 ≠ 言語仕様的には I think getting worked up
over whether Lambdas as expressed in Java SE 8 are "real" closures or not is a pretty unconstructive activity Project Lambda from the Inside. An Interview with Brian Goetz Victor Grazi, InfoQ 2013 -
Function<Integer, Integer> func = new Function<>() { public Integer apply(Integer
x) { return x+1; }}; 0: new #7 // class IncAnon$1 3: dup 4: invokespecial #9 // Method IncAnon$1."<init>":()V 7: astore_1 Function<Integer, Integer> func = x -> x+1; 0: invokedynamic #7,0 // InvokeDynamic #0:apply:()Ljava/util/function/Function; 5: astore_1 -
匿名クラス IncAnon.java コンパイル IncAnon.class IncAnon$1.class (匿名クラス) new コンストラクターコール 実行 IncLambda.java
ラムダ式 IncLambda.class コンパイル invokedynamic ?? 実行 -
InvokeDynamic Java 7で導入されたメソッドコール用バイトコード InvokeVirtual InvokeStatic InvokeInterface InvokeSpecial インスタンスメソッド クラスメソッド インタフェース定義メソッド
その他のメソッド InvokeDynamic コールするメソッドを 実行時に動的に検索 -
InvokeDynamic 導入の背景 2000年代 JVM言語の興隆 JRuby Scala Groovy Jython Rhino (JavaScript)
et al. 動的型付け言語をJVMで実装しにくい var x = ...; var y = ...; add(x, y); int add(int x, int y) {...} String add(String x, String y) {...} どちらをコールするかは 実行時にならないと決まらない InvokeDynamic -
InvokeDynamic の動作 登場人物 bootstrapメソッド CallSiteクラス MethodHandleクラス 探索を行い結果をCallSiteで戻すメソッド MethodHandleを保持するコンテナ 実行するメソッドを示す indy
初回 bootstrap 1.メソッド探索 CallSite MethodHandle 2.生成 ターゲットメソッド 3.実行 2回目以降 1.実行 -
bootstrap メソッド探索 メソッド探索以外の 処理を行っても OK 動的メソッド実行以外の用途で Indy の利用が可能 ラムダ式 定数の初期化
文字列連結 String Template -
ラムダ式の実行 IncLambda.java IncLambda.class コンパイル ラムダ式のメソッドボディを static メソッド化して追加 ラムダ式実行 bootstrap 初回
関数型インタフェース実装クラスを動的生成 メソッドはコンパイル時に生成した static メソッドをコール 動的生成クラスをインスタンシエーションする CallSite を生成 CallSite からインスタンシエーション実行 2回目以降 -
> more IncLambda.java import java.util.function.Function; public class IncLambda { public
static void main(String... args) { Function<Integer, Integer> func = x -> x+1; System.out.println(func.apply(0)); } } > more IncLambda.java import java.util.function.Function; public class IncLambda { public static void main(String... args) { Function<Integer, Integer> func = x -> x+1; System.out.println(func.apply(0)); } } > javac IncLambda.java > > more IncLambda.java import java.util.function.Function; public class IncLambda { public static void main(String... args) { Function<Integer, Integer> func = x -> x+1; System.out.println(func.apply(0)); } } > javac IncLambda.java > javap -private IncLambda Compiled from "IncLambda.java" public class IncLambda { public IncLambda(); public static void main(java.lang.String...); private static java.lang.Integer lambda$main$0(java.lang.Integer); } javap クラスファイル解析ツール -
> more IncLambda.java import java.util.function.Function; public class IncLambda { public
static void main(String... args) { Function<Integer, Integer> func = x -> x+1; System.out.println(func.apply(0)); } } > more IncLambda.java import java.util.function.Function; public class IncLambda { public static void main(String... args) { Function<Integer, Integer> func = x -> x+1; System.out.println(func.apply(0)); } } > javac IncLambda.java > > more IncLambda.java import java.util.function.Function; public class IncLambda { public static void main(String... args) { Function<Integer, Integer> func = x -> x+1; System.out.println(func.apply(0)); } } > javac IncLambda.java > javap -private IncLambda Compiled from "IncLambda.java" public class IncLambda { public IncLambda(); public static void main(java.lang.String...); private static java.lang.Integer lambda$main$0(java.lang.Integer); } -
> javap -c -private IncLambda Compiled from "IncLambda.java" public class
IncLambda { <<লུ>> private static java.lang.Integer lambda$main$0(java.lang.Integer); Code: 0: aload_0 1: invokevirtual #34 // Method java/lang/Integer.intValue:()I 4: iconst_1 5: iadd 6: invokestatic #17 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; 9: areturn } -c バイトコード出力 private static Integer lambda$main$0(Integer x) { int xx = x.intValue(); xx = xx + 1; return Integer.valueOf(xx); } -
> javap -v -private IncLambda Compiled from "IncLambda.java" public class
IncLambda { <<লུ>> BootstrapMethods: 0: #54 REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory: (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String; Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType; Ljava/lang/invoke/MethodHandle; Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; Method arguments: #49 (Ljava/lang/Object;)Ljava/lang/Object; #50 REF_invokeStatic IncLambda.lambda$main$0: (Ljava/lang/Integer;)Ljava/lang/Integer; #53 (Ljava/lang/Integer;)Ljava/lang/Integer; -v 詳細情報出力 > javap -v -private IncLambda Compiled from "IncLambda.java" public class IncLambda { <<লུ>> BootstrapMethods: 0: #54 REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory: (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String; Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType; Ljava/lang/invoke/MethodHandle; Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; Method arguments: #49 (Ljava/lang/Object;)Ljava/lang/Object; #50 REF_invokeStatic IncLambda.lambda$main$0: (Ljava/lang/Integer;)Ljava/lang/Integer; #53 (Ljava/lang/Integer;)Ljava/lang/Integer; ラムダ式用の bootstrap 生成したメソッドを bootstrap の引数にする -
> java -Djdk.invoke.LambdaMetafactory.dumpProxyClassFiles=true IncLambda 1 > 動的生成したクラスを ファイル出力するオプション > java
-Djdk.invoke.LambdaMetafactory.dumpProxyClassFiles=true IncLambda 1 > ls IncLambda.class IncLambda.java DUMP_LAMBDA_PROXY_CLASS_FILES > cd DUMP_LAMBDA_PROXY_CLASS_FILES > ls IncLambda$$Lambda.0x00000165e0000a00.class > > java -Djdk.invoke.LambdaMetafactory.dumpProxyClassFiles=true IncLambda 1 > ls IncLambda.class IncLambda.java DUMP_LAMBDA_PROXY_CLASS_FILES > cd DUMP_LAMBDA_PROXY_CLASS_FILES > ls IncLambda$$Lambda.0x00000165e0000a00.class > javap -c IncLambda$$Lambda.0x00000165e0000a00.class final class IncLambda$$Lambda implements java.util.function.Function { public java.lang.Object apply(java.lang.Object); Code: 0: aload_1 1: checkcast #14 // class java/lang/Integer 4: invokestatic #20 // Method IncLambda.lambda$main$0: (Ljava/lang/Integer;)Ljava/lang/Integer; 7: areturn } コンパイル時に 生成したメソッド