Wednesday, March 12, 2008

Java 1.5 Explained - Enum

Before Java 1.5, you'd probably write code like the following to implement enumerated types, similar to how it would be done in C using #define:
public final static int SUIT_CLUBS = 1;
public final static int SUIT_DIAMONDS = 2;
public final static int SUIT_HEARTS = 3;
public final static int SUIT_SPADES = 4;

Sure this is a little better than #define, but not much better. Java 1.5 enumerated types are similar to what's available in C++. Here's a C++ example:

enum Suit {
CLUBS = 1,
DIAMONDS = 2,
HEARTS = 3,
SPADES = 4
};
In Java this would be:
enum Suit {
CLUBS(1),
DIAMONDS(2),
HEARTS(3),
SPADES(4)
};
There's a subtle different between the Java and C++ syntax. Java is using parens to initialize the individual enumerated type entries to a specific value. What's really going on here? Let's take a look a some byte code.

First, here is a complete sample of some Java code using enum:
public class Enum
{
public enum Direction { NORTH, SOUTH, EAST, WEST };

public static void main(String[] args)
{
Direction d = Direction.EAST;
System.out.println(d);
}
}
And the corresponding byte code:
public class Enum extends java.lang.Object{
public Enum();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."":()V
4: return

public static void main(java.lang.String[]);
Code:
0: getstatic #2; //Field Enum$Direction.EAST:LEnum$Direction;
3: astore_1
4: getstatic #3; //Field java/lang/System.out:Ljava/io/PrintStream;
7: aload_1
8: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/Object;)V
11: return
}
Look at line 0 in the main method. The Direction enum is really a Java class (in this case an inner class within this Enum class example) and it has a static field named EAST. Looking a little closer you can see that the field EAST is also Direction object. That explains the reason for having the parens to initialize the enumerated value. It's actually passing the value to a constructor. Now here's the bytecode for the Direction class that was automatically created:

public final class Enum$Direction extends java.lang.Enum{
public static final Enum$Direction NORTH;

public static final Enum$Direction SOUTH;

public static final Enum$Direction EAST;

public static final Enum$Direction WEST;

public static final Enum$Direction[] values();
Code:
0: getstatic #1; //Field $VALUES:[LEnum$Direction;
3: invokevirtual #2; //Method "[LEnum$Direction;".clone:()Ljava/lang/Object;
6: checkcast #3; //class "[LEnum$Direction;"
9: areturn

public static Enum$Direction valueOf(java.lang.String);
Code:
0: ldc_w #4; //class Enum$Direction
3: aload_0
4: invokestatic #5; //Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
7: checkcast #4; //class Enum$Direction
10: areturn

static {};
Code:
0: new #4; //class Enum$Direction
3: dup
4: ldc #7; //String NORTH
6: iconst_0
7: invokespecial #8; //Method "":(Ljava/lang/String;I)V
10: putstatic #9; //Field NORTH:LEnum$Direction;
13: new #4; //class Enum$Direction
16: dup
17: ldc #10; //String SOUTH
19: iconst_1
20: invokespecial #8; //Method "":(Ljava/lang/String;I)V
23: putstatic #11; //Field SOUTH:LEnum$Direction;
26: new #4; //class Enum$Direction
29: dup
30: ldc #12; //String EAST
32: iconst_2
33: invokespecial #8; //Method "":(Ljava/lang/String;I)V
36: putstatic #13; //Field EAST:LEnum$Direction;
39: new #4; //class Enum$Direction
42: dup
43: ldc #14; //String WEST
45: iconst_3
46: invokespecial #8; //Method "":(Ljava/lang/String;I)V
49: putstatic #15; //Field WEST:LEnum$Direction;
52: iconst_4
53: anewarray #4; //class Enum$Direction
56: dup
57: iconst_0
58: getstatic #9; //Field NORTH:LEnum$Direction;
61: aastore
62: dup
63: iconst_1
64: getstatic #11; //Field SOUTH:LEnum$Direction;
67: aastore
68: dup
69: iconst_2
70: getstatic #13; //Field EAST:LEnum$Direction;
73: aastore
74: dup
75: iconst_3
76: getstatic #15; //Field WEST:LEnum$Direction;
79: aastore
80: putstatic #1; //Field $VALUES:[LEnum$Direction;
83: return
}
There's a lot going on in Direction. It's really just a bunch of code which could have been written by hand prior to Java 1.5, but now the compiler is doing all the work for us. The code is basically doing this:
public class Direction extends java.lang.Enum
{
public final static Direction NORTH;
public final static Direction SOUTH;
public final static Direction EAST;
public final static Direction WEST;

static
{
NORTH = new Direction("NORTH", 0);
SOUTH = new Direction("SOUTH", 1);
EAST = new Direction("EAST", 2);
WEST = new Direction("WEST", 3);

VALUES = new Direction[4];
VALUES[0] = NORTH;
VALUES[1] = SOUTH;
VALUES[2] = EAST;
VALUES[3] = WEST;
}
}
How about that? All that code came from pretty much one line of code in Java 1.5. Notice also that Direction extends a new class in Java 1.5, java.lang.Enum. This new class is where the String and oridinal values for the enumerated type are stored. Now you can see where the nice toString() comes from with enumerated types. In our example above, the System.out.println(d) will print "EAST". The Enum.ordinal() method can be used to get the ordinal value as well.

