【问题标题】:Invoking variable-arity methods with Java reflection?使用 Java 反射调用可变参数方法?
【发布时间】:2012-02-27 19:58:31
【问题描述】:

我想了解使用 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

显然我在这里缺少一些东西。我的猜测是这在某种程度上与变量如何编组到可变参数中有关。我找到了this four year old blog post which seems to be talking about the same issue,但我无法重现那里的“成功”案例。对这里可能发生的事情有任何想法吗?

【问题讨论】:

    标签: java reflection variadic-functions arity


    【解决方案1】:

    在这种情况下,您需要传入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 本身被声明为采用可变参数,因此您可以让编译器为您创建外部数组。但如果你传递Object[],它就不会这样做,因为它认为你已经这样做了。所以只需对编译器隐藏这个事实:

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

    注意第一行中的强制转换,现在编译器现在不再有一个数组,所以它创建了一个。在第二行中,这不是必需的,因为编译器无论如何都没有其他机会。

    编辑: 请注意,您的代码甚至无法编译,因为您错过了使用 doAllTheThings 方法将类的实例传递给 invoke(我在代码中将其命名为 o)。

    【讨论】:

    • 非常感谢您的回复,尤其是编辑。 (您在编辑中发现的疏忽解释了为什么我无法让示例代码工作!)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-18
    • 1970-01-01
    • 1970-01-01
    • 2011-01-13
    • 1970-01-01
    相关资源
    最近更新 更多