Java
Programming
Java Generics
Programming Languages
Coding Concepts

What is the difference between 'E', 'T', and '?' for Java generics?

Object-Oriented Design practice on Codemia

Turn requirements into classes, and defend the design, on the problems that come up in OOD rounds.

Practice OOD

Introduction

In Java generics, E, T, and ? serve fundamentally different roles. E and T are type parameter names, which are naming conventions that act as placeholders for concrete types specified by the caller. The ? wildcard represents an unknown type and is used exclusively in type arguments, not in declarations. Understanding when to use each one is essential for writing flexible, type-safe Java code.

The short version: E and T are names you give to type parameters (by convention), while ? is a language-level construct that means "some type I do not know or care about."

Type Parameters: E, T, K, V, and Others

Type parameters are placeholders declared in angle brackets on a class, interface, or method. When someone uses the class, they supply a concrete type that replaces the placeholder throughout.

java
1public class Box<T> {
2    private T value;
3
4    public Box(T value) {
5        this.value = value;
6    }
7
8    public T getValue() {
9        return value;
10    }
11}

When you write Box<String>, every T in the class body becomes String. The compiler enforces type safety at every usage site.

The letters themselves are just conventions, not language rules. You could write Box<Foo> and it would compile. But the Java community follows well-established naming conventions that communicate intent.

ParameterConventionTypical UsageExample
TTypeGeneral-purpose container or methodBox<T>, Comparator<T>
EElementCollections and iterablesList<E>, Set<E>, Queue<E>
KKeyMap keysMap<K, V>
VValueMap valuesMap<K, V>
NNumberNumeric typesMatrix<N extends Number>
RResult/ReturnReturn types in functional interfacesFunction<T, R>
S, USecond, third typesWhen multiple type parameters are neededBiFunction<T, U, R>

These conventions matter because they make generic code self-documenting. When you see E in a method signature, you immediately know it represents an element of a collection.

E for Element: Collection Types

The Java Collections Framework uses E to represent the element type.

java
1import java.util.ArrayList;
2import java.util.List;
3
4public class ElementExample {
5    public static void main(String[] args) {
6        List<String> names = new ArrayList<>();
7        names.add("Ada");
8        names.add("Grace");
9
10        for (String name : names) {
11            System.out.println(name);
12        }
13    }
14}

The List<E> interface declares E as its type parameter. When you write List<String>, every E in the interface (in add(E e), get(int index) returning E, etc.) is bound to String.

If you write your own collection class, use E to follow the convention.

java
1public class SimpleStack<E> {
2    private final Object[] elements;
3    private int size;
4
5    public SimpleStack(int capacity) {
6        elements = new Object[capacity];
7    }
8
9    public void push(E item) {
10        elements[size++] = item;
11    }
12
13    @SuppressWarnings("unchecked")
14    public E pop() {
15        return (E) elements[--size];
16    }
17}

T for Type: General Containers and Methods

T is the go-to parameter for general-purpose generics that are not specifically collections.

java
1public class Pair<T> {
2    private final T first;
3    private final T second;
4
5    public Pair(T first, T second) {
6        this.first = first;
7        this.second = second;
8    }
9
10    public T getFirst() { return first; }
11    public T getSecond() { return second; }
12}

For generic methods, declare the type parameter before the return type.

java
1public class Util {
2    public static <T> T getLastElement(List<T> list) {
3        if (list.isEmpty()) {
4            throw new IllegalArgumentException("List is empty");
5        }
6        return list.get(list.size() - 1);
7    }
8}

The <T> before the return type tells the compiler this is a generic method. The caller does not need to specify T explicitly because the compiler infers it from the argument.

java
List<String> names = List.of("Ada", "Grace", "Linus");
String last = Util.getLastElement(names); // T inferred as String

The Wildcard ?: An Unknown Type

The wildcard ? is not a type parameter name. It is a language construct that means "some type, but I do not know which one." You use it in type arguments, never in type parameter declarations.

Unbounded Wildcard: <?>

Use <?> when the method works with any type and does not need to know what that type is.

java
1public static void printAll(List<?> items) {
2    for (Object item : items) {
3        System.out.println(item);
4    }
5}

This method accepts List<String>, List<Integer>, List<Employee>, or any other List. Inside the method, you can only read elements as Object because the actual type is unknown.

