1. Introduction
1.介绍
Varargs were introduced in Java 5 and provide a short-hand for methods that support an arbitrary number of parameters of one type.
Vargs是在Java 5中引入的,它为支持一种类型的任意数量的参数的方法提供了一个简写。
In this article, we’ll see how we can use this core Java feature.
在这篇文章中,我们将看到如何使用这个核心的Java功能。
2. Before Varargs
2.在Varargs之前
Before Java 5, whenever we wanted to pass an arbitrary number of arguments, we had to pass all arguments in an array or implement N methods (one for each additional parameter):
在Java 5之前,只要我们想传递任意数量的参数,我们就必须在一个数组中传递所有的参数,或者实现N个方法(每个额外参数一个)。
public String format() { ... }
public String format(String value) { ... }
public String format(String val1, String val2) { ... }
3. Use of Varargs
3.使用Varargs
Varargs help us avoid writing boilerplate code by introducing the new syntax that can handle an arbitrary number of parameters automatically – using an array under the hood.
Varargs通过引入能够自动处理任意数量参数的新语法,帮助我们避免编写模板代码–在引擎盖下使用一个数组。
We can define them using a standard type declaration, followed by an ellipsis:
我们可以用一个标准的类型声明来定义它们,后面加一个省略号。
public String formatWithVarArgs(String... values) {
// ...
}
And now, we can call our method with an arbitrary number of arguments, like:
而现在,我们可以用任意数量的参数来调用我们的方法,比如。
formatWithVarArgs();
formatWithVarArgs("a", "b", "c", "d");
As mentioned earlier, varargs are arrays so we need to work with them just like we’d work with a normal array.
如前所述,varargs是数组,所以我们需要像处理普通数组一样来处理它们。
4. Rules
4.规则
Varargs are straightforward to use. But there’re a few rules we have to keep in mind:
Vargs使用起来很直接。但有几条规则我们必须牢记。
- Each method can only have one varargs parameter
- The varargs argument must be the last parameter
5. Heap Pollution
5.垃圾堆污染
Using varargs can lead to so-called Heap Pollution. To better understand the heap pollution, consider this varargs method:
使用varargs会导致所谓的堆污染。为了更好地理解堆污染,请考虑这个varargs方法。
static String firstOfFirst(List<String>... strings) {
List<Integer> ints = Collections.singletonList(42);
Object[] objects = strings;
objects[0] = ints; // Heap pollution
return strings[0].get(0); // ClassCastException
}
If we call this strange method in a test:
如果我们在一个测试中调用这个奇怪的方法。
String one = firstOfFirst(Arrays.asList("one", "two"), Collections.emptyList());
assertEquals("one", one);
We would get a ClassCastException even though we didn’t even use any explicit type casts here:
我们会得到一个ClassCastException ,尽管我们在这里甚至没有使用任何显式类型的转换:。
java.lang.ClassCastException: class java.lang.Integer cannot be cast to class java.lang.String
5.1. Safe Usage
5.1.安全使用
Every time we use varargs, the Java compiler creates an array to hold the given parameters. In this case, the compiler creates an array with generic type components to hold the arguments.
每次我们使用varargs时,Java编译器都会创建一个数组来保存给定的参数。在这种情况下,编译器会创建一个具有通用类型组件的数组来保存参数。
When we use varargs with generic types, as there’s a potential risk of a fatal runtime exception, the Java compiler warns us about a possible unsafe varargs usage:
当我们将varargs与通用类型一起使用时,由于存在致命的运行时异常的潜在风险,Java编译器会警告我们可能存在不安全的varargs用法。
warning: [varargs] Possible heap pollution from parameterized vararg type T
The varargs usage is safe if and only if:
varargs的使用是安全的,当且仅当:
- We don’t store anything in the implicitly created array. In this example, we did store a List<Integer> in that array
- We don’t let a reference to the generated array escape the method (more on this later)
If we are sure that the method itself does safely use the varargs, we can use @SafeVarargs to suppress the warning.
如果我们确定方法本身确实安全地使用了varargs,我们可以使用@SafeVargs来抑制该警告。。
Put simply, the varargs usage is safe if we use them to transfer a variable number of arguments from the caller to the method and nothing more!
简单地说,如果我们使用varargs来将可变数量的参数从调用者转移到方法中,而不是其他的,那么varargs的使用就是安全的。
5.2. Escaping Varargs Reference
5.2 转移Vargs引用
Let’s consider another unsafe usage of varargs:
让我们考虑一下varargs的另一种不安全用法。
static <T> T[] toArray(T... arguments) {
return arguments;
}
At first, it might seem that the toArray method is completely harmless. However, because it let the varargs array escape to the caller, it violates the second rule of safe varargs.
乍一看,toArray方法似乎完全无害。然而,由于它让varargs数组逃到了调用者那里,所以它违反了安全varargs的第二条规则。
To see how this method can be dangerous, let’s use it in another method:
为了了解这种方法的危险性,让我们在另一种方法中使用它。
static <T> T[] returnAsIs(T a, T b) {
return toArray(a, b);
}
Then if we call this method:
那么如果我们调用这个方法。
String[] args = returnAsIs("One", "Two");
We would, again, get a ClassCastException. Here’s what happens when we call the returnAsIs method:
我们将再次得到一个ClassCastException。下面是我们调用returnAsIs方法时发生的情况。
- To pass a and b to the toArray method, Java needs to create an array
- Since the Object[] can hold items of any type, the compiler creates one
- The toArray method returns the given Object[] to the caller
- Since the call site expects a String[], the compiler tries to cast the Object[] to the expected String[], hence the ClassCastException
For a more detailed discussion on heap pollution, it’s highly recommended to read item 32 of Effective Java by Joshua Bloch.
关于堆污染的更详细讨论,强烈建议阅读Joshua Bloch撰写的Effective Java第32项。
6. Conclusion
6.结论
Varargs can make a lot of boilerplate go away in Java.
Varargs可以使Java中的很多模板消失。
And, thanks to their implicit autoboxing to and from Array, they play a role in future-proofing our code.
而且,由于其隐含的自动框定进出Array,它们在保护我们的代码方面发挥了作用。
As always, all code examples from this article can are available in our GitHub repository.
一如既往,本文的所有代码示例都可以在我们的GitHub仓库中找到。