如何使用可变参数和反射技术

26

简单的问题,如何使这段代码工作?

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个回答

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

这个方法是可行的。问题在于getMethod(..)只会搜索public方法。从Java文档中可以看出:

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

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

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

那就是 - 创建一个新的Object数组,其中只有一个元素 - 这个String数组。使用您的变量名称,它看起来像这样:

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

谢谢!但是现在我卡在了调用上。 - PeterMmm
@PeterMmm 不错。顺便说一下,给有用的(包括被采纳的)答案点赞是被接受的做法。你只有2个赞,这与SO的理念相悖。 - Bozho
1
你也可以通过我的解决方案使用 m.invoke(this, (Object) a); - polygenelubricants
@Bozho,我的情况是:void foo()函数有参数Param... param(提示:Param是通用类型),而不是String... s。我使用new Object[] {new URL("http://google.com")},但出现以下错误:IllegalArgumentException: argument 1 should have type java.lang.Object[],got java.net.URL。 - MapleLover

13

//编辑前:

您的问题在于getMethod寻找public成员。

Class.getMethod(重点标记为我的)中可以看到:

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

因此您有两个选择:

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

请注意,getField/sgetDeclaredField/s以及getConstructor/sgetDeclaredConstructor/s也存在相同的差异。


//invoke问题

这特别麻烦,invoke(Object obj, Object... args)会让你感到棘手,如果你需要传递一个引用类型数组作为唯一参数,因为它可以转换成Object[],即使它应该被包装在new Object[1] 中。

您可以执行以下操作:

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

这将绕过可变参数机制。更简洁的写法是:

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

将类型转换为Object可以利用可变参数机制来为您创建数组。

当将null作为参数传递给可变参数时,也需要使用这个技巧,与反射无关。

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

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

@polygenelubricants,我的情况是:void foo()函数有参数Param... param(提示:Param是通用类型),而不是String... s。我使用了new Object[] {new URL("google.com")},但是出现了以下错误:IllegalArgumentException: argument 1 should have type java.lang.Object[],got java.net.URL。提前感谢您。 - MapleLover

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接