Week 5 Day 1 — Why Generics and Type Parameters
Goal
Today I want generics as compile-time types on other types, so List<Order> is not a bag of Object.
Main questions:
- What problem do generics solve?
- What is a type parameter?
- How do generic classes and methods look?
- What is a raw type?
- How does this show up in Spring Data?
1. The problem without generics
List orders = new ArrayList();
orders.add(new Order(1L));
orders.add("oops");
Order first = (Order) orders.get(0); // I hope
The list holds Object. Every get needs a cast. The compiler cannot stop me from inserting a String. The failure is a ClassCastException at runtime, often far from the add.
Generics move that check to compile time.
List<Order> orders = new ArrayList<>();
orders.add(new Order(1L));
// orders.add("oops"); // does not compile
Order first = orders.get(0); // no cast in my code
The compiler still inserts a cast in bytecode (Day 3). I do not write it.
Memory sentence:
Generics are a compile-time contract:
List<Order>holds orders, andgetreturnsOrder.
2. Type parameters
A type parameter is a placeholder for a type, written in angle brackets.
public class Box<T> {
private T value;
public void set(T value) {
this.value = value;
}
public T get() {
return value;
}
}
Box<String> names = new Box<>();
Box<Order> orders = new Box<>();
Conventions:
| Parameter | Usual meaning |
|---|---|
T | a type |
E | element of a collection |
K, V | map key and value |
N | a number |
S, T | source and target (Spring Converter<S,T>) |
ID | identifier (JpaRepository<T, ID>) |
T is not a class. It is a name the compiler replaces with a real type at each use site. Box<String> and Box<Order> share one class file after erasure. They are different types to the compiler.
The diamond <> lets the compiler infer the parameter from the left-hand side: new ArrayList<>().
3. Generic methods
A method can have its own type parameters, even on a non-generic class.
public static <T> T first(List<T> items) {
return items.get(0);
}
Order order = first(orders); // T inferred as Order
The <T> before the return type belongs to the method. Callers rarely write this.<Order>first(orders); inference is enough.
A class can mix both: class Repository<T, ID> with <S extends T> S save(S entity).
4. Raw types
A raw type is the generic class used without parameters: List, Box.
They exist so pre-Java-5 code still compiles. New code should not use them. Mixing raw List with List<Order> produces unchecked warnings and can put a String into a list the rest of the program treats as List<Order> — heap pollution. Then a later get throws ClassCastException in innocent code.
List raw = new ArrayList();
List<Order> orders = raw; // warning
raw.add("nope");
Order o = orders.get(0); // ClassCastException here, not at add
Memory sentence:
Raw types are a compatibility hole. I do not open them in new code.
5. Spring connection
Spring Data is generic at the type level:
public interface OrderRepository extends JpaRepository<Order, Long> {}
T is Order, ID is Long. Query methods return Order, List<Order>, Optional<Order> because the interface filled in those parameters.
Converter<S, T>, ResponseEntity<T>, RestClient.body(new ParameterizedTypeReference<List<Order>>() {}) exist because the compiler knows T and the JVM, after erasure, often does not (Day 3).
I still write List<Order> in services, not List.
6. Common traps
Trap 1: List orders in new code. Raw type.
Trap 2: Thinking Box<String> and Box<Order> are different classes at runtime. They share one class. The compiler’s view differs.
Trap 3: Casting (List<Order>) someList to silence a warning. That is a lie if the list is mixed.
Trap 4: Primitives as type arguments: List<int> is illegal. Use List<Integer>. Boxing applies (Week 1).
Practice Questions and Answers
Question 1
What problem do generics solve?
Answer:
They let the compiler enforce what a collection or container holds, so I do not cast on every get and I fail at compile time instead of with ClassCastException later.
Question 2
Is Box<String> a different class from Box<Order>?
Answer:
To the compiler, they are different types. At runtime they share the same Box class after erasure. Instanceof and getClass() cannot see String vs Order.
Question 3
What is a raw type, and why avoid it?
Answer:
The generic type used without parameters: List. It disables the checks. Mixing raw and parameterized lists can pollute the heap; the crash shows up on get, not on add.
Question 4
Why JpaRepository<Order, Long> rather than a non-generic repository?
Answer:
The interface describes “entities of type T with id ID.” Filling in Order and Long makes save, findById, and findAll return the right types without casts in my application code.
Question 5
Why can I not write List<int>?
Answer:
Type arguments must be reference types. Generics erase to Object (or a bound). Primitives are not objects. I use Integer and accept boxing.
Memory sentences
Generics are a compile-time contract on what a type contains.
Tis a placeholder.Box<String>andBox<Order>share one runtime class.
Raw types are a compatibility hole. Do not use them in new code.