Topic 105 of 119 14. Additional Java Topics Interactive Tutorial

Java Generics: Type Safety, Type Erasure, and Wildcards (? extends / super)

9 mins read Last updated: 22 Aug 2026 15,165 views

Direct Answer & Core Concept

Java Generics allow developers to write classes, interfaces, and methods that work with different types while maintaining compile-time type safety.

Instead of storing everything as Object and manually casting values, generics let you specify the expected type directly. This makes code easier to understand and helps the compiler detect many type-related mistakes before the program runs.

In this tutorial, you will learn Java generics, generic classes, generic methods, bounded type parameters, wildcards, ? extends, ? super, type erasure, raw types, and common generic programming mistakes.

Detailed Explanation & Mechanics

What Are Generics in Java?

Generics allow a type to be supplied as a parameter to a class, interface, or method.

For example:

Main.java
Run Online
List<String> names = new ArrayList<>();
names.add("Alice");
names.add("Bob");

Here, String is the type argument.

The declaration:

means that the list is intended to contain String values.

The compiler can therefore detect an invalid operation such as:

This gives a compile-time error instead of allowing an incorrect value to enter the collection.


Why Were Generics Introduced?

Before generics, collections commonly stored values as Object.

For example:

Main.java
Run Online
List names = new ArrayList();
names.add("Alice");
names.add("Bob");

When retrieving a value, a cast was required:

Main.java
Run Online
String name =(String) names.get(0);

This approach has several problems.

The compiler cannot provide the same level of type checking, and an incorrect value can lead to a ClassCastException later.

With generics:

Main.java
Run Online
List<String> names = new ArrayList<>();
String name = names.get(0);

The cast is normally unnecessary because the compiler knows the list's element type.


Generics Provide Compile-Time Type Safety

One of the biggest advantages of generics is compile-time type checking.

Consider:

Main.java
Run Online
List<Integer> numbers = new ArrayList<>();
numbers.add(10);
numbers.add(20);
numbers.add("Thirty");

The last statement is rejected by the compiler because "Thirty" is a String, while the list accepts Integer values.

This moves many errors from runtime to compile time.

Main.java
Run Online
Without GenericsObject↓Collection↓Cast↓Possible runtime errorWith GenericsType parameter↓Compile-time checking↓Safer code

Generic Collection Example

Generics are heavily used throughout the Java Collections Framework.

Main.java
Run Online
List<String> names = new ArrayList<>();
Set<Integer> numbers = new HashSet<>();
Map<Integer, String> users = new HashMap<>();

The type arguments describe what each collection contains.

For example:

means:

Main.java
Run Online
Key -> IntegerValue -> String

Generic Classes

You can create your own generic class.

Main.java
Run Online
class Box<T> {
    private T value;
    public void set(T value) {
        this.value = value;
    } public T get() {
        return value;
    }
}

Here, T is a type parameter.

You can create a Box for different types:

Main.java
Run Online
Box<String> nameBox = new Box<>();
nameBox.set("Java");
String name = nameBox.get();

Or:

Main.java
Run Online
Box<Integer> numberBox = new Box<>();
numberBox.set(100);
Integer number = numberBox.get();

The same class works with different types.


What Does T Mean?

T does not have any special magical meaning to the compiler.

It is simply a conventional name for a type parameter.

Common conventions include:

Type ParameterCommon Meaning
TType
EElement
KKey
VValue
NNumber
RResult

For example:

Main.java
Run Online
class Pair<K, V> {
    private K key;
    private V value;
}

K and V are simply type parameter names.


Multiple Type Parameters

A generic class can have multiple type parameters.

Main.java
Run Online
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;
    }
}

Usage:

Main.java
Run Online
Pair<Integer, String> user = new Pair<>(101, "Alice");
System.out.println(user.getKey());
System.out.println(user.getValue());

Here:


Generic Methods

A method can also have its own type parameter.

Main.java
Run Online
public static <T> void printValue(T value) {
    System.out.println(value);
}

You can call it with different types:

Main.java
Run Online
printValue("Hello");
printValue(100);
printValue(10.5);

The compiler can infer the appropriate type in many cases.


Generic Method Syntax

The general form is:

Main.java
Run Online
public static <T> T methodName(T value) {
    return value;
}

The <T> before the return type declares the method's type parameter.

For example:

Main.java
Run Online
public static <T> T identity(T value) {
    return value;
}

