Java 可变参 Object...objects 方法的陷进

Java 的可变参数(Varargs) 方法让我们调用起来很方便,不需要总是去构造一个数组来传递不定数量的参数,而且还可以作为方法的一个可选参数,如

void foo(int id, String...name) {
    String yourName = name.length == 0 ? "Anonymous" : name[0];
        ......
}

想要告诉名字就调用 foo(1, "Yanbin"), 不想的话就用 foo(1).

但我们在使用 Java Varargs 时,当变参类型定义为 Object...objects 时就要当心了,因为 Object 类型的包容性原因一不小心就可以掉到坑里去了,例如下面的方法

void foo(Object...objects) {
     Arrays.stream(objects).forEach(System.out::println());
}

当引用类型是 Object[] 时调用没问题,下面代码调用可以得到预期的结果

Object[] input = new Object[]{"1", "2"};
foo(input);

输出为

1
2

可是把引用类型变成 Object[]  后可能就要引发 Bug 了,看下面代码的输出

Object input = new Object[]{"1", "2"};
foo(input);

输出类似如下

[Ljava.lang.Object;@5d099f62

foo(Object...objects) 只识别到了一个参数,它把 input 整体作为可变参 objects 的第一个元素,因为数组 Object[] 也是一个 Object 实例.

这里像是一个多态的情形,行为应该是运行时决定的,与引用类型应无关, 假乎应该类似如下的情型

void foo(Animal animal) {...}

Animal animal = new Dog();
foo(animal);  //1

Dog dog = new Dog();
foo(dog);  //2

//1 和 //2 两种调用方式的行为应该是一样的。

但它们又是不一样的,前面代码

Object input = new Object[] {"1", "2"};
foo(input);  //3

更像是在让 foo(input) 调用从两个重载方法

foo(Object object);
//和
foo(Object...objects);

中选择一个更匹配的方法来调用,所以第一个胜出,因为重载方法的匹配是依据引用类型的

//3 所示的情况可能较少出现,可有时候我们依赖于一个方法来获得 Object[] 类型返回值时就得当心啦,例如我们放大一个方法的返回值类型为 Object 来适应可能的多种类型返回值

Object bar(int type) {
    if(type == 1) {
        return new Object[]{"33", "44"};
    } else  {
        return "Hello";
    }

然后我们心里很清在传入 1 时可以得到一个 Object[]  类型返回值,所以想当然的调用 foo() 方法,像

foo(bar(1))

于是就掉到陷进里了,把 new Object[]{"33", "44"} 以一个整体作为了 Object...objects 的第一个元素了,输出了 [Ljava.lang.Object;@31befd9f  这样的内容,而不是遍历这个数组。

为避免上面的潜在问题,当参数的引用类型是 Object 的对象数组需要显式的转型

foo((Object[]) input);
foo((Object[] bar(1));

就像是我们用整型参数想要匹配长整型重载方法时的做法

foo(int number);  //a
foo(long number); //b

foo((long)100); //这样虽然  100 是一个整型数,显式转型就能调用到 //b 方法。

本文所述及的陷进也就是在变参类型为 Object...objects 才会出现的,要是别的类型倒不用担心,如 String...strings 是不可能把传入的数组整体作为第一个参数的,因为 String 容不下数组的。

本文的情型有些类似于 Scala 使用变参的方法, 请参考 Java 和 Scala 调用变参的方式,只是 Scala 的要求更为苛刻。

本文链接 https://yanbin.blog/java-varargs-method-error-prone/, 来自 隔叶黄莺 Yanbin Blog

[版权声明] Creative Commons License 本文采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可。

Subscribe
Notify of
guest

0 Comments
Inline Feedbacks
View all comments