Вызов методов переменной арности с отражением Java? - PullRequest
1 голос
/ 27 февраля 2012

Я хотел бы понять, что может происходить с вызовом методов переменной арности с использованием отражения Java.Допустим, у нас есть простой метод:

void doAllTheThings(Object ... things) {
  // ...which does something with all the things...
}

И мы хотим, чтобы он вызывался динамически, поэтому мы получаем метод с помощью отражения:

Method doItAll = Superklass.getDeclaredMethod("doAllTheThings", Object[].class);

И передаем массив:

Object[] allTheThings = new Object[] { "abc", true, 15 };
doItAll.invoke(allTheThings);

Теперь, похоже, это не работает так, как думала моя интуиция;в частности, я, кажется, получаю различные оттенки IllegalArgumentException, когда пытаюсь вызвать метод с такими переменными, как этот.

Здесь явно что-то мне не хватает.Я предполагаю, что это как-то связано с тем, как переменные распределяются в переменные.Я нашел это четырехлетнее сообщение в блоге, в котором, похоже, говорится о той же самой проблеме , но я не могу воспроизвести «успешное» дело там.Есть мысли о том, что здесь может происходить?

1 Ответ

8 голосов
/ 28 февраля 2012

Вам необходимо передать Object[][] в этом случае:

Object[] allTheThings = new Object[] { "abc", true, 15 };
doItAll.invoke(o, new Object[]{allTheThings});

Причина в том, что один параметр things преобразуется компилятором в один параметр типа Object[]invoke принимает массив со значениями параметров.

Рассмотрим метод с большим количеством параметров, чтобы сделать его более понятным:

void doMoreThings(Foo bar, Object ... things) { ... }

Object[] allTheThings = new Object[] { "abc", true, 15 };
doMore.invoke(o, new Object[]{new Foo(), allTheThings});

invoke само объявлено, что оно принимает varargs,так что вы можете позволить компилятору создать внешний массив для вас.Но он не сделает этого, если вы передадите Object[], потому что он думает, что вы уже сделали это.Так что просто спрячьте этот факт от компилятора:

doItAll.invoke(o, (Object)allTheThings);
doMore.invoke(o, new Foo(), allTheThings);

Обратите внимание на приведение в первой строке, теперь компилятор больше не знает, что массив уже существует, поэтому он его создает.Во второй строке это не нужно, потому что у компилятора в любом случае нет другого шанса.

Редактировать : обратите внимание, что ваш код даже не компилируется, потому что вы пропустили передачу экземпляра классас doAllTheThings методом до invoke (я назвал его o в моем коде).

...