← Home
☕Java Developer Interview Questions — Fresher to 5 YOEJava Dev InterviewNEW
🧩

Generics & Wildcards

Type erasure, bounded types, PECS · 10 questions

1

Why were generics introduced in Java?

Must Know🌱 Fresher

Before generics (Java 5), collections stored plain Object — you got no compile-time type checking on what you put in, and every read required an explicit, unchecked cast that could fail at runtime with a ClassCastException far away from the actual bug. Generics let you parameterize a class/method with a type, so the compiler enforces type-correctness at compile time and inserts the casts for you automatically and safely.

Before vs after genericsjava
// Before generics
List list = new ArrayList();
list.add("hello");
String s = (String) list.get(0); // manual cast, could fail at runtime for wrong content

// With generics
List<String> list = new ArrayList<>();
list.add("hello");
String s = list.get(0); // compiler already knows it's a String, no cast needed
Generics
2

What is type erasure, and what does it mean at runtime?

Must Know📊 Mid

Generics exist only at compile time — the compiler uses type parameters to check your code, then erases them, replacing T with Object (or its bound, if any) and inserting the necessary casts, so the compiled bytecode looks just like the old raw-type code. At runtime, a List<String> and a List<Integer> are both literally just List — there's no way to recover the type argument via reflection, and list.getClass() for both returns the same ArrayList class. This is why you can't do if (obj instanceof List<String>) — the JVM has no idea what String was at that point.

GenericsType Erasure
3

What is the difference between `? extends T` and `? super T`?

Must Know📊 Mid
List<? extends T>List<? super T>
MeaningA list of some unknown subtype of TA list of some unknown supertype of T
You can safely...Read a T out (it's guaranteed to be at least a T)Write a T in (any supertype can accept a T)
You can't safely...Add anything (except null) — the actual list could be a stricter subtype than you're addingRead as T with confidence (you only know it's at least Object)

This asymmetry is exactly what the PECS principle captures — see that question for the mnemonic.

GenericsWildcards
4

What are bounded type parameters (`<T extends Number>`)?

Important🌱 Fresher

A bound restricts what types can be substituted for a type parameter, and lets you call methods from that bound inside the generic code without an explicit cast.

Bounded type parameterjava
static <T extends Comparable<T>> T max(List<T> list) {
    T best = list.get(0);
    for (T t : list) {
        if (t.compareTo(best) > 0) best = t; // compareTo() is only known because of the bound
    }
    return best;
}
Generics
5

What are generic methods, and how do they differ from a generic class?

Important🌱 Fresher

A generic method declares its own type parameter (before the return type) independent of whether the enclosing class is generic — often used for utility/static methods where the type varies per call, not per instance.

A generic static methodjava
static <T> List<T> singletonList(T item) {
    List<T> list = new ArrayList<>();
    list.add(item);
    return list;
}
List<String> ls = singletonList("hi"); // T inferred as String for this call
Generics
6

What is the PECS principle (Producer Extends, Consumer Super)?

Advanced📊 Mid

Producer Extends, Consumer Super — a rule of thumb for choosing which wildcard to use:

  • If a parameter is a source you only read from (it "produces" values for you), use ? extends T.
  • If a parameter is a destination you only write to (it "consumes" values from you), use ? super T.
  • If you both read and write, don't use a wildcard — just use the exact type T.
Collections.copy signature applies PECSjava
// dest is written to (consumer) -> super
// src is read from (producer) -> extends
static <T> void copy(List<? super T> dest, List<? extends T> src) {
    for (T t : src) dest.add(t);
}
GenericsWildcards
7

Why can’t you create an array of a generic type (`new T[]`)?

Advanced📊 Mid

Because of type erasure, at runtime the JVM has no idea what T actually was — but array creation in Java is one of the few places the JVM performs a runtime type check on every store (ArrayStoreException if you put the wrong type into a typed array). new T[] would need to embed a real runtime type for that check, but T has already been erased to Object by the time the array is created — so the language simply forbids it at compile time to avoid a type-safety hole.

Not allowed, and the usual workaroundjava
T[] arr = new T[10]; // compile error: generic array creation

// Workaround: create as Object[] and cast (with an unchecked warning),
// or accept a Class<T> token and use Array.newInstance(componentType, size)
@SuppressWarnings("unchecked")
T[] arr = (T[]) new Object[10];
GenericsArrays
8

What are raw types, and why should you avoid them?

Optional📊 Mid

A raw type is a generic class/interface used without a type argument (e.g. plain List instead of List<String>) — kept in the language purely for backward compatibility with pre-Java-5 code. Using raw types today throws away all compile-time type checking on that variable, reintroduces the unchecked-cast ClassCastException risk generics were designed to eliminate, and produces compiler "unchecked operation" warnings — there's essentially never a reason to use one in new code.

GenericsRaw Types
9

What are bridge methods, and why does the compiler generate them?

Optional📊 Mid

When a generic class is subclassed with a concrete type argument and overrides a method, erasure can leave the subclass's method with a different erased signature than the interface/superclass expects. The compiler auto-generates a synthetic bridge method matching the erased signature, which simply casts and delegates to your actual (more specifically-typed) method — invisible in source code, but visible via reflection (Method.isBridge()).

Why a bridge method is neededjava
interface Box<T> { void set(T value); }

class StringBox implements Box<String> {
    public void set(String value) { /* ... */ }
    // compiler ALSO generates: public void set(Object value) { set((String) value); }
    // -- because Box's erased method signature is set(Object), not set(String)
}
GenericsType Erasure
10

What is the difference between `List<?>` and `List<Object>`?

Optional🌱 Fresher

List<Object> is a list that's specifically declared to hold Objects — you can add any object to it. List<?> ("list of unknown type") means "a list of some specific type, but I don't know which" — you can safely read elements out as Object, but you can't add anything to it (except null), because the compiler can't verify what you're adding matches the list's actual, unknown element type.

List<?> rejects writes; List<Object> doesn'tjava
List<String> strings = List.of("a", "b");
List<?> unknown = strings;      // OK -- List<?> accepts any List
unknown.add("c");                // compile error -- unknown type, could be anything

List<Object> objects = new ArrayList<>();
objects.add("c");                 // OK -- declared to hold Object
GenericsWildcards