Creating Generic Classes
Introduction Java Generics were added to allow for type- safe collections and eliminate the need for burdensome, code-cluttering casts when working with collections. ArrayList list = new ArrayList(); list.add(“foo”); String e = (String) list.get(0);// cast is annoying Integer e = (Integer)list.get(0); //runtime error!
Generics Many Java classes (including all Collections) were retrofitted to use generic types as: ArrayList slist= new ArrayList (); slist.add(“hello”); //compiler can check! String s = slist.get(0); //no cast required This is read as “An ArrayList of String”. Obviously, any object can be used, String just an example.
Autoboxing The native datatype wrapper classes (Integer, Double, Float, etc.) have a special property when used with generics. LinkedList list = new LinkedList (); list.add(3); //new Integer(3) not necessary! int i = list.get(0); //returns a native int This feature saves wrapping and unwrapping native types. It is separate from but goes hand in hand with generics.
Some gotchas This is all pretty simple – using classes built on generics not much harder than using classes based on Object. There are a few gotchas though even at list level. The most common are: – instanceof will not work with parameterized types – arrays of parameterized types are illegal E.g. ArrayList [] list = new ArrayList [3]; //NO!
View from the other side Sometimes you might need to create your own generic classes. This is considered a bit more of an advanced topic, but everyone should be familiar with the basics. We go into much more detail in Advanced Java.
** * Generic version of the Box class. the type of value being boxed */ public class Box { private T t; // T stands for "Type" public void add(T t) { this.t = t; } public T get() { return t; } Using Box signals that users can create Boxes of any type. it is also possible to have generic methods within classes that themselves are not Generic.
Using the Box class class BoxDemo3 { public static void main(String[] args) { Box integerBox = new Box (); //as of 1.7 ok to use Box i = new Box(); integerBox.add(new Integer(10)); Integer someInteger = integerBox.get(); // no cast! int someIntegerAsNativeType = integerBox.get(); //autoboxing System.out.println(someInteger); }
public class Box { private T t; public void add(T t) { this.t = t; } public T get() { return t; } public void inspect(U u){ System.out.println("T: " + t.getClass().getName()); System.out.println("U: " + u.getClass().getName()); } public static void main(String[] args) { Box integerBox = new Box (); integerBox.add(new Integer(10)); integerBox.inspect("some text"); } Signals that the method takes a generic type parameter (U by convention). u is of type U Generic type parameters for methods
public class Box { private T t; public void add(T t) { this.t = t; } public T get() { return t; } public void inspect(U u){ System.out.println("T: " + t.getClass().getName()); System.out.println("U: " + u.getClass().getName()); } public static void main(String[] args) { Box integerBox = new Box (); integerBox.add(new Integer(10)); integerBox.inspect("some text"); // error: this is still String! } Bounded type parameters. Read as “anything that extends Number”. Float ok but String not, e.g.
A couple of more gotchas You cannot instantiate type variables! T obj = new T(); //NO This can be worked around with reflection but it is really ugly. Consider other alternatives. You cannot throw or catch instances of a generic class