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
Method
qui 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"