Upper-Bounded Wildcard: <? extends Type>

Use extends when you need to read from the structure and require a minimum type guarantee.

java
1public static double sum(List<? extends Number> numbers) {
2    double total = 0.0;
3    for (Number n : numbers) {
4        total += n.doubleValue();
5    }
6    return total;
7}

This accepts List<Integer>, List<Double>, List<BigDecimal>, or any List of a Number subtype. You can read elements as Number, but you cannot add to the list (because the compiler does not know the specific subtype).

Lower-Bounded Wildcard: <? super Type>

Use super when you need to write into the structure.

java
1public static void addNumbers(List<? super Integer> list) {
2    list.add(1);
3    list.add(2);
4    list.add(3);
5}

This accepts List<Integer>, List<Number>, or List<Object>. You can safely add Integer values because any of those list types can hold an Integer. However, reading from the list only guarantees Object.

The PECS Principle

Joshua Bloch's mnemonic from Effective Java captures the rule concisely.

Producer Extends, Consumer Super (PECS)

  • If the generic structure produces values for your code to read, use ? extends T.
  • If the generic structure consumes values that your code writes, use ? super T.
  • If the structure both produces and consumes, use a concrete type parameter like T.
java
1// Producer: reading FROM source
2public static <T> void copy(
3    List<? extends T> source,    // produces T values
4    List<? super T> destination  // consumes T values
5) {
6    for (T item : source) {
7        destination.add(item);
8    }
9}

Key Differences at a Glance

AspectE / T (Type Parameters)? (Wildcard)
RoleNamed placeholder for a concrete typeRepresents an unknown type
Where declaredClass, interface, or method declarationType argument at a usage site
Can you reference it?Yes, use the name throughout the scopeNo, cannot be referenced by name
Can you add to a collection?Yes, List<T> allows add(T)Only with ? super T
Multiple bounds<T extends Comparable<T> & Serializable><? extends Number> (single bound)
Convention matters?Yes, E/T/K/V convey intentN/A, ? is syntax, not a name

When to Use Which

Use a type parameter (T, E) when you need to:

  • Refer to the same type in multiple places within a signature
  • Return the same type that was passed in
  • Declare a class or interface that is generic
java
1// T appears in both the parameter and return type
2public static <T extends Comparable<T>> T max(T a, T b) {
3    return a.compareTo(b) >= 0 ? a : b;
4}

Use a wildcard (?) when you:

  • Do not need to reference the type by name
  • Want to accept multiple generic instantiations of the same class
  • Are writing a method that only reads from a structure
java
1// We never reference the element type by name
2public static boolean isEmpty(Collection<?> c) {
3    return c.size() == 0;
4}

Common Pitfalls

Thinking E and T have language-level meaning. They are conventions. The compiler treats Box<T> and Box<X> identically. The conventions exist purely for human readability.

Using List<Object> when you mean List<?>. A List<Object> can hold any object, but a method accepting List<Object> will not accept List<String> because generics are invariant. List<?> accepts any List regardless of its element type.

Adding elements to a List<? extends T>. The compiler blocks this because it cannot verify that the element you are adding matches the unknown subtype. Use ? super T for writing.

Confusing <T> method declaration with <?> usage. <T> in a method signature declares a new type variable. <?> in a parameter type is an argument that says "I accept any type here."

Overcomplicating simple signatures with wildcards. If a method takes a List and returns an element from it, a simple <T> parameter is clearer than a wildcard. Only introduce wildcards when the added flexibility is genuinely needed.

Summary

  • E, T, K, V are naming conventions for type parameters. They are placeholders that get replaced by concrete types.
  • E conventionally means Element (collections), T means Type (general purpose), K/V mean Key/Value (maps).
  • ? is the wildcard, representing an unknown type. It appears only in type arguments, not declarations.
  • Use ? extends T when reading (producer), ? super T when writing (consumer), following the PECS principle.
  • Prefer type parameters when you need to reference the type by name across a signature. Prefer wildcards when the type is irrelevant to the method's logic.

Related reading
Course
Intermediate
27 lessons
14 hours
OOD Fundamentals

Master object-oriented design from first principles, SOLID, design patterns, and classic interview problems with hands-on coding.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Object-Oriented Design practice on Codemia

Turn requirements into classes, and defend the design, on the problems that come up in OOD rounds.

Practice OOD

All Rights Reserved.