W3docs

Java Reflection: Konstruktoren aufrufen

Java-Klassen per Reflection instanziieren mit Constructor.newInstance und Class.getDeclaredConstructor.

Ein Objekt zu erzeugen, ohne new zu schreiben, ist der Reflection-Trick hinter jedem Dependency-Injection-Container, Deserialisierer und Plugin-Loader: Man hat eine Class und braucht eine Instanz. Das Constructor<T>-Objekt repräsentiert einen Konstruktor und erstellt Instanzen mit newInstance(args...). Dieses Kapitel behandelt das Finden von Konstruktoren, den Aufruf mit Argumenten, den Zugriff auf private-Konstruktoren und warum der alte Class.newInstance()-Shortcut als veraltet markiert ist.

Wenn du neu bei Reflection bist, beginne mit der Reflection-Einführung und kehre dann hierher zurück. Die unten beschriebenen Mechanismen spiegeln das wider, was du für das Aufrufen von Methoden und das Lesen von Feldern per Reflection gesehen hast.

Konstruktoren finden

Konstruktoren werden nur anhand der Parametertypen gesucht — es gibt keinen Namen, da alle Konstruktoren den Namen der Klasse teilen:

Class<User> c = User.class;

Constructor<User> noArg  = c.getDeclaredConstructor();                  // ()
Constructor<User> twoArg = c.getDeclaredConstructor(String.class, int.class);  // (String, int)

Constructor<?>[] pub  = c.getConstructors();          // public only
Constructor<?>[] all  = c.getDeclaredConstructors();  // any access level

Wie überall in Reflection müssen Parametertypen exakt übereinstimmen (int.class, nicht Integer.class), und getConstructor sieht nur public-Konstruktoren, während getDeclaredConstructor auch private-, protected- und Package-Konstruktoren sieht. Beachte: Constructor<T> ist generisch für die Klasse, die es erzeugt, sodass newInstance ein typisiertes T zurückgibt (anders als Method.invoke's rohes Object).

Instanzen mit newInstance erstellen

Constructor<User> ctor = User.class.getDeclaredConstructor(String.class, int.class);
User u = ctor.newInstance("ada", 36);     // returns a typed User

Argumente funktionieren genauso wie bei Method.invoke: ein Varargs-Object[], Primitives werden automatisch geboxt. Der Unterschied ist, dass es kein Zielobjekt gibt — ein Konstruktor erstellt das Ziel. Vom Konstruktorrumpf geworfene Ausnahmen werden in InvocationTargetException eingewickelt, genau wie bei Methoden; entpacke sie mit getCause().

Zugriff auf private Konstruktoren

Singletons, Utility-Klassen und Builder verstecken oft ihren Konstruktor. Reflection geht einfach daran vorbei mit setAccessible(true):

Constructor<Singleton> ctor = Singleton.class.getDeclaredConstructor();
ctor.setAccessible(true);                 // bypass the private modifier
Singleton fresh = ctor.newInstance();     // a SECOND instance — breaks the singleton!

Das ist wirklich mächtig und wirklich gefährlich: Es hebt die Singleton-Garantie auf, den "keine Instanzen"-Vertrag einer Utility-Klasse und jede Invariante, die der Konstruktor schützen sollte. (Ein enum-Singleton ist die einzige Form, die Reflection nicht instanziieren kann — Constructor.newInstance lehnt enum-Typen explizit mit IllegalArgumentException ab, was ein Grund dafür ist, warum "enum-Singleton" das empfohlene Muster ist.)

Warum Class.newInstance() veraltet ist

Du wirst in altem Code den Shortcut clazz.newInstance() sehen:

User u = User.class.newInstance();   // DEPRECATED since Java 9

Er ist aus zwei echten Gründen veraltet:

  1. Er ruft nur den No-Arg-Konstruktor auf. Keine Möglichkeit, Argumente zu übergeben.
  2. Er behandelt Ausnahmen falsch. Wenn der No-Arg-Konstruktor eine geprüfte Ausnahme wirft, propagiert Class.newInstance() sie ohne sie zu deklarieren — was die geprüfte-Ausnahme-Analyse des Compilers untergräbt.

Der Ersatz lautet immer:

User u = User.class.getDeclaredConstructor().newInstance();

Dies ist eine Zeile länger, ruft einen explizit gewählten Konstruktor auf und wickelt Konstruktorausnahmen in InvocationTargetException ein, sodass nichts undeklariert entkommen kann. Verwende es als Standard-Idiom auch für den No-Arg-Fall.

Ein durchgearbeitetes Beispiel: eine kleine reflektive Factory

Das Programm erstellt Objekte auf drei Arten: einen öffentlichen Multi-Arg-Konstruktor, einen private-Konstruktor, der via setAccessible erreichbar ist, und das moderne No-Arg-Idiom — dann zeigt es, wie ein werfender Konstruktor eingewickelt wird, und die veraltete Signatur zum Vergleich.

java— editable, runs on the server

Was aus dem Lauf mitzunehmen ist:

  • Die generische build-Factory hat Widget und Hidden aus einer Class plus einem Parametertyp-Array erstellt — ohne einen der Typen in einem new-Ausdruck zu nennen. Diese Signatur, <T> T build(Class<T>, Class<?>[], Object...), ist im Wesentlichen das, wie ein DI-Container-Instanziierungskern aussieht: übergib einen Typ und Argumente, erhalte eine Instanz zurück.
  • getDeclaredConstructor().newInstance() hat den Standard-Widget erstellt und damit den modernen Ersatz für Class.newInstance() demonstriert. Bevorzuge ihn immer: Er ermöglicht die Wahl des Konstruktors und leitet Konstruktorausnahmen durch InvocationTargetException, anstatt undeklarierte geprüfte Ausnahmen zu leaken.
  • Die reflektive Hidden-Instanz war nicht dasselbe Objekt wie Hidden.INSTANCE (same instance? false). setAccessible(true) ging direkt am private-Konstruktor vorbei und prägte eine zweite Instanz — konkreter Beweis, dass Reflection die Kerngarantie eines Singletons brechen kann. Defensiv-Singletons werfen im Konstruktor, wenn bereits eine Instanz existiert; Enums sind durch ihre Konstruktion immun.
  • Der Konstruktor, der eine negative Größe ablehnte, warf IllegalArgumentException aus seinem Rumpf, und das tauchte als InvocationTargetException mit dem echten Grund darin auf — identische Einwicklung wie bei Method.invoke. Validierung zur Konstruktionszeit bleibt durch Reflection erhalten; man muss nur entpacken, um sie zu sehen.
  • Constructor<T> gab ein typisiertes T (Widget, Hidden) ohne Cast zurück, anders als Method.invoke's rohes Object. Da der Konstruktor generisch in der Klasse ist, die er erstellt, bleibt die Factory an ihrer Grenze typsicher, auch wenn alles innen reflektiv ist.

Übungsaufgaben

Übung
Ein Teamkollege schreibt 'MyService s = MyService.class.newInstance();' und der Linter markiert 'newInstance()' als veraltet. Was ist der empfohlene Ersatz, und was ist der wichtigste praktische Grund, warum die alte Form veraltet ist?
Ein Teamkollege schreibt 'MyService s = MyService.class.newInstance();' und der Linter markiert 'newInstance()' als veraltet. Was ist der empfohlene Ersatz, und was ist der wichtigste praktische Grund, warum die alte Form veraltet ist?
Was this page helpful?