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)
24
demandé sur PeterMmm 2010-04-08 18:45:56

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});
45
répondu Bozho 2010-04-08 15:02:49

/ / 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 utilise getMethod
  • 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"
9
répondu polygenelubricants 2010-04-08 15:20:40