InstantiationException для newInstance сгенерированного анонимного класса - PullRequest
3 голосов
/ 10 августа 2010

Обновление : это более или менее дублирование , и оказывается, что это волшебство компилятора, добавляющее конструктор для передачи локальной переменной в 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.

1 Ответ

4 голосов
/ 10 августа 2010

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

  • 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 обращается к анонимному классу.

Смежные вопросы

...