350 likes | 389 Views
CSE 501N Fall ‘09 16: Java Generics. October 27, 2009 Nick Leidenfrost. Lecture Outline. Generic Types Generic Methods Bounds Upper Bounds Lower Bounds Wildcards. The Downside of Casting. In Java, we use class casts to assign objects to a more specific type
E N D
CSE 501NFall ‘0916: Java Generics October 27, 2009 Nick Leidenfrost
Lecture Outline • Generic Types • Generic Methods • Bounds • Upper Bounds • Lower Bounds • Wildcards
The Downside of Casting • In Java, we use class casts to assign objects to a more specific type • In a perfect world, having to perform this cast is only mildly annoying, but… Hashtable students = new Hashtable(); ... Integer studentID = new Integer(4453563); string studentName = “Bing Slothersby”; students.put(studentID, studentName); ... String name = (String)students.get(studentID);
The Downside of Casting • … in reality, it is just one more place for programmer error. • Wouldn’t it be nice if the compiler could check for things like this? Hashtable students = new Hashtable(); ... Integer studentID = new Integer(4453563); students.put(studentID, studentID); ... /* Class Cast Exception */ String name = (String)students.get(studentID);
Type Variables • Generic programming: creation of programming constructs that can be used with many different types • In Java, achieved with inheritance -or- with Type Variables • For example: • Inheritance vs. Type variables: a LinkedList (e.g.LinkedList<String>)
Type Variables: Generic Classes • Generic class: a class declared with one or more type variables • The type variable denotes the element type public class LinkedList<E> { // could have used "ELEMENT_TYPE" or other name // name instead of E public LinkedList() { . . . }public void add(E element) { . . . } . . .} Continued
Type Variables • Can be instantiated with class or interface types • Cannot use a primitive type as a type variable • Use corresponding wrapper class instead LinkedList<BankAccount>LinkedList<Comparable> LinkedList<double>// Wrong! LinkedList<Double>
Type Variables • Supplied type “replaces” type variable in the class interface • Example: add in LinkedList<BankAccount> has type variable E “replaced” with BankAccount: • Contrast with LinkedList.add: public void add (BankAccount element) public void add (Object element)
Type Variables Increase Safety • Type variables allow the compiler to perform some compile-time checks to ensure the correct type is being used • Unlike when implemented with inheritance it is now impossible to add a String into an LinkedList<BankAccount>
Type Variables Increase Safety • Can add a String into a non-generic collection intended to hold bank accounts ArrayList<BankAccount> acc1 = new ArrayList<BankAccount>();ArrayList acc2 = new ArrayList(); // Should hold BankAccountsaccounts1.add("my savings"); // Compile-time erroraccounts2.add("my savings"); // Not detected at compile time. . .BankAccount account = (BankAccount)acc2.getFirst(); // Run-time error (ClassCastException)
Generic Types • Interfaces can be generic too! public interface Iterator <T> { public T next (); public boolean hasNext(); }
Type Variable Names • Like classes and variables, Type Variables have naming conventions • In longer type names, lower-case letters should be avoided in type variables to avoid confusion with class names and normal variables
Implementing Generic Classes • Example: simple generic class that stores pairs of objects • The getFirst and getSecond retrieve first and second values of pair Pair<String, BankAccount> result = new Pair<String, BankAccount>("Harry Hoo", checking); String name = result.getFirst();BankAccount account = result.getSecond();
Class Pair public class Pair<T, S> { private T first;private S second;public Pair (T firstElement, S secondElement) { first = firstElement; second = secondElement; } public T getFirst() { return first; } public S getSecond() { return second; }}
Implementing Generic Classes • Generics are particularly useful for collections • Use Type Variables when you receive, return, or store an element • Complexities arise only when your data structure uses helper classes, e.g. Nodes, KeyValuePairs, MapItems, etc. • If helpers are inner classes, no need to do anything special • Helper types defined outside generic class need to become generic classes toopublic classListNode<E>
Turning LinkedList into a Generic Class public class LinkedList<E> { private Node<E> head; public E removeFirst() {if (this.isEmpty())return null; E element = head.next.contents; head.next = head.next.next; head.next.prev = head; return element; } } public class Node<E> { E data; Node<E> next; }
Generic Assignability • We must not think of generic objects as having similar properties to class hierarchies: • Type Variable bounds and wildcards will let us do this to some extent in a more type-safe manner LinkedList<String> stringList = new ArrayList<String>(); LinkedList<Object> objectList = stringList; We now have an alias of type LinkedList<Object> that would allow an Object to be inserted into a LinkedList<String>!
Generic Methods • Generic method: method with a type variable • Can be defined inside ordinary and generic classes • A regular (non-generic) method: public static int sum (int[] array) {int sum = 0; for (int num : array) sum += num; return sum; }
Generic Methods • What if we want to sum an array of doubles instead? public static <E> void sum (E[] array) { E sum;for (E element : array) sum += element;return sum;}
Generic Methods • When calling a generic method, you don’t need to specify the type variables: • The compiler deduces that E is the class Rectangle • (You can also define generic methods that are not static) Rectangle[] rectangles = . . .;ArrayUtil.print(rectangles);
Generic Methods • You can even have generic methods in generic classes • Cannot replace type variables with primitive types
Syntax: Instantiating a Generic Class modifiers <TypeVariable1, TypeVariable2, . . .> returnType methodName (parameters) { // method body} Example: public static <E> void print(E[] a) {// Method body } Purpose: To define a generic method that depends on type variables
Constraining Type Variables • Type variables can be constrained with bounds public static<EextendsComparable>E min(E[] a) { E smallest = a[0];for (int i = 1; i < a.length; i++)if (a[i].compareTo(smallest) < 0) smallest = a[i];return smallest;}
Constraining Type Variables • Can call min with a String[] array but not with a Person[] array • Comparable bound necessary for calling compareTo • Otherwise, min method would not have compiled
Constraining Type Variables • Very occasionally, you need to supply two or more type bounds • extends, when applied to type variables, actually means "extends or implements" • The bounds can be either classes or interfaces • Type variable can be replaced with a class or interface type <E extends Comparable & Cloneable>
Wildcard Types • Wildcard types are useful when we want to allow the broadest set of generic objects to interact with our generic object • We may not know (or care) what actual generic type is contained within the object we are dealing with • As long as the generic type of the object we are dealing with matches our criteria
Wildcard Types public void addAll(LinkedList<? extends E> other) { ListIterator<E> iter = other.listIterator(); while (iter.hasNext()) add(iter.next());} public static <E extends Comparable<E>> E min(E[] a) static void reverse(List<?> list)
Wildcard Types: ? extends T • Determines an “upper bound” in the inheritance hierarchy for what the actual type could be • Writing non null elements is not allowed • Why? Because we don’t know what type is contained • List<? extends Animal> could be a List<Animal> or a List<Dog>, and we can’t put an Animal into a List<Dog> • Useful when we need to “read” from a generic typed object: public void emptyAndProcess (LinkedList<? extends T> list) { T listItem = list.removeFirst(); // Do something with T }
Wildcard Types: ? super T • Determines an “lower bound” in the inheritance hierarchy for what the actual type could be • Reading from such a list returns data of type Object • Useful when we need to “write” to a generic typed object: public void add (LinkedList<? super T> list, T toAdd) { list.add(toAdd); }
Raw Types • The virtual machine works with raw types, not with generic classes • Once compiled, any detectable type incompatibility problems have necessarily been eliminated • The raw type of a generic type is obtained by erasing the type variables • “Type erasure”
Raw Types • For example, generic class Pair<T, S> turns into the following raw class: public class Pair { private Object first;private Object second; public Pair(Object firstElement, Object secondElement) { first = firstElement; second = secondElement; } public Object getFirst() { return first; }public Object getSecond() { return second; } }
Raw Types • Same process is applied to generic methods: public static Comparable min (Comparable[] a) { Comparable smallest = a[0];for (int i = 1; i < a.length; i++)if (a[i].compareTo(smallest) < 0) smallest = a[i];return smallest;}
Raw Types • Knowing about raw types helps you understand limitations of Java generics • For example, you cannot replace type variables with primitive types • To interface with legacy code (non-generic code), you can convert between generic and raw types
Conclusion • For additional reading on Generics: • http://java.sun.com/j2se/1.5/pdf/generics-tutorial.pdf • Questions? • Lab 6 Assigned today • Due 11/03