Question 11 · Section 20

What are Generics in Java

The main idea: you write code once, and it works with any type.

Language versions: English Russian Ukrainian

🟢 Junior Level

Generics are a mechanism in Java that allows creating classes, interfaces, and methods that work with different types while maintaining type safety.

The main idea: you write code once, and it works with any type.

// Without generics (before Java 5)
List list = new ArrayList();
list.add("Hello");
String s = (String) list.get(0);  // cast needed!

// With generics (Java 5+)
List<String> list = new ArrayList<>();
list.add("Hello");
String s = list.get(0);  // no cast!

Why they are needed:

  1. Type safety — error is caught at compile time
  2. No cast needed — code is cleaner and safer
  3. Reusability — one code for different types

Example:

// One class works with any type
public class Box<T> {
    private T value;

    public void set(T value) { this.value = value; }
    public T get() { return value; }
}

Box<String> stringBox = new Box<>();
stringBox.set("Hello");

Box<Integer> intBox = new Box<>();
intBox.set(42);

🟡 Middle Level

How it works

Type parameter (<T>) is a placeholder for a type that is specified at usage:

// Declaration
public class Box<T> {
    private T value;
    public void set(T value) { this.value = value; }
    public T get() { return value; }
}

// Usage
Box<String> box = new Box<>();  // T = String
box.set("Hello");
String s = box.get();  // automatically String, no cast

Multiple parameters:

public class Pair<K, V> {
    private K key;
    private V value;

    public Pair(K key, V value) {
        this.key = key;
        this.value = value;
    }

    public K getKey() { return key; }
    public V getValue() { return value; }
}

Pair<String, Integer> pair = new Pair<>("age", 25);

Bounded Type Parameters

You can constrain the type:

// Only Number and its subclasses
public class NumberBox<T extends Number> {
    private T value;
    public double doubleValue() { return value.doubleValue(); }
}

NumberBox<Integer> intBox = new NumberBox<>();  // ✅
NumberBox<String> strBox = new NumberBox<>();   // ❌ error

Common Mistakes

  1. Using raw types: ```java // ❌ Raw type — no type safety List list = new ArrayList(); list.add(“Hello”); list.add(42); // compiler doesn’t complain, but this is a bug!

// ✅ Parameterized type List list = new ArrayList<>(); list.add("Hello"); // list.add(42); // compilation error


2. **Expecting primitives to work:**
```java
// ❌ Primitives cannot be used as type parameters
List<int> list = new ArrayList<>();  // compilation error

// ✅ Use wrapper classes
List<Integer> list = new ArrayList<>();

Practical Application

1. Generic methods:

public static <T> T getFirst(List<T> list) {
    return list.isEmpty() ? null : list.get(0);
}

List<String> strings = List.of("a", "b", "c");
String first = getFirst(strings);  // T is inferred as String

2. Generic interfaces:

public interface Repository<T, ID> {
    Optional<T> findById(ID id);
    List<T> findAll();
    T save(T entity);
}

public class UserRepository implements Repository<User, Long> {
    public Optional<User> findById(Long id) { /* ... */ }
}

🔴 Senior Level

Internal Implementation

Type Erasure:

// Source code
public class Box<T> {
    private T value;
    public void set(T value) { this.value = value; }
    public T get() { return value; }
}

// After compilation (type erasure)
public class Box {
    private Object value;  // T -> Object (upper bound)
    public void set(Object value) { this.value = value; }
    public Object get() { return value; }
}

// Compiler adds cast at usage
Box<String> box = new Box<>();
box.set("Hello");
String s = (String) box.get();  // implicit cast

Bounded type erasure:

public class NumberBox<T extends Number> {
    private T value;
}

// After erasure: T -> Number (first bound)
public class NumberBox {
    private Number value;
}

Architectural Trade-offs

Type Erasure:

Pros Cons
Binary compatibility (Java 5 code works with Java 1.4) Cannot create new T()
No runtime overhead Cannot check obj instanceof T
  Problems with arrays (new T[])
  Bridge methods needed to preserve polymorphism

