我想了解使用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);
现在,这似乎不像我的直觉所认为的那样起作用。特别是,当我尝试使用这样的varargs调用方法时,似乎得到了各种阴影的
IllegalArgumentException
。显然我在这里缺少什么。我的猜测是,这与变量如何编组到varargs中有关。我找到了this four year old blog post which seems to be talking about the same issue,但是无法在此处重现“成功”的情况。对这里可能发生的事情有任何想法吗?
最佳答案
在这种情况下,您需要传递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
)。