Cosmic Module

J

Qubits of DPK

March 15, 2026

Core Java

Layman Explanation

Imagine a box that can hold ANYTHING — but you want a box that only holds apples. If someone tries to put an orange in, you want an error immediately, not when you open the box later.
Generics let you create classes, methods, and interfaces that work with any type but are type-safe at compile time.

Real World Analogy

A printer that prints documents. Whether you give it a Word doc, PDF, or image — it prints anything. But it guarantees: what you put in is what comes out. You put in a PDF, you get a PDF back — not a random document.

What Are Generics?

Generics allow you to write classes, methods, and interfaces that are parameterized by type — meaning the type is a variable that gets resolved at compile time.
java
QUBITS OF DPK
1// Without generics — dangerous
2List list = new ArrayList();
3list.add("hello");
4list.add(123);           // no error!
5String s = (String) list.get(1);  // ClassCastException at runtime!
6
7// With generics — type safe
8List<String> list = new ArrayList<String>();
9list.add("hello");
10list.add(123);           // COMPILE ERROR — caught immediately!
11String s = list.get(0);  // no cast needed

Generic Class

java
QUBITS OF DPK
1// T is a type parameter — acts like a variable for type
2class Box<T> {
3    private T value;
4
5    Box(T value) { this.value = value; }
6
7    T getValue() { return value; }
8
9    void setValue(T value) { this.value = value; }
10}
11
12// Usage
13Box<String>  nameBox  = new Box<>("Deepak");
14Box<Integer> ageBox   = new Box<>(25);
15Box<Double>  priceBox = new Box<>(99.99);
16
17System.out.println(nameBox.getValue());   // Deepak
18System.out.println(ageBox.getValue());    // 25

Multiple Type Parameters

java
QUBITS OF DPK
1class Pair<K, V> {
2    private K key;
3    private V value;
4
5    Pair(K key, V value) { this.key = key; this.value = value; }
6
7    K getKey()   { return key; }
8    V getValue() { return value; }
9
10    @Override
11    public String toString() { return key + " = " + value; }
12}
13
14Pair<String, Integer> score = new Pair<>("Deepak", 95);
15System.out.println(score);  // Deepak = 95

Generic Method

java
QUBITS OF DPK
1class Utils {
2    // Generic method — T resolved per call
3    static <T> void printArray(T[] array) {
4        for (T element : array) {
5            System.out.print(element + " ");
6        }
7        System.out.println();
8    }
9
10    // Generic method that returns type
11    static <T> T getFirst(List<T> list) {
12        return list.get(0);
13    }
14}
15
16Integer[] nums   = {1, 2, 3};
17String[]  names  = {"Deepak", "Arjun"};
18
19Utils.printArray(nums);    // 1 2 3
20Utils.printArray(names);   // Deepak Arjun

Bounded Type Parameters

Restrict which types can be used as type arguments.

Upper Bounded — extends

java
QUBITS OF DPK
1// T must be Number or its subclass (Integer, Double, Long...)
2static <T extends Number> double sum(List<T> list) {
3    double total = 0;
4    for (T item : list) {
5        total += item.doubleValue();  // safe — T is always a Number
6    }
7    return total;
8}
9
10List<Integer> ints    = List.of(1, 2, 3);
11List<Double>  doubles = List.of(1.5, 2.5);
12
13System.out.println(sum(ints));     // 6.0
14System.out.println(sum(doubles));  // 4.0

Multiple Bounds

java
QUBITS OF DPK
1// T must extend both Comparable AND Serializable
2<T extends Comparable<T> & Serializable> T findMax(T a, T b) {
3    return a.compareTo(b) >= 0 ? a : b;
4}

Wildcards

Wildcards use ? — represent an unknown type.

Unbounded Wildcard — <?>

java
QUBITS OF DPK
1void printList(List<?> list) {
2    for (Object item : list) {
3        System.out.println(item);
4    }
5}
6// Works for ANY List — List<String>, List<Integer>, List<Double>...
7printList(List.of(1, 2, 3));
8printList(List.of("a", "b"));

Upper Bounded Wildcard — <? extends Type>

java
QUBITS OF DPK
1// Read-only: accepts List<Integer>, List<Double>, List<Number>
2void printNumbers(List<? extends Number> list) {
3    for (Number n : list) {
4        System.out.println(n.doubleValue());
5    }
6    // list.add(1.0);  // ❌ can't add — type unknown!
7}

Lower Bounded Wildcard — <? super Type>

java
QUBITS OF DPK
1// Write: accepts List<Integer>, List<Number>, List<Object>
2void addNumbers(List<? super Integer> list) {
3    list.add(1);
4    list.add(2);
5    list.add(3);   // ✅ safe to add Integer
6}

PECS Rule — Producer Extends, Consumer Super

javascript
QUBITS OF DPK
1Reading from a collection (producer)  → use <? extends T>
2Writing to a collection  (consumer)   → use <? super T>

Type Erasure — How Generics Work at Runtime

Generics are a compile-time feature only. At runtime, all type parameters are erased:
java
QUBITS OF DPK
1List<String>  strList = new ArrayList<>();
2List<Integer> intList = new ArrayList<>();
3
4// At runtime, both are just:
5List strList = new ArrayList();
6List intList = new ArrayList();
7
8// So this is TRUE at runtime:
9strList.getClass() == intList.getClass()  // true! Both are ArrayList
The compiler inserts casts automatically based on the generic type information.