Usage:

Main.java
Run Online
String text = identity("Java");
Integer number = identity(100);

Generic Interfaces

Interfaces can also use type parameters.

Main.java
Run Online
interface Repository<T> {
    void save(T value);
    T find();
}

A class can implement the interface with a specific type.

Main.java
Run Online
class UserRepository implements Repository<String> {
    private String value;
    public void save(String value) {
        this.value = value;
    } public String find() {
        return value;
    }
}

The generic interface defines a reusable type-safe contract.


Bounded Type Parameters

Sometimes you do not want to accept every possible type.

You can restrict a generic type using a bound.

For example:

Main.java
Run Online
public static <T extends Number> double square(T value) {
    return value.doubleValue() * value.doubleValue();
}

Here:

means that T must be Number or a subclass of Number.

Examples:

Main.java
Run Online
square(10);
square(10.5);

Both Integer and Double satisfy the bound because they extend Number.


Multiple Bounds

A type parameter can have multiple bounds.

For example:

Main.java
Run Online
<T extends Number & Comparable<T>>

The class bound, if present, must appear first, followed by interface bounds.

Example:

Main.java
Run Online
public static <T extends Number & Comparable<T>> void process(T value) {
    // ...
}

This requires the type to satisfy both constraints.


Generic Types Are Invariant

One of the most important concepts in Java generics is invariance.

Suppose:

Main.java
Run Online
List<Integer> integers = new ArrayList<>();

You might think this should be allowed:

Main.java
Run Online
List<Number> numbers = integers;

But Java does not allow it.

Even though:

it does not follow that:

Main.java
Run Online
List<Integer> extends List<Number>

This prevents type-safety problems.


Why Is List<Integer> Not a List<Number>?

Imagine Java allowed:

Main.java
Run Online
List<Integer> integers = new ArrayList<>();
List<Number> numbers = integers;

Then this would become possible:

But the original list is supposed to contain only Integer objects.

That would break type safety.

Therefore Java keeps generic parameterized types invariant.


Wildcards in Java

A wildcard represents an unknown type.

The basic wildcard syntax is:

For example:

This means:

A list of some unknown type.

The exact type is unknown to the code using the wildcard.


Unbounded Wildcard: ?

Consider:

Main.java
Run Online
public static void printList(List<?> list) {
    for (Object value : list) {
        System.out.println(value);
    }
}

This method can accept:

and many other parameterized lists.

The wildcard makes the method flexible without using a raw type.


What Can You Add to List<?>?

Consider:

Main.java
Run Online
List<?> list = new ArrayList<String>();

You generally cannot add an arbitrary value:

This is not allowed because the actual type represented by ? is unknown.

The only generally safe value to add is null:

You can read values from List<?> as Object:

Main.java
Run Online
Object value = list.get(0);

Upper-Bounded Wildcard: ? extends

The syntax:

means an unknown type that is Type or a subtype of Type.

For example:

can represent:

or:

or:


Example of ? extends

Main.java
Run Online
public static double sum(List<? extends Number> numbers) {
    double total = 0;
    for (Number number : numbers) {
        total += number.doubleValue();
    } return total;
}

Now you can pass:

Main.java
Run Online
List<Integer> integers = List.of(10, 20, 30);
List<Double> decimals = List.of(1.5, 2.5);
System.out.println(sum(integers));
System.out.println(sum(decimals));

The method can read the elements as Number.


Why Can't You Add to ? extends?

Consider:

Main.java
Run Online
List<? extends Number> numbers;

The actual list could be:

If Java allowed:

that would attempt to put a Double into a possible List<Integer>.

Therefore Java prevents adding arbitrary non-null values.

This is the key idea:

With ? extends, you can safely read values as the upper-bound type, but you generally cannot add values.


? extends Is Useful for Producers

A useful rule is:

PECS — Producer Extends, Consumer Super

If a generic structure produces values for your code to read, ? extends is often appropriate.

Example:

Main.java
Run Online
public static void printNumbers( List<? extends Number> numbers) {
    for (Number number : numbers) {
        System.out.println(number);
    }
}

The list produces Number values for the method.


Lower-Bounded Wildcard: ? super

The syntax:

means an unknown type that is Type or a supertype of Type.

For example:

can refer to:

or:


Example of ? super

