Java · Collections · Topic 7
Generics
Build type-safe reusable Java classes, methods, and interfaces with bounds, wildcards, inference, and PECS.
Step 1 of 20
7.1 Introduction to Generics
Explain generic type arguments and compile-time type safety.
A generic type is written inside angle brackets. ArrayList<String> accepts strings, while ArrayList<Integer> accepts integers.
ArrayList<String> names = new ArrayList<>();
ArrayList<Integer> numbers = new ArrayList<>();
names.add("Amit");
// names.add(100); // compilation errorCheck your understanding: What do angle brackets specify in ArrayList<String>?
The permitted element type, String.
Step 2 of 20
7.2 Need for Generics
Contrast raw collections and runtime casts with generic compile-time checking.
ArrayList list = new ArrayList();
list.add("Java");
list.add(100);
String value = (String) list.get(1); // ClassCastExceptionArrayList<String> list = new ArrayList<>();
list.add("Java");
// list.add(100); // compilation errorCheck your understanding: Which exception can an incorrect raw-type cast cause?
ClassCastException.
Step 3 of 20
7.3 Advantages of Generics
Identify the practical benefits of generic code.
Advantages
- Compile-time type safety
- Earlier error detection
- No explicit retrieval casts
- Reusable classes and methods
- Clearer declarations
Map<Integer, String> students = new HashMap<>();Check your understanding: Why is String name = names.get(0) possible without casting?
The generic declaration guarantees that list elements are String values.
Step 4 of 20
7.4 Generic Type Parameters
Interpret conventional generic parameter names.
| Parameter | Meaning |
|---|---|
| T | Type |
| E | Element |
| K | Key |
| V | Value |
| N | Number |
| R | Return type |
class Box<T> {
T value;
}Check your understanding: Which conventional names represent map keys and values?
K and V.
Step 5 of 20
7.5 Generic Class
Define and instantiate a class whose field and methods share one type parameter.
class Box<T> {
private T value;
void setValue(T value) { this.value = value; }
T getValue() { return value; }
}
public class GenericClassDemo {
public static void main(String[] args) {
Box<Integer> integerBox = new Box<>();
integerBox.setValue(100);
System.out.println("Value: " + integerBox.getValue());
Box<String> stringBox = new Box<>();
stringBox.setValue("Java");
System.out.println("Value: " + stringBox.getValue());
}
}Value: 100
Value: JavaCheck your understanding: Can the same Box class store Integer in one object and String in another?
Yes, each object supplies its own type argument.
Step 6 of 20
7.6 Multiple Type Parameters
Use more than one generic placeholder in a class.
class Pair<K, V> {
private final K key;
private final V value;
Pair(K key, V value) { this.key = key; this.value = value; }
void display() { System.out.println(key + " : " + value); }
}
public class GenericPairDemo {
public static void main(String[] args) {
new Pair<>(101, "Amit").display();
new Pair<>("Laptop", 55000.0).display();
}
}101 : Amit
Laptop : 55000.0Check your understanding: What are the two types in Pair<Integer,String>?
Integer for K and String for V.
Step 7 of 20
7.7 Generic Method
Declare a method with its own type parameter before the return type.
public class GenericMethodDemo {
static <T> void display(T value) {
System.out.println(value);
}
public static void main(String[] args) {
display(100); display("Java"); display(45.75); display('A');
}
}100
Java
45.75
ACheck your understanding: Where is <T> written in a generic method declaration?
Before the return type.
Step 8 of 20
7.8 Generic Method with an Array
Process object arrays of different element types through one method.
public class GenericArrayDemo {
static <T> void displayArray(T[] array) {
for (T element : array) System.out.println(element);
}
public static void main(String[] args) {
displayArray(new Integer[]{10, 20, 30});
displayArray(new String[]{"Amit", "Neha", "Raj"});
}
}10
20
30
Amit
Neha
RajCheck your understanding: Can displayArray(T[]) accept both Integer[] and String[]?
Yes.
Step 9 of 20
7.9 Generic Interface
Define a type-safe interface and implement it with a fixed or preserved type.
interface Container<T> {
void add(T value);
T get();
}
class StringContainer implements Container<String> {
private String value;
public void add(String value) { this.value = value; }
public String get() { return value; }
}
public class GenericInterfaceDemo {
public static void main(String[] args) {
Container<String> container = new StringContainer();
container.add("Java Generics");
System.out.println(container.get());
}
}Java Genericsclass MyContainer<T> implements Container<T> {
private T value;
public void add(T value) { this.value = value; }
public T get() { return value; }
}Check your understanding: What type does StringContainer bind to Container<T>?
String.
Step 10 of 20
7.10 Bounded Type Parameters
Restrict a type parameter to a parent type and its subclasses.
class Calculator<T extends Number> {
private final T number;
Calculator(T number) { this.number = number; }
double square() { return number.doubleValue() * number.doubleValue(); }
}
public class BoundedTypeDemo {
public static void main(String[] args) {
System.out.println(new Calculator<>(5).square());
System.out.println(new Calculator<>(4.5).square());
}
}25.0
20.25Valid examples
- Integer
- Double
- Float
- Long
- Other Number subclasses
Check your understanding: Why is Calculator<String> invalid?
String does not extend Number.
Step 11 of 20
7.11 Multiple Bounds
Combine a class bound with one or more interface bounds.
<T extends Number & Comparable<T>>Check your understanding: Which bound must appear first?
The class bound.
Step 12 of 20
7.12 Wildcards
Explain the unknown-type symbol and the three wildcard forms.
| Form | Meaning |
|---|---|
| <?> | Unknown type |
| <? extends Type> | Type or a subtype |
| <? super Type> | Type or a supertype |
Check your understanding: What symbol represents an unknown generic type?
The question mark (?).
Step 13 of 20
7.13 Unbounded Wildcard <?>
Read values from a List of any element type.
import java.util.Arrays;
import java.util.List;
public class WildcardDemo {
static void displayList(List<?> list) {
for (Object element : list) System.out.println(element);
}
public static void main(String[] args) {
displayList(Arrays.asList(10, 20, 30));
displayList(Arrays.asList("Amit", "Neha"));
}
}10
20
30
Amit
NehaCheck your understanding: What common type can safely receive values read from List<?>?
Object.
Step 14 of 20
7.14 Upper-Bounded Wildcard <? extends Type>
Read a family of subtype lists through a common upper bound.
import java.util.Arrays;
import java.util.List;
public class UpperBoundedWildcardDemo {
static double calculateTotal(List<? extends Number> numbers) {
double total = 0;
for (Number number : numbers) total += number.doubleValue();
return total;
}
public static void main(String[] args) {
System.out.println(calculateTotal(Arrays.asList(10, 20, 30)));
}
}60.0Check your understanding: Can List<? extends Number> refer to List<Integer>?
Yes.
Step 15 of 20
7.15 Lower-Bounded Wildcard <? super Type>
Add values through a lower-bounded consumer.
import java.util.ArrayList;
import java.util.List;
public class LowerBoundedWildcardDemo {
static void addNumbers(List<? super Integer> list) {
list.add(10); list.add(20); list.add(30);
}
public static void main(String[] args) {
List<Number> numbers = new ArrayList<>();
addNumbers(numbers);
System.out.println(numbers);
}
}[10, 20, 30]Check your understanding: Which wildcard accepts List<Number> while allowing Integer insertion?
List<? super Integer>.
Step 16 of 20
7.16 Understanding extends and super
Apply the PECS rule to generic API design.
| Wildcard | Main purpose |
|---|---|
| <?> | Work with an unknown type |
| <? extends Type> | Read Type or subtype values |
| <? super Type> | Add Type or subtype values |
Check your understanding: What does PECS stand for?
Producer Extends, Consumer Super.
Step 17 of 20
7.17 Type Inference
Use compiler inference for constructors and generic methods.
ArrayList<String> names = new ArrayList<>();display("Java"); // T is String
display(100); // T is IntegerCheck your understanding: What type is inferred on the right side of ArrayList<String> names = new ArrayList<>()?
String.
Step 18 of 20
7.18 Generics and Primitive Types
Use wrapper classes because generic arguments must be reference types.
| Primitive | Wrapper |
|---|---|
| byte | Byte |
| short | Short |
| int | Integer |
| long | Long |
| float | Float |
| double | Double |
| char | Character |
| boolean | Boolean |
List<Integer> numbers = new ArrayList<>();
numbers.add(10); // int to Integer
int value = numbers.get(0); // Integer to intCheck your understanding: Which wrapper class represents int?
Integer.
Step 19 of 20
7.19 Raw Types
Recognize and avoid generic types used without a type argument.
ArrayList list = new ArrayList(); // raw type
ArrayList<String> names = new ArrayList<>(); // preferredRaw-type risks
- No compile-time element safety
- Mixed data types
- Explicit casts
- Runtime ClassCastException
Check your understanding: Why should ArrayList list usually be avoided?
It permits mixed types and may require unsafe casts.
Step 20 of 20
7.20 Limitations of Generics
Explain primitive restrictions, type erasure, instantiation limits, generic-array limits, and static context.
Key limitations
- Primitive type arguments are invalid
- Most type information is erased at runtime
- new T() is normally invalid
- new T[10] is invalid
- Static members cannot use a class-level type parameter directly
// T object = new T();
// T[] array = new T[10];Check your understanding: Can new T() normally create an instance of a type parameter?
No.