Generic Interface

java
QUBITS OF DPK
1interface Repository<T, ID> {
2    T findById(ID id);
3    void save(T entity);
4    List<T> findAll();
5    void delete(ID id);
6}
7
8// Concrete implementation
9class UserRepository implements Repository<User, Long> {
10    @Override
11    public User findById(Long id) { /* DB query */ return null; }
12
13    @Override
14    public void save(User user) { /* INSERT */ }
15
16    @Override
17    public List<User> findAll() { /* SELECT * */ return null; }
18
19    @Override
20    public void delete(Long id) { /* DELETE */ }
21}
This is exactly how Spring Data JPA works — JpaRepository<Entity, ID> is a generic interface!

Production Use Cases

java
QUBITS OF DPK
1// 1. Spring Data JPA repositories
2public interface UserRepository extends JpaRepository<User, Long> { }
3
4// 2. API response wrapper
5class ApiResponse<T> {
6    private int status;
7    private String message;
8    private T data;  // could be User, Order, Product...
9}
10ApiResponse<User>  userResponse  = new ApiResponse<>();
11ApiResponse<Order> orderResponse = new ApiResponse<>();
12
13// 3. Generic service layer
14class CrudService<T, ID> {
15    T findById(ID id) { /* ... */ return null; }
16    void save(T entity) { /* ... */ }
17}
18
19// 4. Result type (like Optional but with error)
20class Result<T> {
21    private T value;
22    private String error;
23    static <T> Result<T> success(T value) { return new Result<>(value, null); }
24    static <T> Result<T> failure(String err) { return new Result<>(null, err); }
25}

️ All Traps

java
QUBITS OF DPK
1// Trap 1 — Cannot create generic array
2T[] array = new T[10];         // ❌ compile error (type erasure)
3Object[] array = new Object[10]; // ✅ workaround
4
5// Trap 2 — Cannot use primitive types
6List<int> list = new ArrayList<>();   // ❌
7List<Integer> list = new ArrayList<>(); // ✅ use wrapper
8
9// Trap 3 — instanceof with generics
10if (list instanceof List<String>) { } // ❌ compile error (erasure)
11if (list instanceof List<?>) { }      // ✅
12
13// Trap 4 — static members can't use type parameter
14class Box<T> {
15    static T value;  // ❌ T is per-instance, static is per-class
16}
17
18// Trap 5 — Wildcards are read-only
19List<? extends Number> nums = new ArrayList<>();
20nums.add(1.0);  // ❌ can't add to ? extends
21
22// Trap 6 — Raw type bypasses generics
23List rawList = new ArrayList();
24rawList.add("hello");
25rawList.add(123);   // ⚠️ no error but loses type safety

30-Second Interview Answer

"Generics allow writing type-safe, reusable code by parameterizing types at compile time. Instead of List list (unsafe), you write List<String> list — the compiler enforces type safety and eliminates manual casting. Bounded type parameters restrict which types are allowed (extends for upper bound). Wildcards (?) represent unknown types — extends for reading, super for writing (PECS rule). Generics are implemented via type erasure — all type information is removed at runtime, making List<String> and List<Integer> the same class at runtime. Used everywhere in Java's Collections API and Spring Data JPA repositories."

Interview Questions & MAANG-Level Answers

Q1. What are generics and why were they introduced?
Generics allow classes, methods, and interfaces to be parameterized by type. Introduced in Java 5 to solve two problems: (1) Type safety — without generics, collections accepted any Object and ClassCastExceptions occurred at runtime. With generics, type errors are caught at compile time. (2) Eliminate castinglist.get(0) returns the typed String directly without (String) cast. Third benefit: code reuse — one Box<T> works for any type instead of writing BoxString, BoxInteger etc.
Q2. What is type erasure?
Type erasure is the process by which the Java compiler removes all generic type information after compilation. List<String> becomes List, Box<T> becomes Box. The compiler inserts appropriate casts automatically. Consequences: (1) Cannot create new T[] — type unknown at runtime. (2) list instanceof List<String> is illegal — no type info at runtime. (3) List<String>.class doesn't exist — only List.class. (4) Overloading methods by generic type only is impossible since signatures are identical after erasure.
Q3. What is the difference between <T extends Number> and <? extends Number>?
<T extends Number> is a bounded type parameter — T is a named type that can be referred to throughout the method/class: T get(T input) { return input; }. <? extends Number> is a bounded wildcard — anonymous type, cannot refer to it by name. Use T when you need to use the type multiple times in signatures. Use ? when you just need to accept a variety of types without referring to the specific type.
Q4. What is the PECS rule?
PECS = Producer Extends, Consumer Super. When a collection PRODUCES (you read from it) → use <? extends T>. When a collection CONSUMES (you write to it) → use <? super T>. Example: Collections.copy(List<? super T> dest, List<? extends T> src)src produces elements (extends), dest consumes them (super). <?> (unbounded) when you only call methods available on Object.
Q5. Why can't you use primitive types with generics?
Generics work with Objects only — primitives (int, double, char etc.) are not Objects. List<int> is a compile error. Use wrapper classes instead: List<Integer>, List<Double>. Autoboxing/unboxing happens automatically: list.add(5) autoboxes 5 to Integer. Performance note: heavy use of generics with primitives causes autoboxing overhead — for performance-critical code with large primitive arrays, consider primitive arrays or specialized libraries.