Java
Generics
Programming
Type Safety
Object vs Wildcard

What is the difference between ? and Object in 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, ? and Object are related but not equivalent. Object is a concrete type, while ? means unknown captured type. This difference controls assignment compatibility, which operations are allowed, and how you should design APIs.

Concrete Type Versus Unknown Type

List<Object> means the list element type is exactly Object. List<?> means the list element type is some specific type, but that type is unknown at the current use site.

That unknown type could be String, Integer, or any reference type, but compiler rules preserve type safety.

java
1import java.util.*;
2
3List<String> names = new ArrayList<>();
4List<?> wildcard = names;          // valid
5// List<Object> objects = names;   // invalid

The second assignment is rejected because Java generics are invariant.

Why Invariance Matters

If List<String> were assignable to List<Object>, code could add an integer into a string list.

java
1List<String> strings = new ArrayList<>();
2strings.add("a");
3
4// Imagine this were legal:
5// List<Object> objs = strings;
6// objs.add(10);

That would break type guarantees for all code using strings. Java prevents this at compile time.

Read and Write Behavior

With List<Object>, you can add any object type because all reference types are subtypes of Object.

java
1List<Object> values = new ArrayList<>();
2values.add("hello");
3values.add(42);
4Object first = values.get(0);

With List<?>, you can read elements as Object, but you cannot safely add non-null values.

java
1List<?> unknown = new ArrayList<String>();
2Object item = unknown.get(0); // allowed
3// unknown.add("x");         // compile error
4unknown.add(null);            // allowed

Why null is allowed: null fits any reference type.

API Design Use Cases

Use List<?> when your method only needs to inspect data, not mutate element type-specific content.

java
static int countElements(List<?> list) {
    return list.size();
}

This accepts lists of many element types without requiring callers to copy data into List<Object>.

If your method needs to insert heterogeneous values, then List<Object> may be correct.

Bounded Wildcards and PECS

Unbounded wildcard is only one part of variance tools. Bounded wildcards provide more expressive contracts.

Read from producer:

java
1static double sumNumbers(List<? extends Number> nums) {
2    double total = 0;
3    for (Number n : nums) total += n.doubleValue();
4    return total;
5}

Write to consumer:

java
1static void addDefaults(List<? super Integer> out) {
2    out.add(1);
3    out.add(2);
4}

PECS rule helps remember intent:

  • Producer extends.
  • Consumer super.

This is usually better than forcing everything to Object.

Generic Methods Versus Wildcards

Sometimes a named type parameter is clearer than wildcard usage.

java
static <T> T firstOrNull(List<T> list) {
    return list.isEmpty() ? null : list.get(0);
}

If method logic depends on one consistent element type across inputs and outputs, prefer explicit type parameter. Use wildcards mainly for flexible input acceptance.

Common Refactoring Mistakes

A common anti-pattern is replacing wildcard parameters with Object because it seems simpler. That usually reduces API compatibility and increases casts.

Another mistake is returning wildcard-heavy collection types from public APIs. Consumers then struggle to use returned values ergonomically.

Good rule:

  • Input parameters can use wildcards for flexibility.
  • Return types should be concrete generic types when possible.

Common Pitfalls

  • Expecting List<String> to be assignable to List<Object>.
  • Trying to add typed values into List<?> directly.
  • Replacing wildcard parameters with Object and reducing API usability.
  • Misapplying bounded wildcards by ignoring producer-versus-consumer intent.
  • Returning wildcard collections where concrete generic return types are clearer.

Summary

  • 'Object is a concrete type, while ? represents unknown captured type.'
  • 'List<Object> and List<?> have different assignment and mutation rules.'
  • Invariance prevents unsafe cross-assignment between typed lists.
  • Wildcards improve parameter flexibility while preserving type safety.
  • Use bounded wildcards and generic methods intentionally for clean API design.

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.