Generic classes and methods, bounded types, wildcards, PECS, and type erasure.
Question 1: Why were generics introduced in Java?
Ans
Before generics, collections stored plain Object references, so you had to manually cast every element back to its real type, and mistakes were only caught at runtime as a ClassCastException. Generics let you specify the type a collection or class works with at compile time, so the compiler both removes the need for manual casts and catches type mismatches before the program ever runs.
Example
java
List rawList = new ArrayList(); // pre-generics style
rawList.add("text");
String s = (String) rawList.get(0); // manual cast, unsafe
List<String> list = new ArrayList<>(); // generic style
list.add("text");
String s2 = list.get(0); // no cast needed, type-checked
Important Point
Generics are a compile-time safety feature — they don't exist as extra information at runtime, which leads to type erasure.
Question 2: What is a lower-bounded wildcard (? super), and how is it different from an upper-bounded wildcard (? extends)?
Ans
? extends T means the unknown type is T or a subtype of T, which is safe for reading values out as T but generally unsafe for adding new values in. ? super T means the unknown type is T or a supertype of T, which is safe for adding T values in but only lets you safely read values out as Object.
Example
java
void copyIn(List<? super Integer> dest) { dest.add(10); } // safe to add Integer
Number readOut(List<? extends Number> src) { return src.get(0); } // safe to read as Number
Important Point
This asymmetry is exactly what PECS captures: Producer Extends, Consumer Super.
Question 3: What does PECS (Producer Extends, Consumer Super) mean, and how do you apply it?
Ans
PECS is a rule of thumb for choosing which wildcard to use: if a generic structure is a "producer" that you only read values from, use ? extends T; if it's a "consumer" that you only write values into, use ? super T. Applying it correctly keeps a generic API both flexible for callers and type-safe.
Example
java
static void copy(List<? extends Number> src, List<? super Number> dst) {
for (Number n : src) dst.add(n); // src produces, dst consumes
}
Important Point
PECS is a design guideline for API authors, not a Java keyword or compiler-enforced rule.
Question 4: What is type erasure, and why does Java use it?
Ans
Type erasure is the compiler strategy of removing most generic type information after compile-time checks are done, replacing type parameters with their bound, or Object if unbounded, in the generated bytecode, and inserting the necessary casts automatically. Java uses it to keep generic code binary-compatible with code written before generics existed, so old and new code and libraries can still interoperate.
Example
java
List<String> a = new ArrayList<>();
List<Integer> b = new ArrayList<>();
System.out.println(a.getClass() == b.getClass()); // true — both are just ArrayList at runtime
Important Point
Because of erasure, you cannot check obj instanceof List<String> or create a new T[] directly — the generic type argument simply isn't available at runtime.