Chapter 4 · Robust, modern Java
Generics
Step 1 of 21 · Concept
One class, any type
ArrayList<String> and ArrayList<Integer> are one class, ArrayList. Its authors wrote it once, for any type
you put in the angle brackets. A class like that is generic.
In this lesson you will:
- write generic classes and methods of your own,
- limit which types they accept,
- see why it is
List<Integer>and notList<int>.
Step 2 of 21 · Concept
A box for any type
class Box<T> has a type parameter T: a name for "whatever type this box holds". Inside the class, T
stands wherever a type would go.
Box<String> is a box in which every T means String. new Box<>("hello") leaves the brackets empty: javac
fills them in from the variable's type.
Step 3 of 21 · Demo
A generic class
One class, two boxes: one for text, one for a number. Run it.
Step 4 of 21 · Quiz
Boxes do not mix
word is a Box<String>, number a Box<Integer>. Does word = number; compile?
Step 5 of 21 · Concept
What <String> does
The type in angle brackets is for javac. It checks every add and every get before the program runs.
While it runs, a List<String> is a list like any other: it holds arrows to objects.
Step 6 of 21 · Quiz
Who checks the type?
List<String> names = new ArrayList<>(); Who stops names.add(42)?
Step 7 of 21 · Exercise
Your turn: a pair
A class may have several type parameters: Pair<A, B>. Write it:
- a constructor that takes the two values,
first()andsecond(), which return them,swap(), which returns a newPair<B, A>the other way round,- a
toStringthat gives(first, second).
The program must print:
(Ada, 36)
(36, Ada)
39
Box<T> in the step A generic class is the model: a field of type T, a constructor, a method that returns it.
Two fields, private final A first; and private final B second;. In swap, the new pair gets second first.
Pair<B, A> swap() {
return new Pair<>(second, first);
}
Step 8 of 21 · Concept
A generic method
A single method can be generic too. Its type parameter goes in <T> before the return type:
static <T> T first(List<T> list) {
return list.get(0);
}
firstworks for a list of anything.- For a
List<String>it returns aString, for aList<Integer>anInteger.
Step 9 of 21 · Concept
compareTo puts things in order
String and Integer implement the interface Comparable. Its one method, compareTo, puts two values in order:
a.compareTo(b) is | means |
|---|---|
| negative | a comes before b |
| zero | they are equal |
| positive | a comes after b |
Numbers compare by size. Text compares letter by letter, as in a dictionary, but capitals come before small
letters: "Zoe" comes before "ada".
Step 10 of 21 · Quiz
Which comes first?
What is "Ada".compareTo("Grace")?
Step 11 of 21 · Concept
A bound limits T
Finding the largest item needs compareTo. An unknown T might be a Box, which has none.
<T extends Comparable<T>> is a bound: T may only be a type that implements Comparable<T>. In a bound,
extends means "extends or implements".
Step 12 of 21 · Quiz
Predict max
first returns the first item, max the largest. Type exactly what this prints.
List<String> names = List.of("Grace", "Ada", "Linus");
List<Integer> numbers = List.of(3, 9, 4);
System.out.println(first(names) + " " + first(numbers));
System.out.println(max(names) + " " + max(numbers));
Step 13 of 21 · Demo
Generic methods
Run it and compare with your answer. Then delete extends Comparable<T> from max and run again: javac refuses
compareTo.
Step 14 of 21 · Exercise
Your turn: count the matches
Write the generic method static <T> int count(List<T> list, T item). It returns how many items of list are
equal to item: compare with equals, as for text in lesson 9. The program must print:
"a" appears 3 times
2 appears 2 times
A counter that starts at 0, and a for-each loop over list. count needs no bound: every object has equals.
In the loop, if (x.equals(item)) adds 1 to the counter. Return it after the loop.
static <T> int count(List<T> list, T item) {
int n = 0;
for (T x : list) {
Step 15 of 21 · Concept
Why not List<int>?
A type parameter stands for a type of object: a class or an interface. int, double, boolean and char are
not classes: they are primitive types, plain values with no methods.
Java has a wrapper class for each, which holds one such value as an object:
| primitive | wrapper class |
|---|---|
int | Integer |
double | Double |
boolean | Boolean |
char | Character |
List<int> does not compile. Write List<Integer>.
Step 16 of 21 · Concept
An Integer is an object
An int sits right in its box. An Integer is an object on the heap, and the box holds an arrow to it.
Java boxes and unboxes for you, as in lesson 13: list.add(90) puts 90 into a new Integer.
Step 17 of 21 · Quiz
Predict the wrappers
Type exactly what this prints.
List<Integer> scores = new ArrayList<>();
scores.add(90);
scores.add(75);
int sum = scores.get(0) + scores.get(1);
System.out.println(sum);
Integer a = 1000;
Integer b = 1000;
System.out.println(a == b);
System.out.println(a.equals(b));
Step 18 of 21 · Demo
Wrapper types
Run it and compare with your answer. Then change both 1000s to 100 and run it again: now a == b is true.
Step 19 of 21 · Check yourself
Check yourself: a generic stack
A stack is a pile: you push items on top and pop them off the top. The call stack of lesson 2 works this way.
Write the generic class Stack<T>, keeping its items in an ArrayList<T>. An ArrayList has isEmpty() too,
and remove(i) returns the item it removes.
Then write the method largest. The program must print:
3 three
three two
1 false
11 true
The top of the stack is the end of the list. largest compares items, so its T needs a bound.
Step 20 of 21 · Challenge · optional
Challenge: any kind of number
Optional. It earns the lesson's third star.
sum adds up a List<Integer>. Make it work for a List<Double> too, so that the program compiles and prints:
6.0
4.0
A List<Double> is not a List<Integer>. Make sum a generic method with a bound.
Integer and Double both extend the class Number. Every Number has doubleValue(), its value as a
double.
static <T extends Number> double sum(List<T> list) {
double total = 0;
for (T n : list) {
total += n.doubleValue();
Step 21 of 21 · Recap
Recap
- A generic class or method has a type parameter,
<T>, that stands for a type. - javac checks the type in angle brackets. At run time a list holds only arrows.
- A bound,
<T extends Comparable<T>>, limitsTto types with the methods you need. - A type parameter is a class:
Integer, notint. Java boxes and unboxes for you; compare wrappers withequals.
Next: lesson 18 passes a small piece of code to a method, as a lambda.
Review
5 questions from this lesson, in a new order each time. Answer from memory: 3 of 5 right on the first try passes. On a day after you finish the lesson, a pass earns 15 XP.
The review needs JavaScript.
public class Main {
public static void main(String[] args) {
Box<String> word = new Box<>("hello");
Box<Integer> number = new Box<>(42);
System.out.println(word + " " + number);
String w = word.get();
int n = number.get();
System.out.println(w.length() + n);
}
}
public class Main {
public static void main(String[] args) {
Pair<String, Integer> p = new Pair<>("Ada", 36);
Pair<Integer, String> q = p.swap();
System.out.println(p);
System.out.println(q);
System.out.println(p.first().length() + q.first());
}
}
import java.util.List;
public class Main {
static <T> T first(List<T> list) {
return list.get(0);
}
static <T extends Comparable<T>> T max(List<T> list) {
T best = list.get(0);
for (T item : list) {
if (item.compareTo(best) > 0) {
best = item;
}
}
return best;
}
public static void main(String[] args) {
List<String> names = List.of("Grace", "Ada", "Linus");
List<Integer> numbers = List.of(3, 9, 4);
System.out.println(first(names) + " " + first(numbers));
System.out.println(max(names) + " " + max(numbers));
}
}
import java.util.List;
public class Main {
// write the method count here
public static void main(String[] args) {
List<String> letters = List.of("a", "b", "a", "c", "a");
List<Integer> numbers = List.of(1, 2, 2, 3);
int a = count(letters, "a");
int two = count(numbers, 2);
System.out.println("\"a\" appears " + a + " times");
System.out.println("2 appears " + two + " times");
}
}
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<Integer> scores = new ArrayList<>();
scores.add(90);
scores.add(75);
int sum = scores.get(0) + scores.get(1);
System.out.println(sum);
Integer a = 1000;
Integer b = 1000;
System.out.println(a == b);
System.out.println(a.equals(b));
}
}
public class Main {
// largest: takes every item off the stack
// and returns the largest one
static <T> T largest(Stack<T> stack) {
return null;
}
public static void main(String[] args) {
Stack<String> words = new Stack<>();
words.push("one");
words.push("two");
words.push("three");
System.out.println(words.size() + " " + words.peek());
System.out.println(words.pop() + " " + words.pop());
boolean empty = words.isEmpty();
System.out.println(words.size() + " " + empty);
Stack<Integer> numbers = new Stack<>();
numbers.push(4);
numbers.push(11);
numbers.push(7);
Integer big = largest(numbers);
System.out.println(big + " " + numbers.isEmpty());
}
}
import java.util.List;
public class Sum {
static double sum(List<Integer> list) {
double total = 0;
for (Integer n : list) {
total += n;
}
return total;
}
public static void main(String[] args) {
System.out.println(sum(List.of(1, 2, 3)));
System.out.println(sum(List.of(1.5, 2.5)));
}
}