3

更新:这或多或少是一个骗局,结果证明是编译器魔术添加了一个构造函数来传递 build2 中的局部变量。

给定这样的接口:

public interface IFoo { public int get(); }

下面的代码会打印 1、1、2,然后在尝试对 build2 返回的值调用 getClass().newInstance() 时抛出异常,但在对 build1 的返回值调用相同时不会。任何想法为什么?

public class Foo {

 public static IFoo build1() {
  return new IFoo() { public int get() { return 1; } };
 }

 public static IFoo build2(final int v) {
  return new IFoo() { public int get() {return v;} };
 }

 public static void main(String[] args) throws Exception {
  IFoo foo, bar;

  foo = build1();
  System.out.println(foo.get());

  bar = foo.getClass().newInstance();  
  System.out.println(bar.get());

  foo = build2(2);
  System.out.println(foo.get());

  bar = foo.getClass().newInstance();  
  System.out.println(bar.get());
 }
}

我的调试器表明在 newInstance() 调用中,getConstructor0 抛出了 NoSuchMethodException。

4

1 回答 1

4

这是发生的事情:

  • newInstance()需要一个空构造函数
  • 当您创建访问final变量的匿名类时,实际上会隐式创建一个字段来保存该值,该值最初传递给其隐式构造函数
  • 因此,IFoocreated inbuild2实际上没有空构造函数

这是一个片段来显示发生了什么:

import java.lang.reflect.*;
public class Foo {
    interface IFoo { public int get(); }

    public static IFoo build2(final int v) {
        return new IFoo() { public int get() {return v;} };
    }
    public static void main(String[] args) throws Exception {
        Class<?> klazz = build2(42).getClass();
        for (Constructor<?> c : klazz.getDeclaredConstructors()) {
            System.out.println(c);
        }
        // prints: Foo$1(int)
    }
}

它表明Foo$1(为匿名IFoo类分配的二进制名称)只有一个构造函数,并且它需要一个int. 这就是它可以做到return v的,因为返回的实际上是这个隐式创建的构造函数分配给隐式创建的字段的任何内容。

反编译Foo$1(使用 eg javap -c)以查看生成的字节码是有益的。final您会看到,实际上这就是匿名类访问变量时发生的情况。

相关问题

于 2010-08-10T19:11:19.497 回答