Generics: An Extra Measure of Type Safety in Java

Robert Eccardt

Notes and code snippets from the Hello Java Workgroup of the NYJavaSIG held on December 14, 2011.

Bad Old Days (Java 1.4 and earlier):
Collections did not have type safety.
Wrong type could be inserted.
Cast required when extracting an item from the collection.
Type errors are detected at runtime (ClassCastException).

We set Eclipse's Java compiler compliance level to 1.4 and noted how in the old style we could add an integer to an intended ArrayList of Strings without warning, and get a runtime error when attempting to use it (BadOldDays.java):

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;


public class BadOldDays {

	public static void main(String[] args) {
		List al = new ArrayList();
		al.add("hello");
		al.add("goodbye");
		//al.add(new Integer(7));
		
		Iterator i = al.iterator();
		while(i.hasNext()) {
			String s = (String) i.next();
			System.out.println(s);
		}

	}

}

Since Java 5 (generics were added):
Generics - fancy name: parametric polymorphism
Old style (raw types) still allowed but cause warnings.
Using generics prevents inserting the wrong type into a collection.
Type errors are caught at compile time.

Next, we set Eclipse's Java compiler compliance level to 1.7 and converted the previous code to the new style with generics. Now, attempting to add an integer causes a compile time error: "The method add(String) in the type List<String> is not applicable for the arguments (Integer)." BetterDays.java:

import java.util.ArrayList;
import java.util.List;


public class BetterDays {

	public static void main(String[] args) {
		List<String> al = new ArrayList<String>();
		al.add("hello");
		al.add("goodbye");
		//al.add(new Integer(7));
		
		for(String s : al) {
			System.out.println(s);
		}

	}

}

New in Java 7
Type inference using the diamond operator
http://docs.oracle.com/javase/tutorial/java/generics/gentypeinference.html
Saves you from typing out potentially long generic specifications twice.
This:
    ArrayList<Map<String,? extends Float>> alm = new ArrayList<Map<String,? extends Float>>();
becomes this:
    ArrayList<Map<String,? extends Float>> alm = new ArrayList<>();
My pet peeve: this is backwards!
It would be much cleaner to do it the way C++, Scala, and C# do it: simplify on the left side of the equals.
This eliminates all retyping, not just the type arguments.
In C++, this:
    vector<boost::shared_ptr<Fruit> >* pvf = dynamic_cast<vector<boost::shared_ptr<Fruit> >*>(&vf);
becomes this:
    auto pvf = dynamic_cast<vector<boost::shared_ptr<Fruit> >*>(&vf);

They battle it out here:
<