Main.java
Run Online
public static void addNumbers( List<? super Integer> list) {
    list.add(10);
    list.add(20);
    list.add(30);
}

This is safe because every possible type represented by:

can accept an Integer.


Why Can You Add to ? super?

Suppose:

Main.java
Run Online
List<? super Integer> list;

The actual list could be:

or:

or:

All of these can safely store an Integer.

Therefore:

is allowed.


What Do You Get From ? super?

Reading from:

is more restricted.

You can safely retrieve values as Object:

Main.java
Run Online
Object value = list.get(0);

But Java cannot guarantee that the returned object is an Integer.

For example, if the actual list is:

it could contain a Double as well.

Therefore this is not generally safe:

Main.java
Run Online
Integer value = list.get(0);

? super Is Useful for Consumers

Following PECS:

Consumer Super

If your method is putting values into a generic structure, ? super can often be used.

Example:

Main.java
Run Online
public static void addUsers( List<? super User> users) {
    users.add(new User());
}

The method is consuming User objects by adding them to the list.


PECS: Producer Extends, Consumer Super

PECS is one of the most useful rules for understanding Java wildcards.

Main.java
Run Online
Producer -> ? extendsConsumer -> ? super

Producer

If you mainly read values:

Consumer

If you mainly add values:

This is a guideline rather than a rule that every generic method must follow.


extends vs super

The difference can be summarized like this:

WildcardMeaningReadAdd
?Unknown typeAs ObjectOnly null
? extends TUnknown subtype of TAs TGenerally no non-null value
? super TUnknown supertype of TAs ObjectT values are safe

Example:

is useful when reading numbers.

is useful when adding integers.


Generic Arrays

Java treats arrays and generics differently.

Arrays are covariant:

Main.java
Run Online
Integer[] integers = new Integer[10];
Number[] numbers = integers;

This is allowed.

But generics are invariant:

Main.java
Run Online
List<Integer> integers = new ArrayList<>();
// Not allowed
// List<Number> numbers = integers;

This difference is important when designing APIs.


Why Generic Array Creation Is Restricted

You cannot normally write:

Main.java
Run Online
List<String>[] lists = new List<String>[10];

Java does not allow direct creation of arrays of most parameterized types.

However, you may encounter unchecked operations involving generic arrays in advanced code.

This restriction exists because arrays retain runtime component-type information, while generic type arguments are subject to type erasure.


Type Erasure in Java

Java generics were designed to provide compile-time type safety while maintaining compatibility with older Java code.

One important mechanism is type erasure.

During compilation, generic type information is largely removed from the generated bytecode's ordinary runtime type representation.

For example:

Main.java
Run Online
List<String> names = new ArrayList<>();

and:

Main.java
Run Online
List<Integer> numbers = new ArrayList<>();

are both based on the runtime class:

The JVM does not normally have separate runtime classes such as:

Main.java
Run Online
ArrayList<String>ArrayList<Integer>

Simple Type Erasure Example

Consider:

Main.java
Run Online
class Box<T> {
    private T value;
    public void set(T value) {
        this.value = value;
    } public T get() {
        return value;
    }
}

Conceptually, after type erasure, the implementation is similar to using Object for an unbounded type parameter:

Main.java
Run Online
class Box {
    private Object value;
    public void set(Object value) {
        this.value = value;
    } public Object get() {
        return value;
    }
}

The compiler then inserts appropriate casts where necessary.

This is a conceptual explanation rather than a literal source transformation you should expect to see.


Type Erasure With Bounds

If a type parameter has a bound:

the erased type is generally the leftmost bound.

For example:

Main.java
Run Online
class Calculator<T extends Number> {
    T value;
}

The effective erased type for T is Number.


Why Can't You Use instanceof List<String>?

Because generic type arguments are erased at runtime.

This is invalid:

Main.java
Run Online
if (value instanceof List<String>) {
    // ...
}

The JVM cannot distinguish between:

and:

using ordinary runtime generic type information.

You can check the raw parameterized class:

Main.java
Run Online
if (value instanceof List<?>) {
    // ...
}

This is allowed because List<?> does not claim a specific element type.


Why Can't You Create new T()?

This is not allowed:

Main.java
Run Online
class Box<T> {
    T create() {
        return new T();
    }
}

At runtime, due to type erasure, the JVM does not generally know which concrete type T represents.

A common alternative is to pass a factory, constructor reference, or Class<T> when appropriate.

