Monday, March 15, 2010

Java Generics

  • introduced in J2SE 5.0
  • adds robustness/stability on code by making more bugs detectable at compile time
  • normal way can cause runtime ClassCastException
    public class Box {
        private Object object;
        public void add(Object object) {
            this.object = object;
        }
        public Object get() {
            return object;
        }
    }
        Box integerBox = new Box();
        integerBox.add("10"); // note how the type is now String
        Integer someInteger = (Integer)integerBox.get();

  • sample generic version with type variable T
    public class Box {
    private T t; // T stands for "Type"      
    public void add(T t) {
        this.t = t;
    }
    public T get() {
        return t;
    }
    }
        Box integerBox = new Box();
        integerBox.add(new Integer(10));
        Integer someInteger = integerBox.get(); // no cast!

  • generic method/constructor
    public static <U> void fillBoxes(U u, List<Box<U>> boxes) {
    for (Box<U> box : boxes) {
        box.add(u);
    }
    }
    Crayon red = ...;
    List<Box<Crayon>> crayonBoxes = ...;
    // normal way
    Box.<Crayon>fillBoxes(red, crayonBoxes);
    // with type inference
    Box.fillBoxes(red, crayonBoxes); // compiler infers that U is Crayon

  • Bounded Parameter types -> when variables are restricted to certain types, upper bound only
    <U extends Number & MyInterface> // uses extend for both classes & interfaces
  • Unknown type/wilcard bounds -> represented by wildcard character "?"

    3 different flavors of wildcards:
    • " ? " - the unbounded wildcard. It stands for the family of all types.
    • " ? extends Type " - a wildcard with an upper bound. It stands for the family of all types that are subtypes of Type , type Type being included.
    • " ? super Type " - a wildcard with a lower bound. It stands for the family of all types that are supertypes of Type , type Type being included.
    // Box<Integer> and Box<Double> are not subtypes of Box<Number>
    Box<Number> box = new Box<Integer>(); // compile-time error
    
    // Box<Integer> and Box<Double> are subtypes of Box<? extends Number>
    Box<? extends Number> box = new Box<Integer>(); // ok
    
    Integer i = new Integer(1);
    Number n = i;
    // compile-time error
    // Integer is not '? extends Number'
    // we are not sure that the instance type of Box is Integer, so we cannot add i
    box.add(i); 
    
    // compile-time error
    // Number is not '? extends Number'
    // we are sure that the type of Box extends a Number but we are not sure of the 
    //     instance type of n, so we cannot add n
    // what if runtime type of Box is Box<Double> but n is Integer
    box.add(n)
    
    // compile-time error
    // we are not sure that the type of Box is an Integer
    Integer i2 = box.get();
    
    // we are sure that the type of Box is always extends a Number
    Number n2 = box.get(); // ok
    
    // sample using upperbound & lowerbound wildcards
    public class Collections {
    public static <T> void copy
    ( List<? super T> dest, List<? extends T> src) {  // bounded wildcard parameterized types
       for (int i=0; i<src.size(); i++)
         dest.set(i,src.get(i));
    }
    }

  • Type Erasure -> when generic types are instantiated, all generic information are removed leaving only the raw type. Box becomes Box only.
    public class MyClass {
        public static void myMethod(Object item) {
            if (item instanceof E) {  //Compiler error
                ...
            }
            E item2 = new E();   //Compiler error
            E[] iArray = new E[10]; //Compiler error
            E obj = (E)new Object(); //Unchecked cast warning
        }
    }

Sources:
http://java.sun.com/docs/books/tutorial/java/generics/index.html
http://www.angelikalanger.com/GenericsFAQ/JavaGenericsFAQ.html

No comments:

Post a Comment