Friday, December 21, 2007

Java 1.5 Explained - Varargs

Varargs pretty much does what is sounds like, it adds variable number of arguments support to Java methods. This is very similar to varargs in C and C++, but not exactly the same. Since many Java developers come from C or C++ backgrounds, support for varargs has probably been one of the top most requested features over the years. Along with varargs, Java 1.5 also added support for printf. I really haven't used printf much since I just got used to appending strings together and/or using StringBuffer.

If you read my other blog entry about autoboxing, you'll remember that the Java compiler inserted some extra byte code to autobox and unbox primitive types. Similar things are happing for varargs. Let's look at some code:
public class Varargs
{
public void m(String...args)
{
System.out.println(args[0] + " " + args.length);
}

public static void main(String[] args)
{
Varargs v = new Varargs();
v.m("hi", "there");
}
}
Above you'll see a method that takes a variable number of String objects and within the method you refer to the strings as if they were an array. Well, it turns out they really are an array. So then the call to method m from main must also be doing some array stuff. Let's take a look at the byte code:

public void m(java.lang.String[]);
Code:
0: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream;
3: new #3; //class java/lang/StringBuilder
6: dup
7: invokespecial #4; //Method java/lang/StringBuilder."":()V
10: aload_1
11: iconst_0
12: aaload
13: invokevirtual #5; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
16: ldc #6; //String
18: invokevirtual #5; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
21: aload_1
22: arraylength
23: invokevirtual #7; //Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
26: invokevirtual #8; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
29: invokevirtual #9; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
32: return

public static void main(java.lang.String[]);
Code:
0: new #10; //class Varargs
3: dup
4: invokespecial #11; //Method "":()V
7: astore_1
8: aload_1
9: iconst_2
10: anewarray #12; //class java/lang/String
13: dup
14: iconst_0
15: ldc #13; //String hi
17: aastore
18: dup
19: iconst_1
20: ldc #14; //String there
22: aastore
23: invokevirtual #15; //Method m:([Ljava/lang/String;)V
26: return
The first thing to notice is that the method m does take a String[] as an argument. That's what you would have normally used to pass a variable number of strings to a method. Now the compiler is doing this for you.

The next thing to notice is the byte code to call method m from main. You'll see that it's actually creating a String[] of size 2 and assigning the string constants "hi" and "there" to elements 0 and 1 respectively. That's really handy. The compiler writes all the array code for you.

One nice way to get used to programming with varargs is to start using the following:
public static void main(String... args)
instead of:
public static void main(String[] args)
String... and String[] are the same thing.