Zum Hauptinhalt springen

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:

  1. What problem do generics solve?
  2. What is a type parameter?
  3. How do generic classes and methods look?
  4. What is a raw type?
  5. 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, and get returns Order.


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:

ParameterUsual meaning
Ta type
Eelement of a collection
K, Vmap key and value
Na number
S, Tsource and target (Spring Converter<S,T>)
IDidentifier (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.

T is a placeholder. Box<String> and Box<Order> share one runtime class.

Raw types are a compatibility hole. Do not use them in new code.

Next: Week 5 Day 2 — Bounds and Generic Methods