Come lavorare con varargs e reflection

Domanda semplice, come funziona questo codice?

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); } } 

Produzione:

 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) 

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

lavori. Il problema è che getMethod(..) cerca solo i metodi public . Dalla javadoc:

Restituisce un object Method che riflette il metodo del membro pubblico specificato della class o dell’interfaccia rappresentata da questo object Class.

Aggiornamento: dopo aver ottenuto con successo il metodo, è ansible richiamarlo utilizzando:

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

vale a dire: crea una nuova matrice di Object con un elemento: la serie di String . Con i nomi delle variabili dovrebbe assomigliare a:

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

// prima della modifica:

Il tuo problema è il fatto che getMethod cerca un membro public .

Da Class.getMethod (enfasi mia):

Restituisce un object Method che riflette il metodo del membro pubblico specificato della class o dell’interfaccia rappresentata da questo object Class

Quindi hai due opzioni:

  • Crea public void foo(String... s) e usa getMethod
  • Utilizzare invece getDeclaredMethod

Notare che esiste la stessa differenza per getField/s vs getDeclaredField/s getConstructor/s contro getDeclaredConstructor/s .


// invoke problema

Ciò è particolarmente sgradevole, ma quello che succede è che invoke(Object obj, Object... args) rende difficile se devi passare una matrice di tipo di riferimento come un solo argomento, perché può essere cast-to su Object[] , anche se dovrebbe essere racchiuso all’interno di un new Object[1] .

Tu puoi fare:

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

Questo aggira il meccanismo vararg. Più succintamente puoi anche fare:

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

Il cast su Object fa in modo che il meccanismo vararg faccia il lavoro per creare l’array per te.

Il trucco è anche necessario quando si passa un null come argomento a varargs e non ha nulla a che fare con il reflection.

 public void foo(String... ss) { System.out.println(ss[0]); } foo(null); // causes NullPointerException foo((String) null); // prints "null"