Как работать с 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"