InstantiationException auf newInstance der erzeugten anonymen Klasse
-
27-09-2019 - |
Frage
Aktualisieren : Dies ist mehr oder weniger ein Betrogene , und es stellt sich heraus Compiler Magie, um einen Konstruktor Zugabe in build2 in den lokalen variablen zu übergeben.
eine Schnittstelle wie solche Gegeben:
public interface IFoo {
public int get();
}
Der folgende Code druckt 1, 1, 2 und wirft dann eine Ausnahme, wenn sie versuchen getClass () aufrufen. NewInstance () auf dem Wert von build2 zurückgegeben, aber nicht, wenn sie auf dem Rückgabewert von build1 den gleichen Aufruf. Irgendwelche Ideen, warum?
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());
}
}
Mein Debugger zeigt an, dass in der newInstance () -Aufruf, getConstructor0 eine NoSuchMethodException wirft.
Lösung
Hier ist, was passiert:
-
newInstance()
erfordert eine nullary Konstruktor - , wenn Sie eine anonyme Klasse erstellen, die eine
final
Variable zugreift, wird ein Feld tatsächlich implizit erstellt, diesen Wert zu halten, die sich zunächst auf den impliziten Konstruktor übergeben wird - damit die
IFoo
inbuild2
erstellt eigentlich nicht haben eine nullary Konstruktor
Hier ist ein Ausschnitt zu zeigen, was los ist:
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)
}
}
Es zeigt, dass Foo$1
(die zugewiesene binären Namen für die anonyme IFoo
Klasse) nur ein Konstruktor hat, und es dauert eine int
. Dies ist, wie es return v
kann, weil das, was zurückgegeben werden ist eigentlich, was auf die implizit erstellt von diesem implizit erstellt Konstruktor zugewiesen wird.
Es ist lehrreich, die Foo$1
zu dekompilieren (beispielsweise unter Verwendung javap -c
), um zu sehen, was Bytecode erzeugt wird. Sie werden sehen, dass in der Tat das ist, was passiert, wenn ein final
Variable von einer anonymen Klasse zugegriffen wird.