Chapter 22
Generic Programming
Chapter Goals
- To understand the objective of generic programming
- To be able to implement generic classes and methods
- To understand the execution of generic methods in the virtual
machine
- To know the limitations of generic programming in Java
- To understand the relationship between generic types and
inheritance
- To learn how to constrain type variables
Type Variables
Type Variables
Type Variables
Type Variables Increase Safety
- Type variables make generic code safer and easier to read
Syntax 22.1: Instantiating a Generic Class
GenericClassName<Type1, Type2, . . .>
|
|
Example:
|
ArrayList<BankAccount> HashMap<String, Integer>
|
Purpose:
To supply specific types for the type variables of a generic class |
Self Check
- The standard library provides a class HashMap<K, V>
with key type K and value
type V. Declare a hash map that maps strings to integers.
- The binary search tree class in Chapter 21 is an example of
generic programming
because you can use it with any classes that implement the Comparable
interface. Does it achieve genericity through inheritance or type
variables?
Answers
- HashMap<String, Integer>
- It uses inheritance.
Implementing Generic Classes
- Example: simple generic class that stores pairs of objects
Pair<String, BankAccount> result
= new Pair<String, BankAccount>("Harry Hacker", harrysChecking);
- The getFirst and getSecond retrieve first and
second values of pair
String name = result.getFirst();
BankAccount account = result.getSecond();
- Example of use: return two values at the same time (method
returns a Pair)
- Generic Pair class requires two type variables
public class Pair<T, S>
Good Type Variable Names
Type Variable |
Name Meaning |
E |
Element type in a
collection |
K |
Key type in a map |
V |
Value type in a map |
T |
General type |
S, U |
Additional general types |
Class Pair
public class Pair<T
, S
>
{
public Pair(T
firstElement, S
secondElement)
{
first = firstElement;
second = secondElement;
}
public T
getFirst() { return first; }
public S
getSecond() { return second; }
private T
first;
private S
second;
}
Turning LinkedList into a Generic Class
public class LinkedList<E
>
{
. . .
public E
removeFirst()
{
if (first == null)
throw new NoSuchElementException();
E
element = first.data;
first = first.next;
return element;
}
. . .
private Node first;
private class Node
{
public E
data;
public Node next;
}
}
Implementing Generic Classes
- Use type E when you receive, return, or store an
element object
- Complexities arise only when your data structure uses helper
classes
- If helpers are inner classes, no need to do anything special
- Helper types defined outside generic class need to become generic
classes too
public class ListNode<E>
Syntax 22.2: Defining a Generic Class
accessSpecifier class GenericClassName<TypeVariable1, TypeVariable2, . . .> { constructors methods fields }
|
|
Example:
|
public class Pair<T, S> { . . . }
|
Purpose:
To define a generic class with methods and fields that depend on type
variables |
File LinkedList.java
File ListIterator.java
File ListTester.java
Output
Dick
Harry
Juliet
Nina
Tom
Self Check
- How would you use the generic Pair class to
construct a pair of strings "Hello"
and "World"?
- What change was made to the ListIterator interface, and
why was that change necessary?
Answers
- new Pair<String, String>("Hello", "World")
- ListIterator<E> is now a generic type. Its
interface depends on the element type of the linked list.
Generic Methods
- Generic method: method with a type variable
- Can be defined inside ordinary and generic classes
- A regular (non-generic) method:
/**
Prints all elements in an array of strings.
@param a the array to print
*/
public static void print(String[] a)
{
for (String e : a)
System.out.print(e + " ");
System.out.println();
}
- What if we want to print an array of Rectangle objects
instead?
public static <E
> void print(E
[] a)
{
for (E
e : a)
System.out.print(e + " ");
System.out.println();
}
Generic Methods
Syntax 22.1: Instantiating a Generic Class
modifiers <TypeVariable1, TypeVariable2, . . .> returnType methodName(parameters) { body }
|
|
Example:
|
public static <E> void print(E [] a) { . . . }
|
Purpose:
To define a generic method that depends on type variables |
Self Check
- Exactly what does the generic print method
print when you pass an array of BankAccount objects
containing two bank accounts with zero
balances?
- Is the getFirst method of the Pair class a
generic method?
Answers
- The output depends on the definition of the toString
method in the BankAccount class.
- No–the method has no type parameters. It is an ordinary method in
a generic class.
Constraining Type Variables
Constraining Type Variables
Self Check
- Declare a generic BinarySearchTree class with
an appropriate type variable.
- Modify the min method to compute the minimum of an
array of elements that implements the Measurable interface of
Chapter 11.
Answers
- public class BinarySearchTree<E extends
Comparable>
-
public static <E extends Measurable> E min(E[] a)
{
E smallest = a[0];
for (int i = 1; i < a.length; i++)
if (a[i].getMeasure() < smallest.getMeasure()) < 0)
smallest = a[i];
return smallest;
}
Wildcard Types
Name |
Syntax |
Meaning |
Wildcard with lower bound |
? extends B |
Any subtype of B |
Wildcard with upper bound |
? super B |
Any supertype of B |
Unbounded wildcard |
? |
Any type |
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)
-
public static <E extends Comparable<? super E>> E min(E[] a)
-
static void reverse(List<?> list)
You can think of that declaration as a shorthand for
static void <T> reverse(List<T> list)
Raw Types
Raw Types
Self Check
- What is the erasure of the print method in
Section 22.3?
- What is the raw type of the LinkedList<E> class
in Section 22.2?
Answers
-
public static void print(Object[] a)
{
for (Object e : a)
System.out.print(e + " ");
System.out.println();
}
- The LinkedList class of Chapter 20.