如何使用varargs和反射

时间:2010-04-08 14:45:57

标签: java reflection variadic-functions

简单的问题,如何使这段代码有效?

public class T {

    public static void main(String[] args) throws Exception {
        new T().m();
    }

    public // as mentioned by Bozho
    void foo(String... s) {
        System.err.println(s[0]);
    }

    void m() throws Exception {
        String[] a = new String[]{"hello", "kitty"};
        System.err.println(a.getClass());
        Method m = getClass().getMethod("foo", a.getClass());
        m.invoke(this, (Object[]) a);
    }
}

输出:

class [Ljava.lang.String;
Exception in thread "main" java.lang.IllegalArgumentException: wrong number of arguments
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
        at java.lang.reflect.Method.invoke(Method.java:597)

2 个答案:

答案 0 :(得分:47)

Test.class.getDeclaredMethod("foo", String[].class);

的工作原理。问题是getMethod(..)仅搜索public方法。来自javadoc:

  

返回一个Method对象,该对象反映此Class对象所表示的类或接口的指定公共成员方法。

更新:成功获取方法后,您可以使用以下方法调用它:

m.invoke(this, new Object[] {new String[] {"a", "s", "d"}});

即 - 创建一个包含一个元素的新Object数组 - String数组。使用您的变量名称,它看起来像:

m.invoke(this, new Object[] {a});

答案 1 :(得分:9)

//编辑之前:

您的问题是getMethod寻找public成员。

来自Class.getMethod(强调我的):

  

返回一个Method对象,该对象反映此Class对象所代表的类或接口的指定 public 成员方法

所以你有两个选择:

  • 制作public void foo(String... s)并使用getMethod
  • 使用getDeclaredMethod代替

请注意,getField/sgetDeclaredField/sgetConstructor/sgetDeclaredConstructor/s存在相同的差异。


// invoke问题

这特别令人讨厌,但是如果你需要传递一个引用类型数组作为唯一的参数,那么invoke(Object obj, Object... args)会使它变得棘手,因为它可以转换为Object[],甚至虽然它应该包含在new Object[1]内。

你可以这样做:

m.invoke(this, new Object[] {a}); // Bohzo's solution

这绕过了vararg机制。你可以更简洁地做到:

m.invoke(this, (Object) a);

转换为Object使vararg机制能够为您创建数组。

null作为参数传递给varargs时也需要这个技巧,而且与反射无关。

public void foo(String... ss) {
    System.out.println(ss[0]);
}

    foo(null); // causes NullPointerException
    foo((String) null); // prints "null"