Как работать с varargs и reflection

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

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)

Ответ 1

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

работает. Проблема в том, что getMethod(..) выполняет поиск только методов public. Из javadoc:

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

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

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

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

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

Ответ 2

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

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

Из Class.getMethod (основное внимание):

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

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

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

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


//invoke проблема

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

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

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

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

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"