开发者

InstantiationException on newInstance of generated anonymous class

Update: this is more-or-less a dupe, and it turns out to be compiler magic adding a constructor to pass in the local variable in build2.

Given an interface like such:

public interface IFoo { public int get(); }

The code below prints 1, 1, 2 and then throws an exception when trying to call getClass().newInstance() on the value returned by build2, but does not when calling the same on the returned value of build1. Any ideas why?

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(ba开发者_如何转开发r.get());
 }
}

My debugger indicates that in the newInstance() call, getConstructor0 is throwing a NoSuchMethodException.


Here's what happens:

  • newInstance() requires a nullary constructor
  • when you create an anonymous class that is accessing a final variable, a field is actually implicitly created to hold this value, which is initially passed to its implicit constructor
  • thus, the IFoo created in build2 does NOT actually have a nullary constructor

Here's a snippet to show what's going on:

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)
    }
}

It shows that Foo$1 (the assigned binary name for the anonymous IFoo class) has only one constructor, and it takes an int. This is how it can return v, because what's returned is actually whatever is assigned to the implicitly created field by this implicitly created constructor.

It is instructive to decompile the Foo$1 (using e.g. javap -c) to see what bytecode gets generated. You will see that in fact this is what happens when a final variable is accessed by an anonymous class.

Related questions

  • Why am I having this InstantiationException in Java when accessing final local variables?
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