Programming Java: All about Generics
One of the original design goals for Java was to keep the language as simple as possible. The language’s developer took some unnecessarily complicated features of C++ and tossed them out the window. The result was a language that was elegant and sleek. Some people said the language was too sleek.
So after several years of discussion and squabbling, Java became a bit more complicated. By the year 2004, Java had enum types, enhanced for loops, static import, and some other interesting new features. But the most talked-about new feature was the introduction of generics.
ArrayList<String> people = new ArrayList<String>();
The use of anything like <String> was new in Java 5.0. In old-style Java, you’d write
ArrayList people = new ArrayList();
In those days, an ArrayList could store almost anything you wanted to put in it — a number, an Account, a Room, a String . . . anything. The ArrayList class was very versatile, but with this versatility came some headaches. If you could put anything into an ArrayList, you couldn’t easily predict what you would get out of an ArrayList.
In particular, you couldn’t easily write code that assumed you had stored certain types of values in the ArrayList. Here’s an example:
ArrayList things = new ArrayList(); things.add(new Account()); Account myAccount = things.get(0); //DON'T USE THIS. IT'S BAD CODE.
In the third line, the call to get(0) grabs the earliest value in the things collection. The call to get(0) is okay, but then the compiler chokes on the attempted assignment to myAccount. You get a message on the third line saying that whatever you get from the things list can’t be stuffed into the myAccount variable.
You get this message because, by the time the compiler reaches the third line, it has forgotten that the item added on the second line was of type Account!
The introduction of generics fixes this problem:
ArrayList<Account> things = new ArrayList<Account>(); things.add(new Account()); Account myAccount = things.get(0); //USE THIS CODE INSTEAD. IT'S GOOD CODE.
Adding <Account> in two places tells the compiler that things stores Account instances — nothing else. So, in the third line in the preceding code, you get a value from the things collection. Then, because things stores only Account objects, you can make myAccount refer to that new value.
Java 5.0 added generics to Java. But soon after the birth of Java 5.0, programmers noticed how clumsy the code for generics can be. After all, you can create generics within generics. An ArrayList can contain a bunch of arrays, each of which can be an ArrayList. So you can write
ArrayList<ArrayList<String>> mess = new ArrayList<ArrayList<String>>();
All the repetition in that mess declaration gives programmers a headache! To avoid this ugliness, Java 7 and later versions have a diamond operator, <>. The diamond operator tells Java to reuse whatever insanely complicated stuff you put in the previous part of the generic declaration.
In this example, the <> tells Java to reuse <ArrayList<String>>, even though you write <ArrayList<String>> only once. Here’s how the streamlined Java 7 code looks:
ArrayList<ArrayList<String>> mess = new ArrayList<>();
In Java 7 and later, you can write either of these mess declarations — the original, nasty declaration with two occurrences of ArrayList<String>, or the streamlined (only mildly nasty) declaration with the diamond operator and only one ArrayList<String> occurrence.
Yes, the streamlined code is still complicated. But without all the ArrayList<String> repetition, the streamlined code is less cumbersome. The Java 7 diamond operator takes away one chance for you to copy something incorrectly and have a big error in your code.