InstantiationException su newInstance di generazione di classe anonima
-
27-09-2019 - |
Domanda
Aggiornamento:questo è più o meno un dupe, e si scopre di essere un compilatore magia aggiunta di un costruttore di passare la variabile locale in build2.
Date un'interfaccia come tale:
public interface IFoo {
public int get();
}
Il codice riportato di seguito stampa 1, 1, 2 e quindi genera un'eccezione durante il tentativo di chiamata getClass().newInstance() il valore restituito da build2, ma non quando chiamando lo stesso il valore restituito di build1.Tutte le idee perché?
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());
}
}
Il mio debugger indica che nel newInstance() chiamata, getConstructor0 è il lancio di un NoSuchMethodException.
Soluzione
Ecco cosa succede:
newInstance()
richiede un nullary costruttore- quando si crea una classe anonima che accede a un
final
variabile, un campo è in realtà creato implicitamente a tenere questo valore, che è inizialmente superato la sua implicita costruttore - così, il
IFoo
creato nelbuild2
in realtà NON hanno un nullary costruttore
Ecco un frammento di mostrare ciò che sta succedendo:
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)
}
}
Essa mostra che Foo$1
(assegnato binario nome per l'anonimo IFoo
di classe) ha un solo costruttore, e ci vuole un int
.Questo è come si può return v
, perché ciò che viene restituito è in realtà tutto ciò che è assegnato al creato implicitamente campo da questo creato implicitamente costruttore.
È istruttivo per decompilare il Foo$1
(utilizzando, ad esempio, javap -c
) per vedere cosa bytecode viene generato.Vedrete che, in realtà, questo è ciò che accade quando un final
variabile è accessibile da una classe anonima.