2010-04-08 9 views
22

Einfache Frage, wie funktioniert dieser Code?Wie man mit varargs und Reflexion arbeitet

public class T { 

    public static void main(String[] args) throws Exception { 
     new T().m(); 
    } 

    public // as mentioned by Bozho 
    void foo(String... s) { 
     System.err.println(s[0]); 
    } 

    void m() throws Exception { 
     String[] a = new String[]{"hello", "kitty"}; 
     System.err.println(a.getClass()); 
     Method m = getClass().getMethod("foo", a.getClass()); 
     m.invoke(this, (Object[]) a); 
    } 
} 

Ausgang:

class [Ljava.lang.String; 
Exception in thread "main" java.lang.IllegalArgumentException: wrong number of arguments 
     at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 
     at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) 
     at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) 
     at java.lang.reflect.Method.invoke(Method.java:597) 

Antwort

41
Test.class.getDeclaredMethod("foo", String[].class); 

funktioniert. Das Problem ist, dass getMethod(..) nur die public Methoden durchsucht. Vom javadoc:

Returns a Method object that reflects the specified public member method of the class or interface represented by this Class object.

Update: Nach einer erfolgreich verlaufenen Verfahren erhalten, können Sie rufen Sie es mit:

m.invoke(this, new Object[] {new String[] {"a", "s", "d"}}); 

das ist - erstellen Sie eine neue Object Array mit einem Element - das String Array. Mit Ihrer Variablennamen würde es wie folgt aussehen:

m.invoke(this, new Object[] {a}); 
+0

Danke! Aber jetzt stehe ich bei der Anrufung fest. – PeterMmm

+0

Danke nochmal! Hab das nicht gesehen. – PeterMmm

+0

+1 für 'invoke' Lösung; Das ist ein hässlicher. – polygenelubricants

8

// vor bearbeiten:

Ihr Problem ist die Tatsache, dass getMethod für ein public Mitglied aussieht.

Vom Class.getMethod (Hervorhebung von mir):

Returns a Method object that reflects the specified public member method of the class or interface represented by this Class object

So haben Sie zwei Möglichkeiten:

  • public void foo(String... s) herzustellen und zu verwenden getMethod
  • Verwenden getDeclaredMethod statt

Beachten Sie, dass der gleiche Unterschied besteht für r getField/s vs getDeclaredField/s und getConstructor/s vs getDeclaredConstructor/s.


// invoke Problem

Dies ist besonders böse, aber was ist passiert, dass invoke(Object obj, Object... args) es schwierig macht, wenn Sie eine Reihe von Referenztyp als einziges Argument übergeben müssen, weil es machbar gegossen zu Object[], obwohl es in einem new Object[1] stattdessen verpackt werden sollte.

können Sie tun:

m.invoke(this, new Object[] {a}); // Bohzo's solution 

Dies umgeht den Vararg Mechanismus. Kurz und bündig können Sie auch tun:

m.invoke(this, (Object) a); 

Die Besetzung zu Object macht den Vararg Mechanismus für Sie die Arbeit der Erstellung des Arrays zu tun.

Der Trick wird auch benötigt, wenn ein null als Argument an Varargs übergeben wird, und hat nichts mit Reflexion zu tun.

public void foo(String... ss) { 
    System.out.println(ss[0]); 
} 

    foo(null); // causes NullPointerException 
    foo((String) null); // prints "null" 
+1

+1, nett für diese Besetzung Sache – PeterMmm

+0

@polygeneLubricants, mein Fall ist: void foo() Funktion hat Argument Param ... param (Hinweis: Param ist generischer Typ) anstelle von String ... s. Ich habe das neue Objekt [] {new URL ("google.com")} verwendet, bekomme aber den folgenden Fehler: IllegalArgumentException: Argument 1 sollte den Typ java.lang.Object [] haben, java.net.URL haben. Vielen Dank im Voraus. – MapleLover