W3docs

Java Reflection: Methoden aufrufen

Methoden in Java reflektiv untersuchen und aufrufen mit der Method-Klasse – Parameter, Rückgabewerte und Ausnahmen.

Ein Method-Objekt beschreibt eine Methode und ermöglicht es – entscheidend – sie zu aufzurufen: method.invoke(target, args...). Das ist der reflektive Kern von Test-Frameworks (finde @Test-Methoden, rufe sie auf), von Frameworks, die Handler per Name aufrufen, und von Scripting-Brücken. Dieses Kapitel behandelt das Finden von Methoden, den Parameter-Typ-Abgleich, der alle verwirrt, das Aufrufen von Instanz- und statischen Methoden, Rückgabewerte und wie Ausnahmen verpackt werden.

Wenn Sie neu bei Reflection sind, beginnen Sie mit der Reflection-Einführung und dem Kapitel über das Klassenobjekt, da hier alles mit einem Class<?> beginnt. Das Lesen und Schreiben von Datenelementen funktioniert auf dieselbe Weise und wird im Kapitel über das Reflektieren auf Felder behandelt.

Methoden finden

Sie suchen eine Methode anhand von Name plus Parametertypen — die Parametertypen unterscheiden Überladungen in Java:

Class<?> c = Calc.class;

Method m1 = c.getMethod("add", int.class, int.class);          // public, incl. inherited
Method m2 = c.getDeclaredMethod("secret", String.class);       // any access, this class only

Method[] pub = c.getMethods();           // all public methods, incl. Object's and inherited
Method[] own = c.getDeclaredMethods();   // all access levels, declared here only

Die Class-Objekte für Parameter müssen mit den deklarierten Parametertypen exakt übereinstimmen — es gibt keine Überladungsauflösung und keine Erweiterung. getMethod("add", Integer.class, Integer.class) findet add(int, int) nicht; Sie müssen int.class übergeben. Eine falsche Kombination wirft NoSuchMethodException. Für eine Methode ohne Argumente übergeben Sie keine Klassenargumente: getMethod("toString").

Aufrufen: Instanz, statisch und Argumente

invoke nimmt zuerst das Zielobjekt, dann die Argumente als Varargs Object[]:

Calc calc = new Calc();
Method add = Calc.class.getMethod("add", int.class, int.class);
Object result = add.invoke(calc, 2, 3);     // → Integer 5 (autoboxed)
int sum = (int) result;                       // unbox manually

Bei einer statischen Methode wird das Ziel ignoriert — übergeben Sie null:

Method parse = Integer.class.getMethod("parseInt", String.class);
Object n = parse.invoke(null, "42");          // → Integer 42

Primitive Argumente werden automatisch in das Object[] geboxt; die Laufzeit entboxt sie, um den primitiven Parametern zu entsprechen. Der Rückgabewert ist immer Object — Primitive kommen geboxt zurück, void-Methoden geben null zurück.

Wie Ausnahmen auftreten: InvocationTargetException

Das ist die wichtigste Falle überhaupt. Wenn die aufgerufene Methode eine Ausnahme wirft, propagiert invoke diese Ausnahme nicht direkt. Sie wird in einer InvocationTargetException verpackt, und Sie holen die echte Ausnahme mit getCause():

try {
  riskyMethod.invoke(target);
} catch (InvocationTargetException e) {
  Throwable real = e.getCause();    // the exception the method actually threw
  // handle 'real', not 'e'
}

Die anderen geprüften Ausnahmen betreffen den Reflection-Aufruf selbst, nicht den Methodenrumpf:

  • IllegalAccessException — die Methode ist nicht zugänglich und Sie haben setAccessible(true) nicht aufgerufen.
  • IllegalArgumentException — falsche Anzahl/Typen von Argumenten oder falscher Zieltyp.
  • NoSuchMethodException — wird zur Suchzeit geworfen, nicht zur Aufrufzeit.

