Lesson 127 min read

Generics

Write code that works with any type

The Problem Generics Solve

Imagine you build a gift box. One day you put a book in it, another day a toy, another day chocolates. Without generics, Java would treat every box the same — you'd put stuff in as Object and have to guess what's inside when you take it out. That's messy and error-prone.

Generics let you create a box and say: "This box is specifically for books." Now Java knows exactly what's inside, catches mistakes at compile time, and you never have to guess.

You've already used generics without realizing it! ArrayList<String> is a generic ArrayList that only holds Strings. The <String> part is the type parameter.

Creating a Generic Class

public class Main {
// T is a placeholder — it can be ANY type
static class Box<T> {
private T item;
void put(T item) {
this.item = item;
System.out.println("Put in: " + item);
}
T get() {
return item;
}
boolean isEmpty() {
return item == null;
}
}
public static void main(String[] args) {
// A box for Strings
Box<String> nameBox = new Box<>();
nameBox.put("Hello World");
String name = nameBox.get(); // no casting needed!
System.out.println("Got: " + name);
// A box for Integers
Box<Integer> numBox = new Box<>();
numBox.put(42);
int number = numBox.get(); // autoUnboxing
System.out.println("Got: " + number);
// A box for Doubles
Box<Double> priceBox = new Box<>();
priceBox.put(9.99);
System.out.println("Got: " + priceBox.get());
// numBox.put("oops"); // COMPILE ERROR! Type safety!
}
}
Output
Put in: Hello World
Got: Hello World
Put in: 42
Got: 42
Put in: 9.99
Got: 9.99

Generic Methods & Bounded Types

You don't have to make the whole class generic — you can make individual methods generic too. Just put the type parameter before the return type.

Bounded types let you restrict what types are allowed. <T extends Number> means "T can be Integer, Double, Float, etc. — anything that IS-A Number." This lets you use Number methods like .doubleValue() inside your generic code.

Wildcards (?) are used when you want to accept "any type" but don't need to name it. List<?> means "a list of anything." List<? extends Number> means "a list of some Number type."

Generic Methods

import java.util.Arrays;
import java.util.List;
public class Main {
// Generic method — works with any array type
static <T> void printArray(T[] array) {
System.out.print("[ ");
for (T item : array) {
System.out.print(item + " ");
}
System.out.println("]");
}
// Bounded type — T must be a Number or subclass
static <T extends Number> double sum(T[] numbers) {
double total = 0;
for (T num : numbers) {
total += num.doubleValue(); // works because T is a Number
}
return total;
}
// Wildcard — accept list of any Number subtype
static double sumList(List<? extends Number> list) {
double total = 0;
for (Number n : list) {
total += n.doubleValue();
}
return total;
}
public static void main(String[] args) {
String[] names = {"Alice", "Bob", "Charlie"};
Integer[] nums = {1, 2, 3, 4, 5};
Double[] prices = {4.99, 2.50, 8.75};
printArray(names);
printArray(nums);
printArray(prices);
System.out.println("Sum of ints: " + sum(nums));
System.out.println("Sum of doubles: " + sum(prices));
// Wildcard in action
List<Integer> intList = Arrays.asList(10, 20, 30);
List<Double> dblList = Arrays.asList(1.5, 2.5, 3.5);
System.out.println("Int list sum: " + sumList(intList));
System.out.println("Dbl list sum: " + sumList(dblList));
}
}
Output
[ Alice Bob Charlie ]
[ 1 2 3 4 5 ]
[ 4.99 2.50 8.75 ]
Sum of ints: 15.0
Sum of doubles: 16.24
Int list sum: 60.0
Dbl list sum: 7.5

Generic Class with Multiple Type Parameters

public class Main {
// A Pair holds two values of potentially different types
static class Pair<K, V> {
private K key;
private V value;
Pair(K key, V value) {
this.key = key;
this.value = value;
}
K getKey() { return key; }
V getValue() { return value; }
@Override
public String toString() {
return key + " -> " + value;
}
}
public static void main(String[] args) {
Pair<String, Integer> age = new Pair<>("Alice", 25);
Pair<String, String> color = new Pair<>("Sky", "Blue");
Pair<Integer, Boolean> evenCheck = new Pair<>(7, false);
System.out.println(age);
System.out.println(color);
System.out.println(evenCheck);
// Type-safe access
String name = age.getKey();
int years = age.getValue();
System.out.println(name + " is " + years + " years old");
}
}
Output
Alice -> 25
Sky -> Blue
7 -> false
Alice is 25 years old
Note: Here's a fun fact: generics only exist at compile time. At runtime, Java erases all the type information — this is called type erasure. Your Box<String> becomes just Box in the compiled code. This is why you can't do new T() or T[] arr = new T[10] — Java doesn't know what T is at runtime!

Quick check

What does mean?
Collections FrameworkException Handling