For example:

Main.java
Run Online
class Box<T> {
    private final Supplier<T> factory;
    Box(Supplier<T> factory) {
        this.factory = factory;
    } T create() {
        return factory.get();
    }
}

Usage:

Main.java
Run Online
Box<StringBuilder> box = new Box<>(StringBuilder::new);

Generic Type Information at Runtime

Consider:

Main.java
Run Online
List<String> names = new ArrayList<>();

You cannot generally ask the list:

Main.java
Run Online
"What exact generic type argument are you storing?"

and expect the ordinary object instance to provide String.

The runtime object is an ArrayList.

Generic information can sometimes be available through reflection on class or method declarations, but that is different from the runtime object knowing the concrete type argument of every instance.


Raw Types

A raw type is a generic type used without specifying its type argument.

For example:

Main.java
Run Online
List list = new ArrayList();

instead of:

Main.java
Run Online
List<String> list = new ArrayList<>();

Raw types are mainly retained for backward compatibility with pre-generics Java code.

New code should generally avoid them.


Problems With Raw Types

Consider:

Main.java
Run Online
List list = new ArrayList();
list.add("Java");
list.add(100);

Now the collection contains different types.

A later cast can fail:

Main.java
Run Online
String value =(String) list.get(1);

This can result in:

Using generics prevents this class of mistake earlier:

Main.java
Run Online
List<String> list = new ArrayList<>();

Unchecked Warnings

You may see compiler warnings such as:

Main.java
Run Online
unchecked conversionunchecked callunchecked cast

These warnings often indicate that the compiler cannot fully verify type safety.

For example:

Main.java
Run Online
List rawList = new ArrayList();
List<String> names = rawList;

The compiler may issue an unchecked conversion warning.

Rather than suppressing the warning immediately, investigate why it occurs.


@SuppressWarnings("unchecked")

Sometimes unchecked operations are unavoidable in carefully designed low-level code.

You may encounter:

Main.java
Run Online
@SuppressWarnings("unchecked")

However, this annotation should be used narrowly.

Do not use it simply to hide generic type-safety problems.

A better approach is to isolate the unchecked operation and ensure the surrounding code maintains the required type invariant.


Generic Inheritance

A class can implement or extend a generic type with a concrete argument.

Main.java
Run Online
class StringRepository implements Repository<String> {
    @Override public void save(String value) {
        System.out.println(value);
    } @Override public String find() {
        return "Java";
    }
}

It is also possible to keep the type parameter:

Main.java
Run Online
class RepositoryImpl<T> implements Repository<T> {
    public void save(T value) {
        // ...
    } public T find() {
        return null;
    }
}

Generic Constructors

Constructors can use the class's type parameters.

Main.java
Run Online
class Box<T> {
    private T value;
    Box(T value) {
        this.value = value;
    }
}

Usage:

Main.java
Run Online
Box<String> box = new Box<>("Java");

Constructors can also have their own independent type parameters in certain contexts.


Diamond Operator

Java provides the diamond operator:

It allows the compiler to infer generic type arguments.

Instead of:

Main.java
Run Online
List<String> names = new ArrayList<String>();

you can write:

Main.java
Run Online
List<String> names = new ArrayList<>();

This is shorter and usually easier to read.


Generic Type Inference

Java can infer type arguments from context.

For example:

Main.java
Run Online
var names = new ArrayList<String>();

or:

Main.java
Run Online
List<String> names = new ArrayList<>();

The compiler understands that the ArrayList should be parameterized with String.

Generic methods can also benefit from type inference:

Main.java
Run Online
String value = identity("Java");

Static Members and Class Type Parameters

A static field cannot use a class's type parameter.

This is invalid:

Main.java
Run Online
class Box<T> {
    static T value;
}

The reason is that static members belong to the class rather than to a particular parameterized instance.

For example:

would share the same static member, so there is no single T that could represent both.

A static method can declare its own type parameter:

Main.java
Run Online
static <T> T identity(T value) {
    return value;
}

Generic Exception Classes

Java does not allow a generic class to directly extend Throwable.

For example, this is not allowed:

Main.java
Run Online
class MyException<T> extends Exception {
}

Exception handling relies on runtime exception types, while generic type parameters are subject to erasure.


Wildcard Capture

Java sometimes internally treats a wildcard as a specific but unknown type.

For example:

can be thought of as:

