🧰
OOP

Generics

Type-Safe Reusable Code
💡 Generics are like a universal toolbox where you can label it "this time I'll only hold screws (Integer)" or "this time only nails (String)" — same box, but only one type of thing goes in, no mix-ups.

Generics <T> let us build classes/methods that "work for any type", without writing separate versions.

You get compile-time type-safety — the compiler flags an error immediately if you try to insert the wrong type, saving you from a run-time crash.

class Box<T> {
  T item;
  void set(T item) { this.item = item; }
  T get() { return item; }
}
Box<String> b = new Box<>();
b.set("Hello");
🧰
Generics are like a universal toolbox where you can label it "this time I'll only hold screws (Integer)" or "this time only nails (String)" — same box, but only one type of thing goes in, no mix-ups.
1 / 4
⚡ Quick Recap
  • <T> lets one class work for many types
  • Gives compile-time type safety
  • Examples: List<String>, Box<Integer>
On this page (4 subtopics)

It's not just classes — individual methods can be generic too, defining their own type parameter, even if the class itself isn't generic. You write <T> before the return type in the method definition.

static <T> T firstElement(List<T> list) {
  return list.get(0);
}

<T extends Number> means T can only be Number or one of its subclasses (Integer, Double, etc.) — this lets you call Number's methods (like .doubleValue()) on T, which isn't otherwise allowed (the compiler wouldn't know if T has that method, unless a bound is given).

static <T extends Number> double sum(List<T> list) {
  double total = 0;
  for (T item : list) total += item.doubleValue(); // Number ka method
  return total;
}

? extends Type means "Type or any of its subclasses" (good for read-only use — a "producer"; you can read from it, but can't add to it, since the compiler doesn't know the exact type). ? super Type means "Type or any of its superclasses" (good for writing — a "consumer"). This is called the "PECS" principle: Producer Extends, Consumer Super.

void printAll(List<? extends Number> list) { // read-only OK
  for (Number n : list) System.out.println(n);
}

Java generics are a compile-time feature — the generic type information gets "erased" at runtime, for backward compatibility (so older Java code, written before generics existed, can still run on newer JVMs unmodified). This is why List<String> and List<Integer> look like the same class (List) at runtime, and you can't directly instantiate something like new T() (the JVM has no idea what T is at runtime).