Anonymous classのコンパイル

外側のローカル変数にどうやってアクセスしているのか

コンストラクタ経由で、変数を渡してアクセスしている。(なるほどfinalでなくてはいけないわけだ)

public class Foo {
  public void Test() {
    final String x = "Hello World";
    Bar y = new Bar() {
      public void func() {
        System.out.println(y);
      };
    y.func();
    return;
  }
}

は、

public class Foo$1 implements Bar {
  private final String x;
  public Foo$1(String x1) {
    x = x1;
  }
  
  public void func() {
    System.out.println(x);
  }
}

public class Foo {
  public void Test() {
    final String x = "Hello World";
    Bar y = new Foo$1(x);
    y.func();
    return;
  }
}

みたいな感じにコンパイルされる。*1

外側のクラスのメンバにどうやってアクセスしているのか

コンストラクタで外側のクラスのインスタンスを渡し、フィールドに格納しておく。また、外側のクラスの変数に対応するStaticなアクセッサを適当に定義しておく。変数へのアクセスは、コンストラクタに渡されたインスタンスと、スタティックなアクセッサを利用して行う。

class Foo {
  private int value;
  public Test() {
    Bar x = new Bar() {
      public void func() {
        value = 2;
      }
    };
    x.func();
  }
}

は、

class Foo$1 implements Bar {
  private Foo this1;
  
  public Foo$1(Foo this0) {
    this1 = this0;
  }

  public void func() {
    Foo.setvalue(this1, 2);
  }
}

class Foo {
  private int value;

  public void Test() {
    Bar x = new Foo$1(this);
    x.func();
  }
  
  public static void setvalue(Foo inst, int i) {
    inst.value = i;
  }

  public static int getvalue(Foo inst) {
    return inst.value;
  }
}

みたいな感じ。


賢いなあ。

*1:実際には、Stringはimmutableなので、なんか最適化されてたけど(わかりにくい)