Comment travailler avec varargs et réflexion
Question Simple, comment faire fonctionner ce code ?
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);
}
}
Sortie:
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)
2 réponses
Test.class.getDeclaredMethod("foo", String[].class);
Fonctionne. Le problème est que getMethod(..) Ne recherche que les méthodes public. Du javadoc:
Renvoie un objet de méthode qui reflète la méthode de membre public spécifiée de la classe ou de l'interface représentée par cet objet de classe.
Update: Après avoir obtenu la méthode avec succès, vous pouvez l'invoquer en utilisant:
m.invoke(this, new Object[] {new String[] {"a", "s", "d"}});
C'est-à - créer un nouveau Object tableau avec un élément - le String tableau. Avec vos noms de variables il ressemblerait comme:
m.invoke(this, new Object[] {a});
/ / avant l'édition:
Votre problème est le fait que getMethod recherche un public membre.
De la Class.getMethod (je mets l'accent sur le mien):
Renvoie un objet
Methodqui reflète la méthode membre public spécifiée de la classe ou de l'interface représentée par cet objet de classe
Vous avez donc deux options:
- marque
public void foo(String... s)et utilisegetMethod - utilisez
getDeclaredMethodà la place
Notez que la même différence existe pour getField/s vs getDeclaredField/s et getConstructor/s vs getDeclaredConstructor/s.
//invoke problème
C'est particulièrement méchant, mais ce qui se passe est que invoke(Object obj, Object... args) le rend difficile si vous avez besoin de passer un tableau de type de référence comme argument unique, car il est capable de Object[], même s'il doit être enveloppé dans un new Object[1] à la place.
, Vous pouvez faire:
m.invoke(this, new Object[] {a}); // Bohzo's solution
Cela contourne le mécanisme vararg. Plus succinctement, vous pouvez aussi faire:
m.invoke(this, (Object) a);
Le casting à Object fait le vararg mécanisme faire le travail de création du tableau pour vous.
L'astuce est également nécessaire lors du passage d'un null comme argument à varargs, et n'a rien à voir avec la réflexion.
public void foo(String... ss) {
System.out.println(ss[0]);
}
foo(null); // causes NullPointerException
foo((String) null); // prints "null"