Invocando metodi di Arity a variabile con la riflessione di Java?
-
13-11-2019 - |
Domanda
Vorrei capire cosa potrebbe succedere con invocazione di metodi arsurti variabili usando la riflessione Java. Diciamo che abbiamo un metodo semplice:
void doAllTheThings(Object ... things) {
// ...which does something with all the things...
}
E vogliamo invocarlo in modo dinamico, quindi prendiamo il metodo attraverso la riflessione:
Method doItAll = Superklass.getDeclaredMethod("doAllTheThings", Object[].class);
E passare in un array:
Object[] allTheThings = new Object[] { "abc", true, 15 };
doItAll.invoke(allTheThings);
Ora, questo non sembra funzionare abbastanza come la mia intuizione avesse pensato; In particolare, mi sembra di avere varie sfumature di IllegalArgumentException
Quando provo a invocare un metodo con Vararg come questo.
C'è chiaramente qualcosa che mi manca qui. La mia ipotesi è che questo sia correlato in qualche modo a come le variabili vengono messe a marcia nei Vararg. ho trovato Questo post sul blog di quattro anni che sembra parlare dello stesso problema, ma non riesco a riprodurre il caso "riuscito" lì. Qualche idea su cosa potrebbe succedere qui?
Soluzione
Devi passare in un Object[][]
in questo caso:
Object[] allTheThings = new Object[] { "abc", true, 15 };
doItAll.invoke(o, new Object[]{allTheThings});
Il motivo è che il singolo things
Il parametro viene convertito dal compilatore in un singolo parametro di tipo Object[]
, e invoke
prende un array con i valori dei parametri.
Considera un metodo con più parametri per renderlo più chiaro:
void doMoreThings(Foo bar, Object ... things) { ... }
Object[] allTheThings = new Object[] { "abc", true, 15 };
doMore.invoke(o, new Object[]{new Foo(), allTheThings});
invoke
è esso stesso dichiarato di prendere Varargs, quindi puoi lasciare che il compilatore crei l'array esterno per te. Ma non lo farà se passi un Object[]
, perché pensa che tu l'abbia già fatto. Quindi nascondi questo fatto al compilatore:
doItAll.invoke(o, (Object)allTheThings);
doMore.invoke(o, new Foo(), allTheThings);
Nota il cast nella prima riga, ora il compilatore non ora non c'è più che esiste già un array, quindi ne crea uno. Nella seconda riga questo non è necessario, perché il compilatore non ha comunque altra possibilità.
Modificare: Nota che il tuo codice non si compila nemmeno perché ti sei perso un'istanza della classe con il doAllTheThings
metodo a invoke
(L'ho chiamato o
nel mio codice).