Generics are a mechanism that defers "which type to deal with" to the time of defining class/method. The brackets in List<String> are the placeholders. It is safe because the type is enforced at compile time, and can handle a variety of types, so it has high reusability.
Learning Objectives
- Define the generic class
Box<T> - Write a generic method
<T> T choose(T a, T b) - Distinguish between wildcards
?,? extends T, and? super T. - Know the effect of bounded type (
<T extends Number>) - A glimpse at the concept of type erasure
Key concepts
1) Generic class
public class Box<T> {
private T value;
public void set(T v) { this.value = v; }
public T get() { return value; }
}
Box<String> b = new Box<>();
b.set("hi");
String s = b.get(); //No casting required
2) Generic method
static <T> T choose(boolean first, T a, T b) {
return first ? a : b;
}
String r = choose(true, "yes", "no");
3) Wildcard
List<? extends Number> nums = ...; //Number or its children (close to read-only)
List<? super Integer> ints = ...; //Integer or its parent (writable)
There is a memorized rule called PECS: Producer-Extends, Consumer-Super. When the value is taken out, it is extends, and when it is put in, it is super.
4) bounded type
static <T extends Number> double sum(List<T> xs) {
double s = 0;
for (T x : xs) s += x.doubleValue();
return s;
}
You can only receive Number or its children, so you can safely call parent methods like doubleValue().
5) Type erasure
Generic information is lost at runtime (List<String> ↔ List<Integer> appears to be the same class). So a code like new T() is not possible.
See as example
Example 1 — BoxGeneric.java: Generic class
public class BoxGeneric {
static class Box<T> {
private T value;
public void set(T v) { this.value = v; }
public T get() { return value; }
}
public static void main(String[] args) {
Box<String> s = new Box<>();
s.set("Hello");
System.out.println(s.get());
Box<Integer> n = new Box<>();
n.set(42);
System.out.println(n.get() + 1);
}
}
Execution result
Hello
43
Note: The diamond operator <> infers the right-hand type.
Example 2 — GenericMethod.java: Generic method
import java.util.List;
public class GenericMethod {
static <T> T first(List<T> list) {
return list.isEmpty() ? null : list.get(0);
}
public static void main(String[] args) {
System.out.println(first(List.of(1, 2, 3)));
System.out.println(first(List.of("a", "b")));
Object none = first(List.of());
System.out.println(none);
}
}
Execution result
1
a
null
Note: <T> in the method signature is inferred when calling the method.
Example 3 — Wildcards.java : ? extends and ? super
import java.util.ArrayList;
import java.util.List;
public class Wildcards {
/** Sum any list of numbers.*/
static double sum(List<? extends Number> xs) {
double s = 0;
for (Number n : xs) s += n.doubleValue();
return s;
}
/** Add 1 to 3 to Integer or its parent.*/
static void addNums(List<? super Integer> xs) {
xs.add(1); xs.add(2); xs.add(3);
}
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)));
List<Number> ns = new ArrayList<>();
addNums(ns);
System.out.println(ns);
}
}
Execution result
6.0
4.0
[1, 2, 3]
Memo: extends when taken out and used, super when inserted.
Example 4 — BoundedType.java : T extends Number
import java.util.List;
public class BoundedType {
static <T extends Number> double average(List<T> xs) {
if (xs.isEmpty()) return 0;
double s = 0;
for (T x : xs) s += x.doubleValue();
return s / xs.size();
}
public static void main(String[] args) {
System.out.println(average(List.of(10, 20, 30)));
System.out.println(average(List.of(1.5, 2.5, 3.5)));
}
}
Execution result
20.0
2.5
Note: Thanks to T extends Number, you can handle Integer/Double/Long etc. in one method.
Common Mistakes
- Misunderstanding that
Box<Object>andBox<String>are compatible (unrelated) - Try
new T()/new T[10]→ Not possible due to type erasure - Use of raw type (
List list = ...) → Compilation warning + runtime risk - Call
addto collection? extends T(most likely blocked) - Overuse of wildcards in method return types → inconvenience to the caller
Cleanup
- Generics are a tool for compile-time type safety
<T>is available at class/method level- Wildcards are easy to memorize using PECS rules.
- Types are erased at runtime, so some restrictions exist.
Try it yourself
cd 03_Collection_Generic/13_Generic/src
javac BoxGeneric.java
java BoxGeneric