// Bridge Method — a synthetic method that the compiler adds // to preserve polymorphism after type erasure.

Edge Cases

1. Generic array creation:

// ❌ Cannot create array of generic type
public class Box<T> {
    private T[] array = new T[10];  // compilation error
}

// ✅ Solution — Object array with cast
public class Box<T> {
    private T[] array = (T[]) new Object[10];
    // ⚠️ Dangerous: if you return this array externally, caller may get ClassCastException.
    // ArrayList uses Object[] internally, but controls all writes — this is safe.
    // Your code — not always.
}

2. Instanceof with generics:

// ❌ Cannot do
if (obj instanceof Box<String>) { }  // compilation error

// ✅ Can — raw type or wildcard
if (obj instanceof Box<?>) { }  // OK

3. Static context:

public class Box<T> {
    // ❌ Static field cannot use T
    private static T value;  // compilation error

    // ✅ Generic method — its own T
    public static <E> void process(E item) { }
}

Performance

Type erasure overhead:
- Runtime: Zero overhead (no additional checks)
- Memory: No extra data
- Cast: Implicit casts on access (negligible)

Box<Integer> box = new Box<>();
box.set(42);          // autoboxing int -> Integer
Integer val = box.get();  // cast + unboxing

Total overhead: autoboxing + cast (~1-2 ns)

Production Experience

Generic Repository:

public interface BaseEntity {
    Long getId();
}

public interface Repository<T extends BaseEntity, ID extends Serializable> {
    Optional<T> findById(ID id);
    List<T> findAll(Pageable pageable);
    <S extends T> S save(S entity);
    long count();
    boolean existsById(ID id);
}

public abstract class JpaRepository<T extends BaseEntity, ID extends Serializable>
    implements Repository<T, ID> {

    private final Class<T> entityType;
    private final EntityManager em;

    protected JpaRepository(Class<T> entityType, EntityManager em) {
        this.entityType = entityType;
        this.em = em;
    }

    public Optional<T> findById(ID id) {
        return Optional.ofNullable(em.find(entityType, id));
    }
}

Best Practices

// ✅ Use generics for collections
List<User> users = new ArrayList<>();

// ✅ Generic methods for reusability
public static <T> List<T> filter(List<T> list, Predicate<T> predicate) {
    return list.stream().filter(predicate).toList();
}

// ✅ Bounded types for constraints
public static <T extends Comparable<T>> T max(List<T> list) {
    return list.stream().max(Comparable::compareTo).orElse(null);
}

// ❌ Raw types
// ❌ new T[] or new T()
// ❌ instanceof T

🎯 Interview Cheat Sheet

Must know:

  • Generics appeared in Java 5 (JEP 14) for type safety when working with collections
  • Type parameters: <T>, <K, V>, <E> — placeholders for types
  • Bounded type parameters: <T extends Number> — type constraint
  • Type erasure — compiler removes type information, T -> Object or bound
  • Raw types — usage without type argument, disables type checking
  • Primitives cannot be used: List<int> — error, need List<Integer>

Common follow-up questions:

  • Why are generics needed? — Compile-time type safety, elimination of casts, code reuse
  • What is type erasure? — After compilation T is replaced with Object or bound, JVM doesn’t see generics
  • Can you instanceof T? — No, obj instanceof T — compilation error due to type erasure
  • **Why doesn’t List inherit from List?** — Generics are invariant, wildcards are used for flexibility

Red flags (DO NOT say):

  • ❌ “Generics work at runtime” — Type erasure removes types at compilation
  • ❌ “List is a subtype of List" — Generics are invariant
  • ❌ “You can create new T()” — Type erasure doesn’t allow knowing T’s constructor
  • ❌ “Primitive types can be used as parameters” — Only wrapper classes (Integer, Double)

Related topics:

  • [[12. What are the advantages of using Generics]]
  • [[13. What is type erasure]]
  • [[15. What are bounded type parameters]]
  • [[18. Can you use primitive types as generic parameters]]