SLIDE 1 CPSC 331: Data Structures, Algorithms, and their Analysis
Introduction to Java Generics Usman R. Alim
Department of Computer Science
SLIDE 2
Generics in Java
Often times, we want to perform the same operations on various different types of data. Rather than repeating the code for different types, it would be nice to have the same code work for all types. (e.g. a sort function that works on integers, doubles as well as strings.) Introduced in JDK 5, generics provide us with a way of accomplishing this. You have probably already used them without even realizing it. There are generics methods, classes and interfaces.
SLIDE 3
Pair Example: Without Generics
Consider a class that holds a pair of values of any type. Prior to Java 5, we’d do something like:
public class Pair { Object x; Object y; public Pair( Object x, Object y ) { this.x = x; this.y = y; } public Object getX () { return x; } public Object getY () { return y; } // ... }
SLIDE 4
Pair Example: Without Generics
We can use this class to hold e.g. a pair of integers, or a pair of doubles.
Pair pair1 = new Pair(new Integer (1), new Integer (2)); Pair pair2 = new Pair(new Double (1.) , new Double (2.));
SLIDE 5
Pair Example: Without Generics
We can use this class to hold e.g. a pair of integers, or a pair of doubles.
Pair pair1 = new Pair(new Integer (1), new Integer (2)); Pair pair2 = new Pair(new Double (1.) , new Double (2.));
However, in order to retrieve an object, an explicit cast is needed.
int k = (Integer)pair1.getX (); double d = (Double)pair2.getY ();
SLIDE 6
Pair Example: Without Generics
We can use this class to hold e.g. a pair of integers, or a pair of doubles.
Pair pair1 = new Pair(new Integer (1), new Integer (2)); Pair pair2 = new Pair(new Double (1.) , new Double (2.));
However, in order to retrieve an object, an explicit cast is needed.
int k = (Integer)pair1.getX (); double d = (Double)pair2.getY ();
This is inconvenient and also leads to type safety issues. E.g. the following statements don’t signal any compile-time errors but will give runtime errors.
int k = (Integer)pair2.getX (); // error double d = (Double)pair1.getY (); // error
SLIDE 7
Pair Example: With Generics
Generic classes allow us to write classes with parametrized types. Our pair example now looks like:
public class Pair <T1 , T2 > { T1 x; // x is of type T1 T2 y; // y is of type T2 public Pair( T1 x, T2 y ) { this.x = x; this.y = y; } public T1 getX () { return x; } public T2 getY () { return y; } // ... }
Here, T1 and T2 are type parameters.
SLIDE 8
Pair Example: With Generics
A pair of integers can now be created as follows:
Pair <Integer , Integer > pair1 = new Pair <Integer , Integer > (new Integer (1), new Integer (2));
SLIDE 9
Pair Example: With Generics
A pair of integers can now be created as follows:
Pair <Integer , Integer > pair1 = new Pair <Integer , Integer > (new Integer (1), new Integer (2));
Arguments of the second diamond operator (<>) can be omitted, and we can use autoboxing e.g.
Pair <Integer , Integer > pair1 = new Pair <>(1, 2);
SLIDE 10
Pair Example: With Generics
A pair of integers can now be created as follows:
Pair <Integer , Integer > pair1 = new Pair <Integer , Integer > (new Integer (1), new Integer (2));
Arguments of the second diamond operator (<>) can be omitted, and we can use autoboxing e.g.
Pair <Integer , Integer > pair1 = new Pair <>(1, 2);
Casts are no longer necessary, incompatible type assignments are checked at compile time, e.g.
Double d1 = pair1.getX (); // not allowed double d2 = pair1.getX (); // ok due to unboxing
SLIDE 11
Extending Generic Classes
Generic classes can be extended to non-generic or generic classes.
SLIDE 12
Extending Generic Classes
Generic classes can be extended to non-generic or generic classes. Let’s extend the Pair class to a Point class:
public class Point extends Pair <Double , Double > { public Point(Double x, Double y) { super(x,y); } public double getLength2 () { return getX ()* getX () + getY ()* getY (); } }
SLIDE 13
Extending Generic Classes
Generic classes can be extended to non-generic or generic classes. Let’s extend the Pair class to a Point class:
public class Point extends Pair <Double , Double > { public Point(Double x, Double y) { super(x,y); } public double getLength2 () { return getX ()* getX () + getY ()* getY (); } }
Point is a non-generic class:
Point p = new Point( new Double (1.) , new Double (2.)); System.out.println( p.getLength2 () );
SLIDE 14 Extending Generic Classes
Let’s extend the Pair class to a generic Triple class that has only
public class Triple <T> extends Pair <T, T> { T z; // add another member public Triple(T x, T y, T z) { super(x,y); this.z = z; } public T getZ () { return z; } }
SLIDE 15 Extending Generic Classes
Let’s extend the Pair class to a generic Triple class that has only
public class Triple <T> extends Pair <T, T> { T z; // add another member public Triple(T x, T y, T z) { super(x,y); this.z = z; } public T getZ () { return z; } }
Triple is a generic class:
Triple <Integer > tr = new Triple <> (new Integer (1), new Integer (2), new Integer (3)); System.out.println( tr.getZ () );
SLIDE 16
Generics and Subtyping
If B is a subtype of A and G is a generic type, it is not the case that G<B> is a subtype of G<A>.
SLIDE 17
Generics and Subtyping
If B is a subtype of A and G is a generic type, it is not the case that G<B> is a subtype of G<A>. For example, a Triple<Integer> is-a Pair<Integer, Integer>:
Triple <Integer > tr = new Triple <> (new Integer (1), new Integer (2), new Integer (3)); Pair <Integer , Integer > pr = tr; // ok
SLIDE 18
Generics and Subtyping
If B is a subtype of A and G is a generic type, it is not the case that G<B> is a subtype of G<A>. For example, a Triple<Integer> is-a Pair<Integer, Integer>:
Triple <Integer > tr = new Triple <> (new Integer (1), new Integer (2), new Integer (3)); Pair <Integer , Integer > pr = tr; // ok
However, a Triple<Integer> is not a Triple<Object> even though an Integer is-an Object:
Triple <Integer > tr = new Triple <> (new Integer (1), new Integer (2), new Integer (3)); Triple <Object > tro = tr; // error!
SLIDE 19
Wildcards
The previous example raises an interesting question, is there a generic type that can be used as a supertype? Yes, Java has wildcard types.
SLIDE 20
Wildcards
The previous example raises an interesting question, is there a generic type that can be used as a supertype? Yes, Java has wildcard types. The type Triple<?> is a wildcard type that can be used to refer to any kind of Triple. For example the method
void printTriple( Triple <?> tr ) { System.out.println( "" + tr.getX () + ’\n’ + tr.getY () + ’\n’ + tr.getZ () ); }
can be used to print out any kind of triple.
SLIDE 21
Wildcards
The previous example raises an interesting question, is there a generic type that can be used as a supertype? Yes, Java has wildcard types. The type Triple<?> is a wildcard type that can be used to refer to any kind of Triple. For example the method
void printTriple( Triple <?> tr ) { System.out.println( "" + tr.getX () + ’\n’ + tr.getY () + ’\n’ + tr.getZ () ); }
can be used to print out any kind of triple. Wildcards can also be used to refer to subtypes or supertypes of parameter types, e.g.
G<? extends A> g1; // anything that is a subtype of A G<? super B> g2; // anything that is a supertype of B
SLIDE 22 Generic Methods
Like generic classes, we can declare generic methods that have one
- r more parametrized types.
SLIDE 23 Generic Methods
Like generic classes, we can declare generic methods that have one
- r more parametrized types.
For example, we can write a generic method to print a Triple as follows:
public static <T> void printTriple( Triple <T> tr ) { System.out.println( "" + tr.getX () + ’\n’ + tr.getY () + ’\n’ + tr.getZ () ); }
SLIDE 24 Generic Methods
Like generic classes, we can declare generic methods that have one
- r more parametrized types.
For example, we can write a generic method to print a Triple as follows:
public static <T> void printTriple( Triple <T> tr ) { System.out.println( "" + tr.getX () + ’\n’ + tr.getY () + ’\n’ + tr.getZ () ); }
When calling a generic method, the type is automatically inferred, e.g.
Triple <Integer > tr = new Triple <> (new Integer (1), new Integer (2), new Integer (3)); printTriple( tr ); // calls the generic method
SLIDE 25
Example: Generic Insertion Sort
To sort items, they have to be comparable. This is assured through the java.lang.Comparable<T> interface. We can use a generic method but restrict it to those types that implement the Comparable<T> interface:
public <T extends Comparable <T>> void insertionSort ( T[] data ) { for(int i = 1, j; i < data.length; i++) { T tmp = data[i]; for(j = i; j > 0 && tmp.compareTo(data[j -1]) < 0; j--) data[j] = data[j -1]; data[j] = tmp; } }
SLIDE 26
Caveats
Cannot declare static fields whose types are generic:
public class G<T> { private static T var; //not allowed //... }
SLIDE 27
Caveats
Cannot declare static fields whose types are generic:
public class G<T> { private static T var; //not allowed //... }
Cannot use primitive types as type parameters:
Triple <int > tr1; // not allowed Triple <Integer > tr2; // ok
SLIDE 28
Caveats
Cannot declare static fields whose types are generic:
public class G<T> { private static T var; //not allowed //... }
Cannot use primitive types as type parameters:
Triple <int > tr1; // not allowed Triple <Integer > tr2; // ok
Cannot instantiate generic types in a generic context:
T obj = new T(); // not allowed T[] arr = new T[10]; // not allowed
SLIDE 29
Caveats
Cannot declare static fields whose types are generic:
public class G<T> { private static T var; //not allowed //... }
Cannot use primitive types as type parameters:
Triple <int > tr1; // not allowed Triple <Integer > tr2; // ok
Cannot instantiate generic types in a generic context:
T obj = new T(); // not allowed T[] arr = new T[10]; // not allowed
There are more restrictions, please see https://docs.oracle.com/javase/tutorial/java/generics/ for details.