我想了解使用Java反射调用变量arity方法可能会发生什么。假设我们有一个简单的方法:
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,但我无法重现那里的“成功”案例。关于这里可能会发生什么的任何想法?
答案 0 :(得分:8)
在这种情况下你需要传递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
。)