where CAP is an unknown captured type.

This concept becomes useful when understanding why certain wildcard operations are allowed or rejected.

For most beginner and intermediate code, however, the practical rules are more important than the internal terminology.


A Practical extends Example

Suppose you want to calculate the total of any numeric list:

Main.java
Run Online
public static double total( List<? extends Number> values) {
    double result = 0;
    for (Number value : values) {
        result += value.doubleValue();
    } return result;
}

Now:

Main.java
Run Online
List<Integer> integers = List.of(10, 20, 30);
List<Double> doubles = List.of(1.5, 2.5);
System.out.println(total(integers));
System.out.println(total(doubles));

One method works with both collections.


A Practical super Example

Suppose you want a method that copies integers into another collection.

Main.java
Run Online
public static void addIntegers( List<? super Integer> destination) {
    destination.add(10);
    destination.add(20);
    destination.add(30);
}

It can work with:

Main.java
Run Online
List<Integer> integers = new ArrayList<>();
List<Number> numbers = new ArrayList<>();
List<Object> objects = new ArrayList<>();

All can safely receive Integer values.


Collections.copy() and Wildcards

The Java Collections API provides a useful real-world example of wildcard design.

A simplified form of the method is conceptually:

Main.java
Run Online
copy( List<? super T> destination, List<? extends T> source)

The source produces values of type T.

The destination consumes values of type T.

This directly demonstrates PECS:

Main.java
Run Online
Source -> ? extends TDestination -> ? super T

Common Generics Mistakes

Mistake 1: Using Raw Types

Avoid:

Main.java
Run Online
List list = new ArrayList();

Prefer:

Main.java
Run Online
List<String> list = new ArrayList<>();

Mistake 2: Assuming Generic Types Are Covariant

This does not compile:

Main.java
Run Online
List<Integer> integers = new ArrayList<>();
List<Number> numbers = integers;

Use an appropriate wildcard when your API requires variance:

Main.java
Run Online
List<? extends Number> numbers = integers;

Mistake 3: Adding to ? extends

This is not allowed:

Main.java
Run Online
List<? extends Number> numbers = ...;
// numbers.add(10);

The exact subtype is unknown.


Mistake 4: Expecting ? super to Return the Exact Type

This is not generally safe:

Main.java
Run Online
List<? super Integer> list = ...;
// Integer value = list.get(0);

Read values as Object unless you have additional information.


Mistake 5: Confusing extends With Class Inheritance Here

In:

extends describes an upper bound on an unknown type argument.

It does not mean that List itself is a subclass of Number.


Generic Method vs Wildcard Method

These two concepts can look similar but solve different problems.

Generic method:

Main.java
Run Online
static <T> T first(List<T> list) {
    return list.get(0);
}

Wildcard method:

Main.java
Run Online
static Object first(List<?> list) {
    return list.get(0);
}

The generic method preserves the relationship between the input and output type.

The wildcard method intentionally treats the element type as unknown.


When Should You Use Generics?

Use generics when:

  • A class should work with multiple types.

  • A method should operate on different types safely.

  • A collection has a known element type.

  • You want compile-time type checking.

  • You want to avoid unnecessary casts.

  • You are designing reusable APIs.

Example:

Main.java
Run Online
class Repository<T> {
    // ...
}

This can support:

Main.java
Run Online
Repository<User>Repository<Product>Repository<Order>

without duplicating the class for every type.


Generics vs Object

Without generics:

Main.java
Run Online
Object value = "Java";
String text =(String) value;

With generics:

Main.java
Run Online
Box<String> box = new Box<>();
box.set("Java");
String text = box.get();

Generics provide a stronger compile-time contract.


Benefits of Java Generics

1. Type Safety

The compiler catches many invalid type operations.

2. Fewer Casts

Generic collections usually return the declared type directly.

3. Reusable Code

One generic class or method can work with many types.

4. Better API Design

Type parameters communicate intended usage clearly.

5. Improved Maintainability

Explicit types make code easier to understand and refactor.


Java Generics Cheat Sheet

Main.java
Run Online
<T>Generic type parameterList<T>List containing TList<?>List containing an unknown typeList<? extends T>List of T or a subtype of TList<? super T>List of T or a supertype of T

And the PECS rule:

Main.java
Run Online
Producer -> extendsConsumer -> super

Generic Type Relationships

A useful mental model is:

