Java Secret: More uses for varargs
Overview
Varargs have many uses from simplifying code. However, they are not always used as often as they could be.Use in reflections
Calling a method via reflections is fairly ugly without varargs.ClassLoader cl = Thread.currentThread().getContextClassLoader(); Method defineClass = cl.getClass().getDeclaredMethod("defineClass", new Class[] { String.class, byte[].class, int.class, int.class}); defineClass.setAccessible(true); defineClass.invoke(cl, new Object[] { myNewClassName, myByteCode, 0, myByteCode.length });
With varargs, the code appears cleaner.
ClassLoader cl = Thread.currentThread().getContextClassLoader(); Method defineClass = cl.getClass().getDeclaredMethod( "defineClass", String.class, byte[].class, int.class, int.class); defineClass.setAccessible(true); defineClass.invoke(cl, myNewClassName, myByteCode, 0, myByteCode.length);
Use of varargs for optional arguments
System.out.printf use varargs which might be optional.System.out.printf("Hello%n"); // no args to the varargs. System.out.printf("Hello %s%n", name); // one argument to the varargs. System.out.printf("Hello %s you are %d years old.%n", name, age); // two arguments
When varargs must have a minimum of one entry
The minimum number of entries for a varargs is 0, so if you need to have at least 1, you can add a parameter.public static int average(int num, int... nums) { long total = num; for(int n: nums) total += n; return total/(nums.length+1); }This method will fail at runtime when no arguments are provided.
int a = average(); // fails to compile, instead of failing at runtime.
Use of varargs to build a List
A commonly used method is Arrays.asList(T...) The first example uses a varargs to build an array, the second example takes and array and passes it one as expected.List<Integer> ints = Arrays.asList(1,2,3,4,5); List<String> strings = Arrays.asList("one,two,three,four,five".split(","));
Use of varargs to build a Map
You can also use varargs to help build a map.public static <K, V> Map<K, V> mapOf(K key, V value, Object... alteratingsKeysAndValues) { Map<K, V> map = new LinkedHashMap<K, V>(); map.put(key, value); for(int i = 0; i < alteratingsKeysAndValues.length; i += 2) map.put((K) alteratingsKeysAndValues[i], (V) alteratingsKeysAndValues[i+1]); return map; } Map<String, Integer> smallNumbers = mapOf("zero", 0, "one", 1, "two", 2);
So, we are using Java 5 varargs but not Java 5 generic Lists and Maps? Not worth the cost. I would rather have strongly types containers with a little more init code than the opposite.
ReplyDelete@Greg, I agree that using generics is best practice. Unfortunately I forgot that this blog treats generics types as HTML. ;)
ReplyDeletePractical hints. Lists and Maps constructors are implemented conveniently in Google Guava.
ReplyDeleteAnd Google Guava is type safe (generics) as well.
ReplyDeleteNice article.
ReplyDeleteUnfortunately the last example with a map is not a good way of coding. You'll easy can get it work wrong with ClassCastException. So, I wouldn't pass it on code review ;)
SLF4J could make use of varargs:
ReplyDeletelogger.debug("{},{},{},{}",1,2,3,4);
@Stjepan, It could use varargs to simplify the code. However with debug messages, there can be alot of them and they may not be enabled. i.e. you want them to have as little performance impact as possible. Unfortunately varargs creates an object even if it is immediately discarded.
ReplyDelete@1ndigo, The last example is typically used to build a static collection, i.e. if it works the first time it is used e.g. in a unit test, it will continue to run correctly.
ReplyDeleteThere are many ways to meaningfully validate a system before it is deployed.
Variable argument is indeed a very interesting feature but still under utilized. you know even main method is written using varargs. by the way I also blogged my experience as variable arguments in java let me know how do you find it
ReplyDelete