Java中的可变参数(Varargs)

1. 简介
可变参数(Varargs)在Java 5中被引入,它为支持一种类型的任意数量参数的方法提供了一种简便写法。在本文中,我们将了解如何使用这个Java核心特性。

2. 在可变参数(Varargs)之前
在Java 5之前,每当我们想要传递任意数量的参数时,我们必须将所有参数放在一个数组中传递,或者实现N个方法(每增加一个参数就实现一个方法):

public String format() {... }
    public String format(String value) {... }
    public String format(String val1, String val2) {... }

3. 可变参数(Varargs)的使用
可变参数(Varargs)通过引入能够自动处理任意数量参数的新语法(在底层使用数组),帮助我们避免编写样板代码。我们可以使用标准类型声明,后跟省略号(...)来定义它们:

public String formatWithVarArgs(String... values) {
        //...
    }

现在,我们可以使用任意数量的参数来调用我们的方法,例如:

formatWithVarArgs();
    formatWithVarArgs("a", "b", "c", "d");

如前所述,可变参数(Varargs)就是数组,所以我们需要像处理普通数组一样处理它们。

4. 规则
可变参数(Varargs)使用起来很简单,但我们需要记住一些规则:

  • 每个方法只能有一个可变参数(Varargs)参数
  • 可变参数(Varargs)参数必须是最后一个参数

5. 堆污染
使用可变参数(Varargs)可能会导致所谓的堆污染。为了更好地理解堆污染,请考虑这个可变参数(Varargs)方法:

static String firstOfFirst(List<String>... strings) {
        List<Integer> ints = Collections.singletonList(42);
        Object[] objects = strings;
        objects[0] = ints; // 堆污染
        return strings[0].get(0); // 类转换异常
    }

如果我们在测试中调用这个奇怪的方法:

String one = firstOfFirst(Arrays.asList("one", "two"), Collections.emptyList());
    assertEquals("one", one);

我们将会得到一个类转换异常:

java.lang.ClassCastException: class java.lang.Integer不能转换为class java.lang.String

5.1. 安全使用
每次我们使用可变参数(Varargs)时,Java编译器都会创建一个数组来保存给定的参数。在这种情况下,编译器会创建一个具有泛型类型组件的数组来保存参数。当我们将可变参数与泛型类型一起使用时,由于存在潜在的运行时异常,Java编译器会警告可能存在不安全的可变参数使用:

警告: [varargs] 来自参数化可变参数类型T的可能的堆污染

5.2. 可变参数引用的逃逸
让我们考虑可变参数的另一种不安全用法:

static <T> T[] toArray(T... arguments) {
        return arguments;
    }

6. 结论
在Java中,可变参数(Varargs)可以消除大量样板代码,简化代码结构。

若你想提升Java技能,可关注我们的Java培训课程。