0
点赞
收藏
分享

微信扫一扫

java反射调用可变长度参数的方法

芭芭蘑菇 2021-09-21 阅读 75
我的博客

在java中可以用可变长度参数,这样可以给调用者更大的灵活度,可变长度参数在编译后就是一个数组参数,不用担心可变长度参数是null的问题,不传的话就是一个长度为0的数组,可变长度参数只能是最后一个参数。
但是在用反射调用有可变长度参数的方法时要注意,如下图:

public String testa(Object... args){
        for (Object arg : args) {
            System.out.println(arg);
        }
        return "a";
    }

    @Test
    public void test28() throws InvocationTargetException, IllegalAccessException {
        Method method = ReflectionUtil.getMethodByName(NormalTest.class, "testa");
        //报错 argument type mismatch
//      method.invoke(this, 123);
        //报错 argument type mismatch
//      method.invoke(this, new Object[]{123});
        //报错 wrong number of arguments
//      method.invoke(this, new Object[]{123, 456});
        //报错 argument type mismatch
//      method.invoke(this, new Object[]{new int[]{123, 456}});
        //正确
        method.invoke(this, new Object[]{new Integer[]{123, 456}});
    }

从上面看出参数要被包成数组2次,至于为什么2次不明白。

但是下面的例子又说明参数包装成1次也是可以的,如下:

    public String testa(int a, int b) {
        System.out.println(a + b);
        return "testa";
    }

    public String testb(int... a){
        for (int i : a) {
            System.out.println(i);
        }
        return "testb";
    }

    public String testc(int a, int b, int... c){
        System.out.println(a + b);
        for (int i : c) {
            System.out.println(i);
        }
        return "testc";
    }

    @Test
    public void test28() throws InvocationTargetException, IllegalAccessException {
        Method testa = ReflectionUtil.getMethodByName(NormalTest.class, "testa");
        //参数个数固定,直接给参数
        System.out.println(testa.invoke(this, 1, 2));
        Method testb = ReflectionUtil.getMethodByName(NormalTest.class, "testb");
        //方法参数是可变参数,需要包2层数组
        System.out.println(testb.invoke(this, new Object[]{new int[]{1,2}}));
        //正确,和上面结果一样,但是有的情况下需要包2层数组,这个包一层也可以,不知道原因
        System.out.println(testb.invoke(this, new int[]{1,2}));
        //直接给参数,报wrong number of arguments
//      System.out.println(testb.invoke(this, 1, 2));
        //报错 argument type mismatch
//      System.out.println(testb.invoke(this, 1));
        Method testc = ReflectionUtil.getMethodByName(NormalTest.class, "testc");
        //正确
        System.out.println(testc.invoke(this, 1, 2, new int[]{1, 2}));
        //报错 argument type mismatch
        System.out.println(testc.invoke(this, 1, 2, new Object[]{new int[]{1,2}}));
    }

为什么通过反射就要包装成数组,甚至包装2次,不明白。


参考这个作者的回答:https://stackoverflow.com/questions/53694634/call-variable-argument-method-by-reflection
Method.invoke(object, Object...)本身就需要将所有参数封装成一个Object数组,而方法可变长度参数也只是数组参数的语法糖而已,因此要想通过反射调用testa(Object... args)test(Object[] args)就必须用new Object[]{new Object[]{}},最外层Object数组是给Method.invoke用的,最里层数组是你的方法需要的参数。

再说另一个问题,数组是可以协变的,即Object[] objects = new Integer[1];是合法的,但是Object[] objects = new int[1];或Integer[] ints = new int[1];是非法的。

在调用可变长度参数时,如果你给的参数类型和方法参数类型相同或可协变的或者可转换的,则直接把你的参数转换后传递给方法参数,如果不能转换,会在外层包装一层Object数组,把你的参数作为Object数组中的元素传给方法参数。看下图:

    public String testa(Object... args){
        for (Object arg : args) {
            System.out.println(arg);
        }
        return "a";
    }

    public String testb(int... args){
        for (Object arg : args) {
            System.out.println(arg);
        }
        return "b";
    }

    @Test
    public void test28() throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {
        this.testa(123);
        //int[]不能转换成Object[],因此会被包在Object[]里面
        this.testa(new int[]{123});
        //这种写法和上面是一样的
        this.testa(new Object[]{new int[]{123}});
        this.testa(new Integer[]{123});
        this.testa(new Object[]{123});
        System.out.println("======");
        this.testb(123);
        //int[]能直接转换为方法需要的参数
        this.testb(new int[]{123});
        //编译报错
//      this.testb(new Integer[]{123});
    }

打印的结果是

123
[I@783e6358
[I@17550481
123
123
======
123
123

疑问:

public void testa(Object... args){
}

public void testa(Object[] args){
}

在编译后都会变成testa(Object[] args)这种形式,为什么在调用的时候testa()调用可变长度参数方法编译通过,调用数组参数编译报错,是在javac的过程中检查方法参数类型做转换吗?

举报

相关推荐

0 条评论