Main.java
Run Online
Number / \ Integer Double | | List<Integer> | X List<Number>Generic collections are invariant.

But:

can accept a List<Integer> or List<Double>.

Similarly:

can accept a List<Integer>, List<Number>, or List<Object>.


Frequently Asked Questions

What are generics in Java?

Generics allow types to be parameterized so that classes, interfaces, and methods can operate on different types while maintaining compile-time type safety.

Why are Java generics useful?

They reduce incorrect type usage, minimize explicit casts, improve code reuse, and make APIs clearer.

What does T mean in Java generics?

T is a conventional name for a type parameter. It is not a reserved keyword and could be replaced with another valid identifier.

What does ? mean in Java?

? represents an unknown type. It is called an unbounded wildcard.

What does ? extends Number mean?

It represents an unknown type that is Number or a subtype of Number.

What does ? super Integer mean?

It represents an unknown type that is Integer or a supertype of Integer.

What is PECS in Java?

PECS stands for Producer Extends, Consumer Super. It is a common guideline for choosing between ? extends and ? super.

Can I add values to List<? extends Number>?

You generally cannot add a non-null value because the actual list could be a list of a specific subtype such as Integer.

Can I add an Integer to List<? super Integer>?

Yes. Every possible type represented by ? super Integer can safely store an Integer.

What is type erasure?

Type erasure is the mechanism by which Java implements generics while maintaining compatibility with older Java bytecode. Generic type arguments are largely removed from the runtime representation, with the compiler inserting necessary casts and enforcing type rules.

Why can't I use instanceof List<String>?

Because the runtime normally cannot distinguish the generic type arguments String and Integer after type erasure. instanceof List<?> is allowed.

Why can't I create new T()?

Because the concrete runtime type represented by an unbounded type parameter is not generally available due to type erasure. A factory or constructor reference can be used instead.

Are generics available for primitive types?

Java generics work with reference types, not primitive types.

This does not work:

Use the wrapper type:

Autoboxing converts between int and Integer when appropriate.

Graphics and Design Ideas

Graphic 1: Java Generics Overview

Main.java
Run Online
Java Generics | +---------+---------+ | | | Type Safety Reuse Flexibility | | Compile Time Wildcards | +--------+--------+ | | ? extends ? super

Graphic 2: PECS Diagram

Main.java
Run Online
Generic Collection | +---------+---------+ | | PRODUCER CONSUMER | | ? extends ? super | | READ WRITE

Graphic 3: Type Erasure

Show:

Main.java
Run Online
Source Code | vList<String>Box<Integer> | vJava Compiler | vType Checking + Cast Insertion | vRuntime Representation

Graphic 4: Generic Type Relationships

Main.java
Run Online
Number / \Integer DoubleList<Integer> X List<Number>But:List<? extends Number> ↑ accepts both Integer / Double

Graphic 5: extends vs super

Architecture Diagram
? extends Number

Number
 ├── Integer
 ├── Double
 └── Float

        READ
↓
? super Integer

Object
   ↑
Number
   ↑
Integer

        WRITE Integer

These graphics would work well as simple educational diagrams near the corresponding sections and can make the wildcard rules much easier for beginners to remember.

Runnable Code Example: Java Generics

Java 17+
Main.java
Run Online
import java.util.List;
public class GenericsDemo {
    // PECS: Producer Extends (read-only sum)
    public static double sumOfList(List<? extends Number> numbers) {
        double sum = 0.0;
        for (Number n : numbers) {
            sum += n.doubleValue();
        } return sum;
    } public static void main(String[] args) {
        List<Integer> ints = List.of(10, 20, 30);
        List<Double> doubles = List.of(1.5, 2.5, 3.5);
        System.out.println("Ints Sum: " + sumOfList(ints));
        System.out.println("Doubles Sum: " + sumOfList(doubles));
    }
}

Common Java Technical Interview Questions & Answers

Interview Prep
Q1

What is Type Erasure in Java Generics?

The process where the Java compiler removes generic type parameters during compilation, replacing them with their bounds (or Object) for backward compatibility.
Q2

What is the PECS mnemonic?

'Producer Extends, Consumer Super' — use <? extends T> when reading/producing data, and <? super T> when writing/consuming data.

Key Takeaways, Best Practices & Clean Code Rules

Java Best Practice & Production Rules

Follow PECS when designing flexible API method parameters with collection wildcards.