Master Java Generics: When to Use T, E, K, V, and Wildcards

This article explains why Java generics improve type safety, demonstrates how to replace raw Object containers with generic classes, clarifies the conventional meanings of type parameters T, E, K, V and the wildcard ?, and introduces the PECS principle for choosing appropriate wildcards in API design.

Java Tech Enthusiast
Java Tech Enthusiast
Java Tech Enthusiast
Master Java Generics: When to Use T, E, K, V, and Wildcards

1. Why Use Generics

Type Safety and Casting

Assume we need a simple box class to store items:

// Non‑generic box class
public class Box {
    private Object item; // can only store any type as Object

    public void setItem(Object item) {
        this.item = item;
    }

    public Object getItem() {
        return item;
    }
}

Usage:

public static void main(String[] args) {
    Box box = new Box();
    box.setItem("Hello"); // store a String
    String s = (String) box.getItem(); // must cast back to String

    box.setItem(123); // can also store an Integer
    String i = (String) box.getItem(); // throws ClassCastException!
}

Problems:

Type unsafe: any type can be stored, but retrieval requires remembering the original type.

Verbose casting: every retrieval needs an explicit cast.

Runtime errors: wrong casts cause ClassCastException at runtime.

Using Generics

// Generic box class
public class Box<T> {
    private T item; // T is a type parameter

    public void setItem(T item) {
        this.item = item;
    }

    public T getItem() {
        return item; // no cast needed
    }
}

public static void main(String[] args) {
    Box<String> stringBox = new Box<>();
    stringBox.setItem("Hello");
    String s = stringBox.getItem(); // automatically a String

    Box<Integer> intBox = new Box<>();
    intBox.setItem(123);
    Integer i = intBox.getItem(); // automatically an Integer

    stringBox.setItem(123); // compile‑time error!
}

2. Meaning of T, E, K, V, ?

First, T, E, K, V are Type Parameter s, while ? is a wildcard. Java does not enforce specific meanings; the community follows conventions.

2.1 Using T (Type, any type)

Example: API response wrapper

// Generic API response class
public class ApiResponse<T> {
    private int code;
    private String message;
    private T data; // T represents the business data type

    public ApiResponse(int code, String message, T data) {
        this.code = code;
        this.message = message;
        this.data = data;
    }

    public static <T> ApiResponse<T> success(T data) {
        return new ApiResponse<>(200, "成功", data);
    }

    public static ApiResponse<?> error(int code, String message) {
        return new ApiResponse<>(code, message, null);
    }

    public T getData() { return data; }
    public void setData(T data) { this.data = data; }
}

// Business entities
public class User { private Long id; private String name; private String email; }
public class Product { private Long id; private String name; private BigDecimal price; }

// Service layer usage
public class UserService {
    public ApiResponse<User> getUserById(Long id) {
        User user = userRepository.findById(id);
        if (user != null) {
            return ApiResponse.success(user); // T inferred as User
        } else {
            return ApiResponse.error(404, "用户不存在");
        }
    }
}

public class ProductService {
    public ApiResponse<List<Product>> getFeaturedProducts() {
        List<Product> products = productRepository.findFeatured();
        return ApiResponse.success(products); // T inferred as List<Product>
    }
}

// Controller example
@GetMapping("/users/{id}")
public ApiResponse<User> getUser(@PathVariable Long id) {
    return userService.getUserById(id);
}

@GetMapping("/products/featured")
public ApiResponse<List<Product>> getFeaturedProducts() {
    return productService.getFeaturedProducts();
}

2.2 E (Element, element in a collection)

Example: Tree node

// Generic tree node (can be used for org charts, category trees, etc.)
public class TreeNode<E> {
    private E data;
    private List<TreeNode<E>> children;

    public void addChild(TreeNode<E> child) {
        if (children == null) children = new ArrayList<>();
        children.add(child);
    }
}

