ProblemYou wish to define your own container classes using the Generic Type mechanism to avoid needless casting. SolutionDefine a class using <TypeName> where the type is declared and TypeName where it is used. DiscussionConsider the very simple Stack class in Example 8-4. This class has been parameterized to take a type whose local name is T. This type T will be the type of the argument of the push( ) method, the return type of the pop( ) method, and so on. Because of this return type more specific than the Object return type of the original Collections the return value from pop( ) does not need to be downcasted. In 1.5, the Collections Framework classes have been modified similarly. Example 8-4. MyStack.java/** A lax Stack implementation. */ public class MyStack<T> { private int ix = 0; public final int MAX = 10; private T[] data = (T[])new Object[MAX]; public void push(T obj) { data[ix++] = obj; } public boolean hasNext( ) { return ix > 0; } public boolean hasRoom( ) { return ix < MAX; } public T pop( ) { if (hasNext( )) { return data[- -ix]; } throw new ArrayIndexOutOfBoundsException(-1); } } The association of a particular type is done at the time the class is instantiated. For example, to instantiate a MyStack specialized for holding BankAccount objects, one would need to code only the following: MyStack<BankAccount> theAccounts = new MyStack<BankAccount>( ); Note that if you do not provide a specific type, this class defaults to the most general behavior, that is, type T is treated as java.lang.Object. So this toy collection, like the real ones in java.util, will behave as they did in 1.4 accepting input arguments of any type, returning java.lang.Object from getter methods, and requiring downcasting as their default, backward-compatible behavior. Example 8-5 shows a program that creates two instances of MyStack, one specialized for Strings and one left general. The general one, called m2, is loaded up with the same two String objects as m1 but also includes a Date object. The printing code is now "broken", as it will throw a ClassCastException : a Date is not a String. I handle this case specially for pedantic purposes: it is illustrative of the kinds of errors you can get into when using nonparameterized container classes. Example 8-5. MyStackDemo.javapublic class MyStackDemo { public static void main(String[] args) { MyStack<String> ms1 = new MyStack<String>( ); ms1.push("billg"); ms1.push("scottm"); while (ms1.hasNext( )) { String name = ms1.pop( ); System.out.println(name); } // Old way of using Collections: not type safe. MyStack ms2 = new MyStack( ); ms2.push("billg"); ms2.push("scottm"); ms2.push(new java.util.Date( )); // Show that it is broken try { String bad = (String)ms2.pop( ); System.err.println("Didn't get expected exception!"); } catch (ClassCastException ex) { System.out.println("Did get expected exception."); } // Removed the brokenness, print rest of it. while (ms2.hasNext( )) { String name = (String)ms2.pop( ); System.out.println(name); } } } Because of this potential for error, the 1.5 compiler warns that you have unchecked raw types. Like the Deprecation warnings discussed in Recipe 1.9, by default, these warnings are not printed in detail. You must ask for them, with the rather lengthy option -Xlint:unchecked: C:> javac -source 1.5 MyStackDemo.java Note: MyStackDemo.java uses unchecked or unsafe operations. Note: Recompile with -Xlint:unchecked for details. C:> javac -source 1.5 -Xlint:unchecked MyStackDemo.java MyStackDemo.java:14: warning: unchecked call to push(T) as a member of the raw type MyStack ms2.push("billg"); ^ MyStackDemo.java:15: warning: unchecked call to push(T) as a member of the raw type MyStack ms2.push("scottm"); ^ MyStackDemo.java:16: warning: unchecked call to push(T) as a member of the raw type MyStack ms2.push(new java.util.Date( )); ^ 3 warnings C:> |