Announcements PS 3 is ready Midterm exam 1: Tuesday, April 11, in class Closed book but one sheet, both sides, of A4 paper is allowed Today s topic: Generics (parameterized types) Readings for this slide set Section 2.5 of the text Generics in the Java Programming Languages by Gilad Bracha Read the first 5 sections of this tutorial Second tutorial by Sun is optional Java Generics and Collections by M. Naftalin and P. Wadler is a comprehensive reference (PDF available on the Internet) Read the first two chapters of this book if you want Break around 11:45am 1
Generic types Generics allow you to abstract over types. Commonly used in container types, e.g., in a Collection type. Example without generics: ArrayList u = new ArrayList(); u.add(new Point(2,3)); Point p = (Point)a.get(0); The type cast is annoying but necessary; The programmer knows what type of object has been placed into the list, but the compiler can only guarantee that an Object will be returned, thus the need for the type cast. Compiler cannot check that the cast is correct at compile-time, since it would not know what type of objects will be placed in it later. Inconvenient and unsafe, could fail at runtime. 2
Generic types (cont.) Example with generics: ArrayList<Point> u = new ArrayList<Point>(); u.add(new Point(2,3)); Point p = a.get(0); No need to type cast anymore! Compiler can now check the type correctness at compile-time because it knows that the list is to hold only Point objects Generics provide a way to communicate T, Point here, the type of elements in a collection, to the compiler. Compiler can check that you have used the collection consistently. Result: safer and more-efficient code. 3
Raw ArrayLists Creating a raw ArrayList: ArrayList al = new ArrayList(); Accessing a raw ArrayList: Note: this will cause a warning saying you re using the raw unsafe version when you try to add an object to it later. boolean add(object obj) Object set(int index, Object obj) Object get(int index) Why is Object used as a parameter here? 4
Motivation for generics Using Object as the parameter type means you can put any object into an ArrayList Seems useful but it s also a source of problems Can t check the type at compile time Suppose we want an ArrayList of Strings: ArrayList al = new ArrayList(); al.add(new String( Apple )); String as = (String)al.get(0); // need typecast al.add(new Integer(43)); // OK String as = (String)al.get(1); // runtime error! 5
Motivation for generics (cont.) Goal: detect type errors early at compile-time (as opposed to late at runtime) One solution is create specialized collection classes: one for Strings, another one for a different type, etc. ArrayListString, ArrayListYourObj, etc. This solution gives you type checking during compilation but at the undesirable expense of a lot of essentially identical code 6
Generics programming A solution for dealing with these kinds of type errors without code replication is generic programming - a way to write reusable generic type-safe code The String ArrayList example redone using generics: type parameter ArrayList<String> al = new ArrayList<String>(); al.add(new String( Apple )); String as = al.get(0); // no cast needed! al.add(new Integer(43)); // compile-time error, cool! 7
Generic types A generic type allows us to use a placeholder instead of an actual class in the definition of a class or interface Example: public class MyClass<T> {... Identifier T represents a class type within the definition of MyClass Can be any identifier but a single capital letter, e.g., T or E, is usually used for each type 8
Ex: Generic SingleItem class public class SingleItem<T> { private T item; SingleItem(T item) { // Constructor does not need <> this.item = item; public T getitem() { return(item); public void setitem(t item) { this.item = item; 9
Ex: Generic DoubleItem class public class DoubleItem <K,V> { private K item1; private V item2; // Key-Value pair DoubleItem (K item1, V item2) { this.item1 = item1; this.item2 = item2; K getitem1(){ return item1; V getitem2(){ return item2; 10
Instantiating a generic class SingleItem<String> si = new SingleItem<String>() or: SingleItem<String> si = new SingleItem<>(); DoubleItem<String,Integer> di = new DoubleItem<>(); Notice the use of the wrapper class Integer primitive types cannot be used directly as types for generic classes 11
Restricting type parameters Suppose I want my SingleItem class to only allow the class Foo and it s subtypes, e.g., SubFoo. Here s the heading for the new class definition: public class SingleItem<T extends Foo> {... Now I can do the following when instantiating a SingleItem: or SingleItem<Foo> item1 = new SingleItem<>(); SingleItem<SubFoo> item1 = new SingleItem<>(); 12
Generic methods In addition to writing generic data structures (classes), we can also write generic methods Why write generic methods? Similar to generic classes, we want to avoid creating many nearly-identical methods that only differ by the class type it operates on Let s look at a non-generic method and then convert it to generic 13
Generic classes as method parameters Suppose we have a method signature as follows: mymethod(arraylist<foo> mylist) {... Foo SubFoo Can I pass in a reference to ArrayList<SubFoo> instead? You might think so... but you can t! ArrayList<SubFoo> is not a subtype of ArrayList<Foo> even though SubFoo is a subclass of Foo. 14
Generic classes as method parameters (cont.) Instead, consider a method declared as follows: mymethod(arraylist<? extends Foo> mylist ) {... The notation <? extends Foo> means mymethod is expecting an ArrayList whose type parameter is a Foo or a subclass of Foo Now we can pass in a reference to ArrayList<Foo> or ArrayList<SubFoo> Formally, this is known as a bounded wildcard type You can also use <? super Foo> to indicate that a method expects a type parameter of Foo or a superclass of Foo. 15
Non-generic example // returns the number of times item occurs in list public static int count(string[] list, String item){ int c = 0; if (item == null) { for (String x : list) if (x == null) c++; else { for (String x : list ) if (item.equals(x)) // x.equals(item)? c++; return c; 16
Generic example // returns number of times item occurs in list of generic type T public static <T> int count(t[] list, T item){ int c = 0; if (item == null) { for (T x : list) if (x == null) c++; else { for (T x : list) if (item.equals(x)) c++; return c; 17
Generic insertion sort (v1) When writing a generic sort method, we need to use the compareto method so we need to be able say that we can only accept types that have implemented the Comparable<T> interface public static <T extends Comparable<T>> void isort(t[] a) { for (int i = 1; i < a.length; i++) { for (int j = i; j > 0; j--) { if (a[j - 1].compareTo(a[j]) > 0) { T temp = a[j - 1]; a[j - 1] = a[j]; a[j] = temp; 18
Generic insertion sort (v2) When writing a generic sort method, we need to use the compareto method so we need to be able say that we can only accept types that have implemented the Comparable<T> interface public class InsertionSort<T extends Comparable<T>> { public static void isort(t[] a) { for (int i = 1; i < a.length; i++) { T tmp = a[i]; for (int j = i; j>0 && a[j-1].compareto(tmp)>0; j--) { a[j] = a[j-1]; a[j] = tmp; 19
More examples (be sure to read!) See Generic.java Point.java Point3.java See Generic2.java (be sure to read the comments in this file) 20
Summary: Why generics? Safer code type violations caught early at compile-time type more specific to the needs Easier to read (in some cases) the type of the data contained in a data structure is clear ugly casts are not needed Reusable object-oriented code instead of type-specific code 21