Wednesday, August 11, 2021

Difference between List <?> and List<Object> in Java Generics - Example

There is No doubt that Generics is one of the most confusing topics in Java, and you can easily forget concepts and rules, especially if you don't code Java every day. For example, both List<?> and List<Object> looks similar there is a subtle difference between them, the List<?> is basically a list of any type, you can assign a list of String like List<String> or list of Integer i.e. List<Integer> to it. You see the point; it uses the unbounded wildcard <?>, which means any type. It provides it the much needed Polymorphism requires while writing Generic methods. 

Basically, if your method has a type List<?> which means you can pass any type of List to it, but remember the type is unknown until you assign it like:

List<?> myList = new List<String>(). 

Now, coming back to List<Object>, it's no different than a List<String> it just that it will only accept Objects instead of String. Now, since every class in Java extends Object, they are essentially objects, which means you can store any type of Object in this list. 

Read carefully, the List<?> means you can assign any type of List to it and List<Object> means you can store any type of object into it. That's the real difference between List<?> and List<Object> in Java.

That's the real difference between List<?> and List<Object> in Java. Now, let's see an example to understand this difference better. If you think Generics is going top of your head, then don't worry, you can always check out my earlier post about The Ultimate Guide of Java Generics for Beginners, where I have covered Generics from scratch and shared many useful points and examples to quickly master key Generics concepts.







Difference between List<?> vs List<Object> in Java

No concept is complete until we validate our assumption by running some code. Since I have said that List<?> means you can assign any type of List to it, it should not throw a compile-time error when we assign List<String> or List<Integer>, but you cannot do the same with List<Object>

Similarly, you can store String, Integer, Float, or whatever you want into List<Object> but you can't do that with List<?> because type is unknown. Let's see if these concepts are true or not.

import java.util.ArrayList;
import java.util.List;

public class Demo {

    public static void main(String[] args) {

        printElements(new ArrayList<String>()); // OK
        printElements(new ArrayList<Integer>()); // OK

        printObjects(new ArrayList<String>()); 
        // NOT OK compile time error

        printObjects(new ArrayList<Integer>()); 
        // NOT OK compile time error
    }

    public static void printElements(List<?> listOfUnknownType) {
        listOfUnknownType.add("abc"); // compile time error

        for (Object o : listOfUnknownType) {
            System.out.println(o); // OK
        }
    }

    public static void printObjects(List<Object> listOfObjects) {
        listOfObjects.add("abc"); // OK
        listOfObjects.add(101); // OK

        for (Object o : listOfObjects) {
            System.out.println(o); // OK
        }
    }
}


You can see from the above code that you can pass ArrayList<String> and ArrayList<Integer> to the printElements() method but not to printObjects() method because of their different arguments. The first one accepts because it uses List<?> as a method argument which can accept any type of List. 

The printObjects(List<Object> listOfObject) throws an error because it can only accept a list of objects and nothing else. This also means you should prefer bounded wildcards while writing API, e.g., method arguments and return types.

Difference between List of raw types  and List of Object in Java Generics - Example



Similarly, inside the method, when you try to read values, both List<?> and List<Object> will work, but when you try to add objects, the List<?> will give an error because the type is unknown.

The compiler doesn't know what types are valid for that List hence it cannot provide type safety guarantee, hence it throws an error, but this is not the case with List<Object>. Here compiler does know that it is a list of Object and since every class, simplicity extends java.lang.Object, you can store anything in this list like String, Integer, Float etc.

So, the real differnece between List<?> and List<Object> comes depending upon what you are trying to do with them. If you are just reading objects, I mean printing elmeents from a list using enhanced for loop then both are ok, but if you want to add elements then you should use List<Object>. 

Similarly, if you are writing a generic method that should work with any type of list then you should be using wildcards like List<?> as method arguments. If you still have any doubt then let me know.


No comments:

Post a Comment

Feel free to comment, ask questions if you have any doubt.