The Race Moves up a notch

by Ted Neward

Sun released another early-access version of the JSR 14 "Generics for Java" implementation recently, and can be found at http://developer.java.sun.com/developer/earlyAccess/adding_generics/. (The main JDC page claims that the last release was 1.2, on March 21; that's incorrect. If you click through the link to the URL above, it reads as 1.3, released on October 30.)

Using the 1.3 release is a bit awkward, since the release itself ships as two .jar files, one for the compiler and another for the java.util Collection classes (properly genericized). This means, unfortunately, that you'll need to put the javac.jar compiler code somewhere in the bootclasspath of the javac.exe tool when you run it, and reference the collect.jar as part of the bootclasspath when you compile and run the resulting code. The 1.3 release ships with some UNIX and Win32 script/batch files to help ease this pain, but if you're planning to do any serious work with the release, I'd suggest just writing your own quick-and-dirty JNI Invocation launcher to get all the options in the right place; details on how to do so can be found in a variety of different JNI books, as well as my own (now horribly outdated) Server-Based Java Programming. If I get around to it, I'll write one myself and post it here.



One nice thing about the JSR EA implementation is that once you get past the compilation phase (and if you don't use any of the generic collection classes in collect.jar), you can run the compiled code on preexisting VMs--there's no structural changes to the code made when compiling generics into standard Java classes. So, for example, taking this code:


class Generic
{
T data;

public Generic(T d) { data = d; }
}

public class GenTest
{
public static void main(String[] args)
{
Generic gs = new Generic("Hello, world");
System.out.println(gs.data);
}
}


and compiling it in the genericized Javac (which I'll call gjavac from here on out) yields "Generic.class" and "GenTest.class".



One of the goals for the JSR 14 was "designed to be fully backwards compatible with the current language, making the transition from non-generic to generic programming very easy." Part of this means that there can be no structural changes to the underlying JVM that will be used to execute the code. (This is in direct contrast to the Gyro release from Microsoft--it patches the CLR to recognize generic types within the runtime. This pretty accurately reflects the differences in philosophy between Sun and Microsoft--Sun believes in "one language", and Microsoft believes in "multiple languages".) Sure enough, if we disassemble the compiled Generic class, we see


Compiled from GenTest.java
class Generic extends java.lang.Object {
java.lang.Object data;
public Generic(java.lang.Object);
}

Method Generic(java.lang.Object)
0 aload_0
1 invokespecial #1 <Method java.lang.Object()>
4 aload_0
5 aload_1
6 putfield #2 <Field java.lang.Object data>
9 return


As you can see, the underlying data type for "data", which was represented in the source as the type variable "T", is a java.lang.Object reference. Similarly, using the parameterized Generic behaves much as you'd expect (once again, consulting javap for a bytecode disassembly):


Compiled from GenTest.java
public class GenTest extends java.lang.Object {
public GenTest();
public static void main(java.lang.String[]);
}

Method GenTest()
0 aload_0
1 invokespecial #1 <Method java.lang.Object()>
4 return

Method void main(java.lang.String[])
0 new #2 <Class Generic>
3 dup
4 ldc #3 <String "Hello, world">
6 invokespecial #4 <Method Generic(java.lang.Object)>
9 astore_1
10 getstatic #5 <Field java.io.PrintStream out>
13 aload_1
14 getfield #6 <Field java.lang.Object data>
17 checkcast #7 <Class java.lang.String>
20 invokevirtual #8 <Method void println(java.lang.String)>
23 return


The "smoking gun", as it were, is on disassembly lines 14 and 17: the runtime will retrieve the data as an Object, then cast it down to a String (the parameterized type) at runtime. In short, the code is no different than if you'd written it "the long way" yourself--all the type-safety is being checked at compile-time, much as C++ does.



The key thing is to realize that, despite what my earlier post about JSR 14 may have implied, the generics-in-Java effort is not stalled or dead, and with any luck we'll see a release before JDK 1.5 goes out the door. In the meantime, I encourage you to play with the early access release, and offer feedback to the JSR 14 team about what you think of it.



And so now the race continues. Which environment will be the first with generics, Java or .NET?


2 Comments

anonymous2
2002-11-07 13:04:40
Compile time checking
You wrote:


"... all the type-safety is being checked at compile-time, much as C++ does."


I know what you really meant, but this might be mis-leading to others. Yes, both C++ and Generic Java compilers perform type checking at compile time. Unfortunately, due to Generic Java's use of type erasure, the resulting byte code still includes those type checks at run time - while C++ object code doesn't.


I will take this a step further and say that, generally speaking, C++'s template mechanism and Generic Java's type erasure hold little in common. One is a Turing complete language while the other is a type cast generating device.


God bless,
-Toby Reyelts


anonymous2
2002-11-12 11:45:00
why are people considering the greaterthan/lessthan syntax?!?!
I know that C++ used that syntax, but does anybody remember the headaches that this syntax caused parsing the tools? And what about the fact that Java code is often embedded in markup languages? Why is something like this never considered:


List of (List of (HashMap of (String,Object)))
HashMap of (List of (String),Window)


instead of:


List>>
HashMap,Window>>


and this is why this syntax has so much danger
in terms of breaking existing parsers:




<%
if(a z=n<<5
a=var>>>5;
HashMap,Window>> h;
%>
...


It is more difficult to tokenize the second syntax. Even C# is going to use the latter syntax in deference to the decision to use it in C++. I don't understand the reasoning behind it.


It does appear that C# may come in with generics second, but they will probably come in with fully supported generics first because they have few compatibility issues to wrestle with.


ex: instead of reflection giving you something useless like this:


List GraphAlgorithm.StronglyConnectedParts(Graph g)


it could give you:


List of (Graph of (Vertex,Edge)) GraphAlgorithm.StronglyConnectedParts(Graph g)


or


List of (WeightedVertex) GraphAlgorithm.GreedySearch(Graph of (WeightedVertex,Edge))