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

相关文章

  • 反射

    反射 第一节:可变参数 从JDK 5开始,Java 允许为方法定义长度可变的参数。 语法: 注意事项: 调用可变参...

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

    在java中可以用可变长度参数,这样可以给调用者更大的灵活度,可变长度参数在编译后就是一个数组参数,不用担心可变长...

  • 反射机制(四)操作方法

    获取方法: 调用方法 问题一答案: 使用反射调用可变参数 使用反射调用静态方法: 静态方法不属于任何对象,属于类本...

  • scala 集合转java可变参数

    今天用flink写redis,调用sadd(key,values)方法因为values是java的可变参数,但是我...

  • Java 反射

    Java反射Java反射API获取Class对象通过反射创建实例对象,调用公共方法通过反射调用私有方法 一.Jav...

  • 参数

    可变参数 Java声明函数是可通过int ... num 等方式声明可变长度的参数,用于传输参数个数不固定的场景,...

  • 大连滕泰科技学习笔记2020-06-15

    1,什么是可变参数? 2,main方法如何进行传递参数 3,反射定义 定义:在java 虚拟机中运行的时候,情况1...

  • JavaSE之方法

    五、方法 目录:何谓方法、方法的定义及调用、方法重载、命令行传参、可变参数、递归 1.什么是方法? Java方法是...

  • Java语法糖之可变长度参数

    可变长度参数原理 反编译class文件: 可变长度参数只能作为方法中的最后一个参数,而且只能有一个。 总结 Jav...

  • 可变参数

    可变参数 格式: 可变参数注意事项: 如果一个函数的参数使用了可变参数,那么调用该方法时可以传递参数也可以不传递。...

网友评论

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

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