There's a lot more you can do with enum, like add your own methods that will be included in the class that's automatically generated. Please see Sun's Enum page for more information.

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.

Thursday, December 20, 2007

Java 1.5 Explained - Autoboxing

Here's the start of a short series on how many of the new language features were added in in Java 1.5. I'm going to start with Autoboxing. Autoboxing is language feature that allows you to write code using primitive types where previously you had to use the object equivalent. For example, the primitive type int not the same as Integer. You can convert between the two manually but that can be cumbersome. There's also extra code required when you're dealing with lists, sets, maps, etc., since you cannot store primitive types in collections.

Let's look at some code:
List list = new ArrayList();
list.add(100); // instead of list.add(new Integer(100));
Adding a primitive int to a list is now super easy.

So how is this actually working? Primitive types are not magically objects in Java 1.5. The 1.5 compiler is doing tricks behind the scenes to make writing code easier. Let's take a look:
List list = new ArrayList();
list.add(100);
list.add(200);
int i = list.get(0);
Integer j = list.get(0);
This code adds two primitive ints to a list and then gets them in two different ways. The compiler generates the following byte code:
0: new #2; //class java/util/ArrayList
3: dup
4: invokespecial #3; //Method java/util/ArrayList."":()V
7: astore_1
8: aload_1
9: bipush 100
11: invokestatic #4; //Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
14: invokeinterface #5, 2; //InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
19: pop
20: aload_1
21: sipush 200
24: invokestatic #4; //Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
27: invokeinterface #5, 2; //InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
32: pop
33: aload_1
34: iconst_0
35: invokeinterface #8, 2; //InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;
40: checkcast #9; //class java/lang/Integer
43: invokevirtual #10; //Method java/lang/Integer.intValue:()I
46: istore_2
47: aload_1
48: iconst_0
49: invokeinterface #8, 2; //InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;
54: checkcast #9; //class java/lang/Integer
57: astore_3
Interesting things start on line 11. The compiler added some extra code to call Integer.valueOf(100) so the 100 value can be added to the list. It's the same code you'd normally have to write yourself, but now autoboxing is doing it for you.

Next look at line 35. The generated code is calling the list get method and returning an object. The object is then cast to an Integer and the intValue method is called to obtain the primitive type. Again, it's the same code you'd have to write but you don't have to anymore.

You can compare the byte code for the list.get(0) that returns and Integer. It's the same byte code generated for autoboxing.

You'll see there's some use of generics here since the list.get(0) doesn't have to do any casting to an Integer. I'll cover generics in detail later. That's about it for autoboxing. Go back to enjoying using primitive types and not having to manually create all those Integer and Long objects.

Monday, December 10, 2007

Convert Canon SD600 videos for the Apple TV

The Canon PowerShot SD600 captures video in the following format:

RIFF (little-endian) data, AVI, 640 x 480, 30.00 fps, video: Motion JPEG, audio: uncompressed PCM (mono, 11024 Hz)

Note the fps and resolution can be changed within the camera video settings.

Turns out the SD600 AVI files are not one of the Apple TV supported video formats. I've converted other AVI files to MPEG-4 for the ipod using ffmpeg so I decided to give that a whirl. Some of the files worked while others would display an error in itunes about still not being a supported video format. I read the Apple TV specs page again and noticed the bit about up to 3 Mbps. I guess depending on the video content, ffmpeg was outputting a bitrate higher than 3 Mbps for some videos. The following command is what finally worked for me. You can see the -b option to specify desired bitrate. The actual bitrate in the MPEG-4 files will vary.
ffmpeg -i input.avi -f mp4 -r 30 -b 2Mbps -vcodec mp4 -acodec libfaac ouput.mp4
I've tried this on windows and linux.