// Usage
TreeNode<String> root = new TreeNode<>();
root.setData("总公司");
TreeNode<String> branch1 = new TreeNode<>();
branch1.setData("北京分公司");
root.addChild(branch1);
TreeNode<String> branch2 = new TreeNode<>();
branch2.setData("上海分公司");
root.addChild(branch2);

2.3 Type Parameters K (Key) and V (Value) – Key‑Value Pair

Example: Local cache

// Local cache implementation
public class LocalCache<K, V> {
    private Map<K, V> cache = new ConcurrentHashMap<>();
    private long expireTime;

    public void put(K key, V value) {
        cache.put(key, value);
    }

    public V get(K key) {
        return cache.get(key);
    }
}

// Usage
LocalCache<Long, User> userCache = new LocalCache<>();
userCache.put(1001L, new User(1001L, "Alice"));

LocalCache<String, List<Product>> categoryCache = new LocalCache<>();
categoryCache.put("electronics", Arrays.asList(new Product(...), ...));

2.4 Wildcard ? – Handling Unknown Types

Java generic wildcards have three forms:

1) Unbounded wildcard ?

Matches any type; useful when the specific type is irrelevant.

Example: Print any collection element

import java.util.*;

public class Demo1 {
    public static void printList(List<?> list) {
        for (Object element : list) {
            System.out.println(element);
        }
    }

    public static void main(String[] args) {
        List<String> names = Arrays.asList("Tom", "Jerry");
        List<Integer> scores = Arrays.asList(88, 99);
        printList(names);
        printList(scores);
    }
}

Can accept any List type.

Only read elements; cannot add arbitrary elements.

2) Upper‑bounded wildcard ? extends T

Means “some type that is T or a subclass of T”; suitable for producer/readonly scenarios (PECS – Producer).

Example: Print numbers

public class Demo2 {
    public static void printNumbers(List<? extends Number> list) {
        for (Number n : list) {
            System.out.println(n);
        }
    }

    public static void main(String[] args) {
        List<Integer> ints = Arrays.asList(1, 2, 3);
        List<Double> doubles = Arrays.asList(1.1, 2.2, 3.3);
        printNumbers(ints);
        printNumbers(doubles);
    }
}

Can read elements as Number.

Cannot add elements because the exact subtype is unknown.

3) Lower‑bounded wildcard ? super T

Means “some type that is T or a superclass of T”; suitable for consumer/write scenarios (PECS – Consumer).

Example: Add integers to a collection

public class Demo3 {
    public static void addNumbers(List<? super Integer> list) {
        list.add(10);
        list.add(20);
    }

    public static void main(String[] args) {
        List<Number> numbers = new ArrayList<>();
        List<Object> objects = new ArrayList<>();
        addNumbers(numbers); // Number is a supertype of Integer
        addNumbers(objects); // Object is a supertype of Integer
        System.out.println(numbers);
        System.out.println(objects);
    }
}

Can safely add Integer values.

Read elements can only be treated as Object.

3. PECS Principle for Wildcards

PECS (Producer Extends, Consumer Super) is a guideline from Joshua Bloch’s *Effective Java* for choosing between ? extends T and ? super T:

Producer Extends: Use ? extends T when the parameter provides data to you (read‑only).

Consumer Super: Use ? super T when you put data into the parameter (write‑only).

In short: read (producer) → ? extends T, write (consumer) → ? super T.

4. Precautions

Prefer generic type parameters over raw Object unless you truly need an “any type” placeholder.

Choose wildcards wisely:

Use ? for read‑only data.

Use ? extends T for read‑only (producer) scenarios.

Use ? super T for write‑only (consumer) scenarios.

Avoid overusing generics; if a method only works with a specific type (e.g., String), use that concrete type directly.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaBackend DevelopmentGenericsType ParameterswildcardPECS
Java Tech Enthusiast
Written by

Java Tech Enthusiast

Sharing computer programming language knowledge, focusing on Java fundamentals, data structures, related tools, Spring Cloud, IntelliJ IDEA... Book giveaways, red‑packet rewards and other perks await!

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.