$30 off During Our Annual Pro Sale. View Details »

今こそ、ラムダ式を考える - ラムダ式はどうやって動くのか

今こそ、ラムダ式を考える - ラムダ式はどうやって動くのか

Burikaigi ブリ会議 2024
発表資料

Java, Java SE, Project Lambda, Lambda Expression, ラムダ式
Bytecode, バイトコード, javap, InvokeDynamic, indy

Yuichi.Sakuraba

January 19, 2024
Tweet

More Decks by Yuichi.Sakuraba

Other Decks in Technology

Transcript

  1. ラムダ式 = (無名)関数 ≠ 言語仕様的には Java では、関数は 1st Class Citizen

    ではない 関数型は提供されていない ラムダ式 : 型システムを変更することなく 現行の型システムの範囲内で 関数のようなものを表したもの
  2. ラムダ式 = (無名)関数 ≠ 言語仕様的には 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
  3. 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
  4. 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
  5. ラムダ式の実行 IncLambda.java IncLambda.class コンパイル ラムダ式のメソッドボディを static メソッド化して追加 ラムダ式実行 bootstrap 初回

    関数型インタフェース実装クラスを動的生成 メソッドはコンパイル時に生成した static メソッドをコール 動的生成クラスをインスタンシエーションする CallSite を生成 CallSite からインスタンシエーション実行 2回目以降
  6. > 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 クラスファイル解析ツール
  7. > 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); }
  8. > 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); }
  9. > 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 の引数にする
  10. > 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 } コンパイル時に 生成したメソッド