Also: Suchfehler und Argumentfehler werden „direkt" geworfen, aber alles, was der eigene Code der Methode wirft, ist in InvocationTargetException verpackt.

Rückgabetypen, Varargs und Generics

  • Rückgabetyp-Metadaten: m.getReturnType() (ausgelöschte Class) und m.getGenericReturnType() (Type, behält Generics).
  • Parameter: m.getParameterTypes(), m.getParameterCount() und m.getParameters() (Namen verfügbar wenn mit -parameters kompiliert).
  • Varargs: ein String...-Parameter ist wirklich String[]. Suchen Sie ihn mit getMethod("f", String[].class) und rufen Sie ihn durch Übergabe eines echten Arrays auf, oder verlassen Sie sich darauf, dass invoke ein abschließendes Array für den Varargs-Slot akzeptiert.
  • Bridge/synthetische Methoden: generische Klassen erzeugen versteckte Bridge-Methoden; filtern Sie diese beim Aufzählen mit m.isBridge() / m.isSynthetic().

Ein ausgearbeitetes Beispiel: ein Mini-Befehls-Dispatcher

Das Programm baut einen kleinen Dispatcher, der String-Befehle auf Methoden eines Service-Objekts abbildet, diese reflektiv mit geparsten Argumenten aufruft, eine Methode behandelt, die wirft (um das InvocationTargetException-Entpacken zu zeigen), und eine static-Factory mit einem null-Ziel aufruft.

java— editable, runs on the server

Was aus dem Lauf zu entnehmen ist:

  • Die statische Factory wurde mit factory.invoke(null) aufgerufen — bei einer static-Methode ist das Zielobjekt irrelevant, daher ist null die Konvention. Der Dispatcher verwendete dann denselben invoke-Mechanismus für Instanzmethoden und übergab das echte calc als Ziel. Eine API, beide Arten von Methoden.
  • divide(1, 0) hat keine ArithmeticException aus invoke geworfen. Es warf InvocationTargetException, und die echte ArithmeticException: / by zero wurde über getCause() gefunden. Jedes Framework, das Benutzercode reflektiv aufruft, muss dies entpacken; wer es vergisst, sieht manchmal eine verwirrende InvocationTargetException im Stack-Trace statt des echten Fehlers.
  • Das Nachschlagen von add mit Integer.class-Parametern scheiterte mit NoSuchMethodException, obwohl add(int,int) existiert. Reflection gleicht Parametertypen exakt ohne Boxing oder Erweiterung ab — int.class und Integer.class sind verschiedene Schlüssel. Das ist der häufigste Reflection-Fehler und der Grund, warum primitive .class-Literale wichtig sind.
  • Die private secret-Methode war nur nach getDeclaredMethod + setAccessible(true) aufrufbar. Wie bei Feldern sind der Lookup-Stil (getDeclared…) und das Zugangstor (setAccessible) zwei unabhängige Schritte; man braucht beide, um ein privates Element zu erreichen.
  • Rückgabewerte kamen als Object an und wurden an der Aufrufstelle gecastet ((Calculator) factory.invoke(...)), während add's int autogeboxed als Integer zurückkam. Reflection hat kein statisches Wissen über Rückgabetypen, daher ist der Aufrufer für den Cast/Unbox verantwortlich — und ein falscher Cast erscheint zur Laufzeit als ClassCastException, nicht zur Kompilierzeit.

Übungen

Übung
Sie rufen eine Methode reflektiv mit 'm.invoke(obj)' auf, und der Methodenrumpf wirft eine 'IllegalStateException'. Welche Ausnahme fangen Sie in Ihrem aufrufenden Code tatsächlich ab, und wie erreichen Sie die 'IllegalStateException'?
Sie rufen eine Methode reflektiv mit 'm.invoke(obj)' auf, und der Methodenrumpf wirft eine 'IllegalStateException'. Welche Ausnahme fangen Sie in Ihrem aufrufenden Code tatsächlich ab, und wie erreichen Sie die 'IllegalStateException'?
Was this page helpful?