Learnitweb

What is the difference between ? vs Object in Java generics

Java generics allow you to write flexible, type-safe code without committing to specific data types. However, when working with collections or generic methods, you might wonder:
What is the difference between using ? (wildcard) and using Object as the type parameter?

In this tutorial, we’ll break it down clearly with use cases, code samples, and key rules.

1. Basic Meaning

Object in Generics

When you use Object as a type parameter, you are telling Java to expect specifically objects of type Object or its subclasses (e.g., String, Integer), but only if the collection is declared as List<Object>.

List<Object> list = new ArrayList<>();
list.add("Hello");    // OK
list.add(42);         // OK

? (Wildcard) in Generics

? is a wildcard that represents an unknown type. It can be any type: String, Integer, custom types, etc.

List<?> list = new ArrayList<String>();

This list could be a List<String>, List<Integer>, or anything else. That’s why you can’t add elements to it (except null), because Java cannot guarantee type safety.

2. Can You Add Elements?

With List<Object>

List<Object> objectList = new ArrayList<>();
objectList.add("text");    // ✅ OK
objectList.add(100);       // ✅ OK

You can add anything — because everything in Java extends Object.

With List<?>

List<?> wildcardList = new ArrayList<String>();

// wildcardList.add("text");  // Compile error
wildcardList.add(null);       // Only null allowed

You can’t add anything because the exact type is unknown. It might be List<String>, or it might be List<Integer>. Java won’t allow anything except null.

Can You Read Elements?

Yes, in both cases — but types differ.

List<?> wildcardList = List.of("One", "Two");
Object item = wildcardList.get(0);  // OK

List<Object> objectList = List.of("One", "Two");
Object item2 = objectList.get(0);   // OK
  • In both cases, you can safely retrieve elements as Object.
  • With List<?>, you don’t know the exact type, so you must treat everything as Object.

4. Method Argument Flexibility

This is one of the most important differences.

public void printWildcard(List<?> list) {
    for (Object obj : list) {
        System.out.println(obj);
    }
}

public void printObjects(List<Object> list) {
    for (Object obj : list) {
        System.out.println(obj);
    }
}

Now try calling them:

List<String> stringList = List.of("Apple", "Banana");

printWildcard(stringList);   // OK
// printObjects(stringList); // Compile-time error

Why?

  • List<String> is not a subtype of List<Object>.
  • But List<String> is acceptable for List<?>, because ? can represent any type.

6. Real-Life Example

Let’s say you want to print any list, regardless of what it contains:

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

This is preferred over:

public void printList(List<Object> list) {
    // This won't accept List<String> or List<Integer>
}

7. Common Interview Question

Q: Why can’t you pass List<String> to a method that accepts List<Object>?

A: Because List<String> is not a subtype of List<Object> — Java generics are invariant. That means List<A> is not a subtype of List<B> even if A extends B.