Fight the Future

Java言語とJVM、そしてJavaエコシステム全般にまつわること

ラムダ式からコンパイルされたメソッドの3つのパターン

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年前触れていました。

jyukutyo.hatenablog.com

引数ありの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

すべて当然といえばそうなのですが、何となくの理解はいけないですね。