更新:这或多或少是一个 欺骗, ,事实证明这是编译器的魔法,添加了一个构造函数来传入 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。

有帮助吗?

解决方案

发生的情况如下:

  • newInstance() 需要一个空构造函数
  • 当您创建一个正在访问的匿名类时 final 变量,实际上隐式创建了一个字段来保存该值,该值最初传递给其隐式构造函数
  • 就这样 IFoo 创建于 build2 实际上没有空构造函数

这是一个显示正在发生的事情的片段:

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 class) 只有一个构造函数,并且需要一个 int. 。这样就可以了 return v, ,因为返回的实际上是这个隐式创建的构造函数分配给隐式创建的字段的任何内容。

反编译很有指导意义 Foo$1 (使用例如 javap -c)以查看生成了哪些字节码。你会发现事实上这就是当一个 final 变量由匿名类访问。

相关问题

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top