Search code examples
javaanonymous-classinstantiationexception

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(bar.get());
 }
}

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


Solution

  • 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