美文网首页
java反射调用可变长度参数的方法

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

作者: 多关心老人 | 来源:发表于2018-12-10 00:44 被阅读0次

    在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的过程中检查方法参数类型做转换吗?

    相关文章

      网友评论

          本文标题:java反射调用可变长度参数的方法

          本文链接:https://www.haomeiwen.com/subject/etklhqtx.html