If you're here, you're probably at a point in your Java development efforts where you're defining a generic that can accept multiple types but you're not sure if you should use ? or Object.

You could go this route:

List<?> list = fetchData();

Or this route:

List<Object> list = fetchData();

You might think that because Object is the base class of every class in Java that those two definitions are the same. But they aren't.

Let me explain the difference.

The Wildcard

First, I'll explain the wildcard. That's the first option you see above with the question mark as the type parameter.

That means you can put anything in there. It's a wildcard, after all.

It's convenient to use when you aren't sure which type you'll include in the List (or whatever other object you're using that accepts generics).

Also, keep in mind that question mark is just a shorthand way of writing the following code:

List<? extends Object> list = fetchData();

But that's still not the same as just plain old List<Object>.

Why?

It's because List<Object> isn't a supertype of List<String> or List<Integer> or List anything else.

So when you define a variable as List<Object> you can't turn around and instantiate it as ArrayList<String>.

Let's say you tried this:

List<Object> objectList;
objectList = new ArrayList<String>();

Not happening, Jack. That's going to give you a compilation error.

Again: that's because List<Object> is not a supertype of List<String> even though Object is a supertype of String.

However, you can put a String object into that List once it's instantiated. This will work:

List<Object> objectList = new ArrayList<>();
objectList.add("String");

No compilation problems.

List<Object> is useful if you have a single List with mutliple types in it. So you can also get away with this:

List<Object> objectList = new ArrayList<>();
objectList.add("String");
objectList.add(1);

That's a fairly rare use case, though.

The Wildcard, Revisited

So why use a wildcard? It's useful when you want to instantiate the object with a specific type but you don't know what that type is when you define it.

Let's say you're writing code for an HR application and you allow users to perform a search. In some cases, the search will return a List of Employee objects but in other cases it will return a List of Contractor objects.

Take a look at this code:

List<?> searchResults = getSearchResults(criteria);

In that case, the getSearchResults() method might look something like this:

public List<?> getSearchResults(SearchCriteria criteria) {
    List<?> results = null;

    if (criteria.isContractorSearch()) {
        results = new ArrayList<Contractor>();
        //handle search
    }

    if (criteria.isEmployeeSearch()) {
        results = new ArrayList<Employee>();
        //handle search
    }

    return results;
}

I'm simplifying things quite a bit there, but I think you get the idea. You can use that wildcard to return a List containing either Employee or Contractor objects, depending on the nature of the search.

That's where the wildcard outshines Object.

Wrapping It Up

In summary:

  • Use Object when you just want to populate your generified object with any type
  • Use ? when you want to instantiate the generified object using a specific type but don't know what that type is in advance

Feel free to fiddle around by doing some trial-and-error coding. I think you'll get the hang of it soon enough.

Have fun!

Photo by Andrea Piacquadio from Pexels