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 everyday. For example, both List<?> and List<Object> looks similar but there is a subtle difference between them, the List<?> is basically a list of any type, you can assign a list of String i.e. 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 require 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 i.e. List<?> myList = new List<String>().
Now, coming back to List<Object>, it's not different than a List<String> it just that it will only accepts Objects instead of String. Now, since every class in Java extends Object class, they are essentially object, 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. This one is also a popular Java Interview question from Java Generics topics.
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 compile time error when we assign List<String> or List<Integer>, but you cannot do the same with the 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 by writing a Java program and running it for validation
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 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.
First one accept because it uses List<?> as method argument which can accept any type of List. The printObjects(List<Object> listOfObject) throws error because it can only accept list of object and nothing else.
This also means you should prefer bounded wildcards while writing API e.g. for method arguments and return types.
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 error because type is unknown.
Compiler doesn't know what types are valid for that List hence it cannot provide type safety guarantee, hence it throws error, but this is not the case with List<Object>.
Here compiler does know that it is list of Object and since every class implicitly extends java.lang.Object, you can store anything in this list e.g. String, Integer, Float etc.
So, the real difference between List<?> and List<Object> comes depending upon what you are trying to do with them. If you are just reading objects e.g. printing elements from 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 methods which should work with any type of list then you should be using wildcards i.e. List<?> as method arguments. If you still have any doubt then let me know.
Other Java Generics tutorials you may like to explore
- How Generics works internally in Java?
- Difference between ArrayList and ArrayList<?> in Java?
- How to create parameterized classes and methods in Java?
- How to write parameterized generic method in Java?
- 10 Java Generics Interview Questions for Senior developers
- Difference between @Autowired and @Injection annotations in Spring?
- What is the difference between bounded and unbounded wildcards in Generics?
- How to implement a single linked list using Generics in Java?
Thank you for reading this article so far. If you have any doubt or question feel free to ask in comments and if you want to say hello, go ahead.
Nice article, just to add List is also known as List of unknown type and should be used to declare reference variables like you showed in method arguments, return types and as local variable.
ReplyDelete