Recipe 8.3 Avoid Casting by Using Generics


Problem

You wish to define your own container classes using the Generic Type mechanism to avoid needless casting.

Solution

Define a class using <TypeName> where the type is declared and TypeName where it is used.

Discussion

Consider 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.java
public 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:>



Java Cookbook
Java Cookbook, Second Edition
ISBN: 0596007019
EAN: 2147483647
Year: 2003
Pages: 409
Authors: Ian F Darwin

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net