What are varargs?
Varargs are variable-length arguments: a feature that appeared in JDK5. Varargs allow us to create methods with an arbitrary number of arguments. By and large, it was possible to create such methods before. True, doing this was not very convenient. Let's give an example. Let's say we need to write a method that will take an arbitrary number of integer arguments and add them together. >We have two options. Option 1 - overload:class Calculator {
int sum(int a, int b){...};
int sum(int a, int b, int c){...};
int sum(int a, int b, int c, int d){...};
int sum(int a, int b, int c, int d, int e){...};
}
But there are two problems with overloading. Firstly, it is not always clear when it is time to stop, and secondly, it is cumbersome. Arrays are better.
Option 2 - arrays:
class Calculator {
int sum(int[] numbers){...};
}
With an array it’s no longer cumbersome, and the array seems to look like nothing... until it’s time to call the method:
public static void main(String... sss) {
Calculator calculator = new Calculator();
int[] arguments = new int[7];
arguments[0] = 1;
arguments[1] = 10;
arguments[2] = 123;
arguments[3] = 234;
arguments[4] = 6234;
arguments[5] = 12;
arguments[6] = 8;
int sum = calculator.sum(arguments);
}
We agree, you can, of course, write everything down more briefly. But firstly, I would still like to demonstrate the inconvenience of using arrays as variable-length arguments, and secondly, even if we write it like this:
int[] arguments = {1,10,123,234,6234,12,8};
int sum = calculator.sum(arguments);
Then we still won’t get rid of the excessive amount of code. However, with the release of Java 5, the Varargs feature appeared to solve these problems. It made it possible to pass an arbitrary number of arguments to methods. It looks something like this:
public class Calculator {
public static void main(String... sss) {
Calculator calculator = new Calculator();
int sum = calculator.sum(1,10,123,234,6234,12,8);
}
int sum(int... numbers){
return Arrays.stream(numbers).sum();
}
}
So, let's summarize. Varargs are variable-length arguments, a feature that appeared with the release of Java 5. Next, we will take a closer look at some rules for working with Varargs.5 rules of varargs
Rule 1. Vararg argument (or variable/arbitrary length argument) is indicated by an ellipsis as follows:String... words
Integer... numbers
Person... people
Cat... cats
Rule 2. An argument of arbitrary length can only be specified as an argument to some method:
void print(String... words)
int sum(Integer... numbers)
void save(Person... people)
void feed(Cat... cats)
Rule 3. Each such variable-length argument in the method body is an array:
void print(String... words){
for (int i = 0; i < words.length; i++) {
System.out.println(words[i]);
}
}
Rule 4. The Vararg argument must be the last one in the method argument list:
void print(String... words, String anotherWord)
void print(String... words, int someNumber)
void print(String anotherWord, String... words)
void print(int someNumber, String... words)
Rule 5: Even though varargs are arrays, when calling a method that takes variable-length arguments, it is not necessary to create an array. It is sufficient and even desirable to simply list the required arguments separated by commas:
public class Main {
public static void main(String... sss) {
print("How","well","great","learn","Java");
}
static void print(String... words){
for (int i = 0; i < words.length; i++) {
System.out.println(words[i]);
}
}
}
Examples of varargs
In the example below, we will write a method that takes varargs consisting of integers and displays the number of elements passed and their sum. Let's pass both an array and a series of integers to this method (both options are valid):public static void main(String... sss) {
int[] a = new int[100];
for (int i = 0; i < a.length; i++) {
a[i] = i;
}
sum(a);
sum(1,2,3,4,5,6,7,8,9,10);
}
static void sum(int... numbers){
final int length = numbers.length;
final int sum = Arrays.stream(numbers).sum();
final String lineSeparator = System.lineSeparator();
System.out.printf("Number of elements to add - %d, sum - %d%s", length, sum, lineSeparator);
}
After running the program will output:
Number of elements to add - 100, sum - 4950
Number of elements to add - 10, sum - 55
It's worth mentioning that the method System.out.printf
also accepts varargs. If we look at the code of this method, we will see this:
public PrintStream printf(String format, Object ... args) {
return format(format, args);
}
Another widely used method that accepts varags is String.format
. Its code is shown below:
public static String format(String format, Object... args) {
return new Formatter().format(format, args).toString();
}
When to use varargs?
The answer to this question depends on who is asking. If a similar question is asked by a client of some API that has methods with varargs, then the answer would be “use such methods as often as possible.” For the code client, varargs makes life a lot easier by making code easier to write and more readable. However, if this question is asked by an API developer who is wondering how often you should create methods with varargs, then the answer will be “you should not use varargs often.” Varargs should only be used when the benefit of its use is obvious. You should also avoid overloading methods with varargs, as this will make it difficult for clients of your code to understand which of the overloaded methods is actually called.Understanding Heap Pollution and Unsafe Varargs
So you’ve mastered the basics of varargs and how they simplify method signatures. Nice work! But did you know varargs can sometimes lead to tricky issues known as heap pollution? If you’re using generics with varargs, this is a topic you shouldn’t ignore. Let’s explore what this means and how you can keep your code safe.
What Is Heap Pollution?
Heap pollution occurs when a variable of a certain type references an object of an incompatible type. Sounds scary, right? Here’s how it happens with varargs: if you create a varargs method that takes generic types, the compiler quietly creates an array to hold those arguments. Because arrays are covariant and generics are invariant, this can produce a mismatch at runtime that the compiler doesn’t fully catch. That mismatch can lead to ClassCastException or other runtime errors—ouch!
For example, imagine you have a varargs method that’s supposed to take List<String>
arguments, but accidentally gets passed a List<Integer>
. The compiler might only give you a warning, but at runtime, you’re setting yourself up for potential crashes or weird behavior. That’s the essence of heap pollution.
Compiler Warnings About Unsafe Varargs
You might have seen compiler warnings like “Type safety: A generic varargs parameter...” or “Potential heap pollution via varargs parameter”. These warnings pop up because the compiler can’t guarantee type safety. The array used internally by varargs is less strict than generic types are supposed to be.
One way to address this is by annotating your methods with @SafeVarargs
(available since Java 7). This tells the compiler, “Trust me, I’ve ensured no heap pollution occurs here.” But be careful—only do this if you’re absolutely sure your code is safe!
Recommendations for Further Reading
If you’d like to dive deeper into the nitty-gritty details of heap pollution, unsafe varargs, and best practices, we recommend that you read the article in our blog Using Varargs, which dedicates sections to safe varargs usage. You’ll find in-depth explanations, real-world examples, and guidelines for writing robust code that avoids varargs pitfalls.
Conclusion
So, we've covered another topic, varargs in Java. We figured out what it is. We described the rules for using varargs. We looked at examples of methods with arbitrary length arguments, and also discussed when it is better to use varargs and when it is better to refrain from using them. As homework, you can implement the following methods:- Write
void
a method that takes a series of integers and returns their arithmetic mean. - Write
void
a method that takes a series of strings and prints the longest word. - Write a method that returns
boolean
and takes a variable of type as the first argumentString
, and a seriesboolean
of variable length as the next argument.
Possible valuesString
areAND
,OR
,XOR
. If the first argument has some other value, the method must throw an exceptionIllegalArgumentException
.
The method must perform a logical operation (specified in the first argument) on each element of the varargs argument and return the result of the calculation.
- "AND", true, true, false - will return false
- "OR", false, true, false, false - will return true
GO TO FULL VERSION