Как работать с варагсом и отражением - PullRequest
24 голосов
/ 08 апреля 2010

Простой вопрос, как заставить этот код работать?

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

Выход:

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 ]

47 голосов
/ 08 апреля 2010
Test.class.getDeclaredMethod("foo", String[].class);

работает. Проблема в том, что getMethod(..) ищет только методы public. Из Javadoc:

Возвращает объект Method, который отражает указанный открытый метод-член класса или интерфейса, представленного этим объектом Class.

Обновление: После успешного получения метода вы можете вызвать его с помощью:

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

то есть - создать новый массив Object с одним элементом - массивом String. С вашими именами переменных это будет выглядеть так:

m.invoke(this, new Object[] {a});
9 голосов
/ 08 апреля 2010

// до редактирования:

Ваша проблема в том, что getMethod ищет public члена.

Из Class.getMethod (выделено мое):

Возвращает объект Method, который отражает указанный открытый метод-член класса или интерфейса, представленный этим объектом класса

Итак, у вас есть два варианта:

  • Сделайте public void foo(String... s) и используйте getMethod
  • Используйте getDeclaredMethod вместо

Обратите внимание, что такая же разница существует для getField/s против getDeclaredField/s и getConstructor/s против getDeclaredConstructor/s.


// invoke проблема

Это особенно неприятно, но получается, что invoke(Object obj, Object... args) усложняет задачу, если вам нужно передать массив ссылочного типа в качестве единственного аргумента, потому что он может быть приведен к Object[], даже если это и должно быть вместо этого обернутый в new Object[1].

Вы можете сделать:

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

Это обходит механизм варагга. Более кратко вы также можете сделать:

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

Приведение к Object заставляет механизм vararg создавать для вас массив.

Трюк также необходим при передаче null в качестве аргумента varargs и не имеет ничего общего с отражением.

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

    foo(null); // causes NullPointerException
    foo((String) null); // prints "null"
...