One particularly nice feature about varargs is that a variable-length argument can take from zero to n arguments. This means that you can actually invoke one of these methods without any parameters, and things still behave. On the other hand, this means that, as a programmer, you better realize you must safeguard against this condition.
Remember in "Iterating Over Variable-Length Argument Lists," you saw this simple method:
public static int max(int first, int... rest) { int max = first; for (int i : rest) { if (i > max) max = i; } return max; }
You can call this method in several ways:
int max = MathUtils.max(1, 4); int max = MathUtils.max(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); int max = MathUtils.max(18, 8, 4, 2, 1, 0);
What's not so nice is that there are many cases where you may already have the numbers to pass in stored as an array, or at least in some collected form:
// Get the numbers from some method int[] numbers = getListOfNumbers( );
It's impossible to just pass these numbers on to the max( ) method. You would need to check the list length, and strip off the first object (if it's available), then check the type to ensure it's an int. That would be passed in, along with the rest of the array (which can be iterated over, or converted manually to a suitable format). In general, this process is a real pain and is a lot of work for what should be trivial. To get around this, remember that this method is treated by the compiler as the following:
public static int max(int first, int[] rest)
NOTE
Autounboxing helps some, as "Integer" objects are freely converted to``` "int" primitives. Autounboxing is covered in Chapter 4.
So, by extension, you could convert max( ) to look like this:
public static int max(int... values) { int max = Integer.MIN_VALUE; for (int i : values) { if (i > max) max = i; } return max; }
You've now created a method that can easily be used with arrays:
// Get the numbers from some method int[] numbers = getListOfNumbers( ); int max = MathUtils.max(numbers);
While using a single variable-length argument made this task easier, it introduces problems if you pass in a zero-length arrayin the best case, you're going to get unexpected results. To account for this, you now need a little error checking. Example 5-3 is a complete code listing for the MathUtils class, which at this point is more of a MathUtil class!
package com.oreilly.tiger.ch05; public class MathUtils { public static int max(int... values) { if (values.length == 0) { throw new IllegalArgumentException("No values supplied."); } int max = Integer.MIN_VALUE; for (int i : values) { if (i > max) max = i; } return max; } }
Anytime you have the possibility for a zero-length argument list, you need to perform this type of error checking. Generally, a nice informative IllegalArgumentException is a great solution.
...invoking this same method with normal non-array arguments? That's perfectly legal, of course. The following are all legitimate ways to invoke the max( ) method:
int max = MathUtils.max(myArray); int max = MathUtils.max(new int[] { 2, 4, 6, 8 }); int max = MathUtils.max(2, 4, 6, 8); int max = MathUtils.max(0); int max = MathUtils.max( );
NOTE
Whatever you do, please don't throw a checked exceptionyou just add hassle for programmers using your code, and for what is a fringe case, rather than a normal problem.