InstantiationException для нового экземпляра сгенерированного анонимного класса

Обновление: это более или менее dupe, и оказывается, что это магия компилятора, добавляющая конструктор для передачи локальной переменной в build2.

Учитывая такой интерфейс:

public interface IFoo { public int get(); }

Приведенный ниже код выводит 1, 1, 2, а затем выдает исключение при попытке вызвать getClass().newInstance() для значения, возвращаемого build2, но не при вызове того же самого для возвращаемого значения 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.


person David    schedule 10.08.2010    source источник
comment
возможный дубликат Почему я иметь это InstantiationException в Java при доступе к конечным локальным переменным? - вот и все   -  person polygenelubricants    schedule 10.08.2010


Ответы (1)


Вот что происходит:

  • 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) имеет только один конструктор и принимает int. Вот как он может return v, потому что на самом деле возвращается то, что назначено неявно созданному полю этим неявно созданным конструктором.

Поучительно декомпилировать Foo$1 (используя, например, javap -c), чтобы увидеть, какой байт-код генерируется. Вы увидите, что именно это и происходит, когда анонимный класс обращается к переменной final.

Похожие вопросы

person polygenelubricants    schedule 10.08.2010