Java Generics I

One of the biggest addi­tions to the Java lan­guage in the past few releas­es was Generics. Nearly every­one asked, begged, or demand­ed that they be added pret­ty much since Java 2. I was one of those who want­ed them. I felt that my life would be eas­i­er and that I could write bet­ter code if I had them. But now that I have them (actu­al­ly I only have them for my own per­son­al projects as my work must still sup­port 1.4) I’m not so sure that they are real­ly that impor­tant or use­ful in most sit­u­a­tions. In fact, they may actu­al­ly be a detri­ment.

For most peo­ple the major rea­son for hav­ing Generics is the col­lec­tion class­es. How many times have we had to cast the val­ues retrieved from a col­lec­tion? All those extra key­strokes and it seemed extreme­ly sil­ly that a “strong­ly typed” lan­guage would have us con­vert most of our objects to Object and then cast them back again lat­er. All of that strong typ­ing down the drain.

So Generics, when used with the col­lec­tion class­es, give us two basic fea­tures:

  1. Objects do not need casts when removed from a col­lec­tion. Removing cast expres­sions from state­ments makes them eas­i­er to read.
  2. Object types are sta­t­i­cal­ly checked when added to a col­lec­tion. Now you can’t acci­den­tal­ly put the wrong object on a col­lec­tion (unless you inten­tion­al­ly try to cheat the sys­tem).

Let’s take the cast­ing one first. Removing casts from the code is always a good thing. They don’t real­ly con­vey any use­ful infor­ma­tion to peo­ple, they just make the com­pil­er hap­py. But there is an inter­est­ing detail of Generics dis­cussed by oth­ers but is still not com­mon knowl­edge. That is (unlike C# and the .NET run-​time) there is no sup­port for gener­ics in the JVM. No byte-​codes (read instruc­tions) or any oth­er def­i­n­i­tions. What this means is that when the com­pil­er sees your gen­er­al­ized code it com­piles it to some­thing that could run on a 1.4 JVM.

Here is a sim­ple exam­ple to show what I mean. I took this Java file and com­piled it using the 1.6 JDK.

This class has two meth­ods, one that takes a sin­gle para­me­ter which is a Map from String to String the oth­er is an old-​style Map with no type. Note that the gen­er­al­ized Map.get() does not require a cast while the old style does. Now things get inter­est­ing when we dis­as­sem­ble the .class file (I used javap -c).

[text]
pub­lic void doGeneric(java.util.Map);
Code:
0: aload_​1
1: ldc #18; /​/​String abc
3: invokein­ter­face #20, 2; /​/​InterfaceMethod java/util/Map.get:(Ljava/lang/Object;)Ljava/lang/Object;
8: check­cast #26; /​/​class java/​lang/​String
11: astore_​2
12: return

pub­lic void doRaw(java.util.Map);
Code:
0: aload_​1
1: ldc #18; /​/​String abc
3: invokein­ter­face #20, 2; /​/​InterfaceMethod java/util/Map.get:(Ljava/lang/Object;)Ljava/lang/Object;
8: check­cast #26; /​/​class java/​lang/​String
11: astore_​2
12: return
[/​text]

These are the byte-​codes gen­er­at­ed for the two func­tions. As you can see they are iden­ti­cal. Let that sink in a bit. They aren’t just sim­i­lar, they are exact­ly the same.

The two lines of impor­tance for this dis­cus­sion #6 and #15. They are the checkcast byte-​codes. This byte-​code is what a down cast com­piles to in Java. It makes sure that the tar­get object is of the giv­en type or an assign­a­ble type (i.e. sub­class). Even though we have a strong­ly typed Map using gener­ics the code gen­er­at­ed still has a checkcast byte-​code. The sys­tem knows that you can sub­vert the type with a cast so to guar­an­tee safe­ty it still must check that the type is assign­a­ble. So the only thing that the gener­ic def­i­n­i­tion has bought us is we don’t need the cast to String.

This post is get­ting long so we will con­tin­ue in Part II.

Leave a Reply