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 asObject
.
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 ofList<Object>
.- But
List<String>
is acceptable forList<?>
, 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
.