Javaでのラムダ式はinvokedynamicとMethodHandleを使って呼び出されますが、ラムダ式に書いた処理はコンパイラがメソッドに変換します。
僕はあーstaticメソッドになるんだよね、と感覚的にしか理解していなかったのですが、やってみるとそうでないものものあったので、まとめます。
単純にstaticメソッドに変換されるケース
public class Lambda { public static void main(String[] args) { Runnable r = () -> System.out.println("test"); Thread t = new Thread(r); t.start(); } }
ラムダ式で固定文字列を出力しているだけです。この場合、単純にstaticメソッドに変換されます。
$ javap -p -s -c -v Lambda ... private static void lambda$main$0(); descriptor: ()V flags: ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC Code: stack=2, locals=0, args_size=0 0: getstatic #6 // Field java/lang/System.out:Ljava/io/PrintStream; 3: ldc #7 // String test 5: invokevirtual #8 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 8: return LineNumberTable: line 3: 0
ACC_SYNTHETIC
ですので、これはコンパイラによって作成された、合成メソッドです。9年前触れていました。
引数ありのstaticメソッドに変換されるケース
public class Lambda2 { public static void main(String[] args) { String test = "test"; Runnable r = () -> System.out.println(test); Thread t = new Thread(r); t.start(); } }
ラムダ式でローカル変数を使うようにしました。この場合、変数はstaticメソッドの引数となるよう変換されます。
private static void lambda$main$0(java.lang.String); descriptor: (Ljava/lang/String;)V flags: ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC Code: stack=2, locals=1, args_size=1 0: getstatic #7 // Field java/lang/System.out:Ljava/io/PrintStream; 3: aload_0 4: invokevirtual #8 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 7: return LineNumberTable: line 4: 0
インスタンスメソッドに変換されるケース
public class Lambda3 { public static void main(String[] args) { new Lambda3().test(); } void test() { Runnable r = () -> print(); Thread t = new Thread(r); t.start(); } void print() { System.out.println("test"); } }
ラムダ式でインスタンスメソッドを呼び出すようにしました。この場合、staticメソッドではなくインスタンスメソッドに変換されます。
private void lambda$test$0(); descriptor: ()V flags: ACC_PRIVATE, ACC_SYNTHETIC Code: stack=1, locals=1, args_size=1 0: aload_0 1: invokevirtual #12 // Method print:()V 4: return LineNumberTable: line 7: 0
すべて当然といえばそうなのですが、何となくの理解はいけないですね。