JAva 8のlambdaのthisはなぜlamdbaが存在する外部クラスを指しているのか

3523 ワード

ここ数日lambdaを復習して、細部があることに気づいて、とても理解しにくいです.それはlambdaの中のthisポインタです.
Lambdaのthisポインタはその属する内部クラスを指していますが、どのように実現されていますか?
テストとして例を書きました.
import java.util.function.Supplier;

public class LambdaTest {
    public static void main(String[] args) {
        new LambdaTest().test();
    }

    public void test() {
        String para = "abc";
        String para2 = "abc";
        System.out.println(this);
        Supplier supplier = () -> {
            return para + para2;
        };
        System.out.println(supplier.get());
    }
}

対応するjvmマシンコードは:
  private static java.lang.String lambda$test$0(java.lang.String, java.lang.String);
    descriptor: (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
    flags: ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
    Code:
      stack=2, locals=2, args_size=2
         0: new           #12                 // class java/lang/StringBuilder
         3: dup
         4: invokespecial #13                 // Method java/lang/StringBuilder."":()V
         7: aload_0
         8: invokevirtual #14                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        11: aload_1
        12: invokevirtual #14                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        15: invokevirtual #15                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
        18: areturn
      LineNumberTable:
        line 16: 0

メソッド署名により,クラスがthisを持たない場合,静的内部クラスメソッドにコンパイルされることが分かった.
this:
import java.util.function.Supplier;

public class LambdaTest {
    public static void main(String[] args) {
        new LambdaTest().test();
    }

    public void test() {
        String para = "abc";
        String para2 = "abc";
        System.out.println(this);
        Supplier supplier = () -> {
            System.out.println(this);
            return para + para2;
        };
        System.out.println(supplier.get());
    }
}

対応するjvmマシンコード:
  private java.lang.String lambda$test$0(java.lang.String, java.lang.String);
    descriptor: (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
    flags: ACC_PRIVATE, ACC_SYNTHETIC
    Code:
      stack=2, locals=3, args_size=3
         0: getstatic     #6                  // Field java/lang/System.out:Ljava/io/PrintStream;
         3: aload_0
         4: invokevirtual #7                  // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
         7: new           #12                 // class java/lang/StringBuilder
        10: dup
        11: invokespecial #13                 // Method java/lang/StringBuilder."":()V
        14: aload_1
        15: invokevirtual #14                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        18: aload_2
        19: invokevirtual #14                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        22: invokevirtual #15                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
        25: areturn
      LineNumberTable:
        line 15: 0
        line 16: 7

Lambdaは内部クラスにコンパイルされました!これで話が通じる.
結論:
Lambdaは一般的に静的匿名メソッドにコンパイルされ,参照される外部変数はパラメータで伝達される.lambdaでthis指標が使用されている場合、thisポインタがlambda外部クラスを指すように匿名の内部メソッドにコンパイルされます.