Date post: | 19-Jan-2016 |
Category: |
Documents |
Upload: | darleen-foster |
View: | 216 times |
Download: | 0 times |
©SoftMoore Consulting Slide 1
Generics
“Generics constitute the most significant change in the Java programming language since the 1.0 release.”
– Cay Horstmann
©SoftMoore Consulting Slide 2
Generics
• Added to Java in version 5
• Similar to C++ templates and Ada generics
• Support typed collection classes and typed methods
• Reduce the need for casting
• Provide additional compile-time checking for code correctness
• Characteristics– easy to use– not easy to implement
©SoftMoore Consulting Slide 3
Generics Example
• Before Java 5 and genericsList customers = new LinkedList();...Customer c = (Customer) customers.get(0);
• Using genericsList<Customer> customers = new LinkedList<Customer>();...Customer c = customers.get(0); // no cast necessary
©SoftMoore Consulting Slide 4
Generics
• Allow a type or method to operate on objects of various types while providing compile-time type safety.
• Add compile-time type safety to the Collections Framework
• Eliminate the drudgery of casting.
• One of the most requested new features of Java 5
©SoftMoore Consulting Slide 5
A Simple Generic Class
public class Pair<T, S> { private T first; private S second;
public Pair(T first, S second) { this.first = first; this.second = second; }
public T getFirst() { return first; }
public S getSecond() { return second; } }
©SoftMoore Consulting Slide 6
Using Class Pair
• Creating an instance of the generic classPair<String, BankAccount> namedAcct = new Pair<String, BankAccount>("John", acct);
• Since Java 7, the second set of type arguments can be omitted from the constructor as long as the compiler can determine or infer the type arguments from the context.Pair<String, BankAccount> namedAcct = new Pair<>("John", acct);
• This pair of angle brackets, <>, is informally called the diamond.
©SoftMoore Consulting
Using Class Pair(continued)
• The generic class Pair<T, S> can be used– as the type of a method parameter– as the return type of a method
• Note that both generic parameters can have the same type, as in Pair<String, String>.
Slide 7
©SoftMoore Consulting Slide 8
Example: Defining a Generic Class
Excerpt from the definitions of the class List<E> andIterator<E> in package java.util
public interface List<E> { void add(E x); Iterator<E> iterator(); }
public interface Iterator<E> { E next(); boolean hasNext(); }
formal type parameter
©SoftMoore Consulting Slide 9
Type Variables
• A generic class or interface is declared with a type variable (often simply E) enclosed in angle brackets.
• The type variable denotes an element type that can be used within the generic class.
• A generic class can be instantiated with any class or interface typeList<BankAccount> accounts = new
ArrayList<BankAccount>();
List<Comparable> comps = new ArrayList<Comparable>();
• A generic class cannot be instantiated with a primitive type such as int or double (use wrapper classes).
©SoftMoore Consulting Slide 10
Naming Type Variables
Type Variable Name
Meaning
E Element type in a collection
K Key type in a map
V Value type in a map
T, S, U General types
©SoftMoore Consulting Slide 11
Advantages of Generics
• More readable code
• More correct code– can’t add a String to a list instantiated to hold bank accounts– checking performed by the compiler rather than at run time.
But the price we pay is language complexity.
©SoftMoore Consulting Slide 12
Erasure
• Intuitively, List<String> behaves like a version ofList where E has been uniformly replaced by String.
• This intuition can be helpful, but it’s also misleading.
• Generics are implemented by the Java compiler as a front-end conversion called erasure.
• Erasure gets rid of (or erases) all generic type information, resulting in raw type, a list of Object. The checks for correctness and consistency are performed only by the compiler.
©SoftMoore Consulting Slide 13
Example: Pair Class After Erasure
public class Pair { private Object first; private Object second;
public Pair(Object first, Object second) { this.first = first; this.second = second; }
public Object getFirst() { return first; } public Object getSecond() { return second; } }
Interface Iterable<E>
package java.lang;
import java.util.Iterator;
public interface Iterable<T> { Iterator<T> iterator(); }
©SoftMoore Consulting Slide 14
Implementing this interface allows an objectto be the target of a “forEach” loop.
©SoftMoore Consulting Slide 15
Enhanced “for” Loop
• Defined in Java 5 (a.k.a. Java 1.5)
• Syntaxfor (Classname variable : collection) statement; // may be a compound statement
• Works for collections and arrays– Collection must implement the Iterable interface
• Reduces the need for working with iterators and index variables.
©SoftMoore Consulting Slide 16
Example: Enhanced “for” Loop
• Assume we have a list of Customer objectsList<Customer> customers = new ArrayList<Customer>();
• Without the enhanced “for” loop (before Java 5)Iterator iter = customers.iterator();while (iter.hasNext()) { Customer c = (Customer) iter.next(); System.out.println(c); }
• Using the enhanced “for” loopfor (Customer c : customers) System.out.println(c);
©SoftMoore Consulting Slide 17
Example: Using Enhanced “for” Loopwith Primitive Types and Arrays
int sum(int[] a) { int result = 0;
for (int i : a) result += i;
return result; }
©SoftMoore Consulting Slide 18
Autoboxing
• Java makes a distinction between primitive types (int, double, char, etc.) and classes.
• Problem: Can’t put primitive types into collections.
• Possible solutions prior to Java 5– use wrapper classes (Integer, Double, Character, etc.)– write your own List class to handle primitive types
(separate class for each type)
• With autoboxing, the compiler performs automatic conversions between primitive types and their corresponding wrapper classes.
©SoftMoore Consulting Slide 19
Example: Autoboxing
• Without autoboxing (before Java 5)List list = new LinkedList();list.add(new Integer(13));...int n = ((Integer)(list.get(0))).intValue();
• With autoboxingList<Integer> list = new LinkedList<Integer>();list.add(13);...int n = list.get(0);
©SoftMoore Consulting Slide 20
Using Autoboxing
• The wrapper classes are still used. The compiler automatically converts between primitive classes and wrapper classes.
• Acceptable performance for occasional use
• Not appropriate for performance-critical applications and scientific numerical computing
©SoftMoore Consulting Slide 21
Constraining Type Variables
• Type variables can be constrained with boundspublic static <E extends Comparable<E>> E min(E[] a) { ... }
• “extends” actually means “extends” or “implements”.
• Bounds can be either classes or interfaces
• Can be called with String[] since String implements Comparable
• You can specify more than one type bound<E extends Comparable<E> & Cloneable>
©SoftMoore Consulting Slide 22
Generics and Subtyping
• Consider the following code:public class BankAccount { … }public class SavingsAccount extends BankAccount { … }List<BankAccount> a1 = new ArrayList<BankAccount>();List<SavingsAccount> a2 = new ArrayList<SavingsAccount>();
• We can add an object of class SavingsAccount to the list, but List<SavingsAccount> is not a subclass of List<BankAccount>.
©SoftMoore Consulting Slide 23
Wildcards
• It is possible to declare a collection whose element type matches anything.void printCollection(Collection<?> c) { for (Object e : c) { System.out.println(e); } }
• Collection<?> is a “collection of unknown”
©SoftMoore Consulting Slide 24
Using Wildcards
• Given a List<?>, we can call get() and make use of the result. The result type is an unknown type, but we always know that it is an object.
• It is therefore safe to assign the result of get() to a variable of type Object or pass it as a parameter where the type Object is expected.
©SoftMoore Consulting Slide 25
Bounded Wildcards: Motivation
• Assume we have an abstract class Shape and several subclasses of Shape (Circle, Rectangle, etc.).
• Method drawAll() will draw all shapes in a list.public void drawAll(List<Shape> shapes) { for (Shape s: shapes) s.draw(); }
• However, drawAll() will not accept List<Circle> as a valid type parameter.
©SoftMoore Consulting Slide 26
Bounded Wildcards
• We can rewrite method drawAll() using a bounded wildcard so that it will accept List<Circle> as a type parameter.public void drawAll(List<? extends Shape> shapes) { for (Shape s: shapes) s.draw(); }
©SoftMoore Consulting Slide 27
Generic Methods: Motivation
• Suppose you want to create a method that takes an array of objects and a collection and puts all objects in the array into the collection.static void copy (Object[] source, Collection<?> destination) { for (Object o : source) destination.add(o); // compile time error }
• Problem: You cannot add objects into a collection of unknown type.
©SoftMoore Consulting Slide 28
Generic Method
• Method with a type variablestatic <E> void print(List<E> l) ...
• Can be defined inside ordinary or a generic classes
• Can be static or non-static
• Compiler infers the actual type when the method is called – not explicitly expressed by the programmer.List<BankAccount> accounts = new LinkedList<BankAccount>();...print(accounts);
©SoftMoore Consulting Slide 29
Using Generic Methods
• We can solve the copy problem using a generic methodstatic <T> void copy(T[] source, Collection<T> destination) { for (T o : source) destination.add(o); // Correct }
• Calling the generic methodList<BankAccount> accts = new LinkedList<BankAccount>();...BankAccount[] actArray = new BankAccount[100];...copy(actArray, accts); // T inferred to be BankAccount
©SoftMoore Consulting Slide 30
Using Legacy Code with Generics
• When a generic type like List is used without a type parameter, it is a raw type (type Object).
• Using a raw type generates an unchecked warning, not an error.List list = new ArrayList(); // use only for
compatibility // with legacy codelist.add("Hello");list.add(new Date());
• The programmer is responsible for checking that the code is correct.