Wherever is <?> in collection initialization, you can not add anything to List. Except one case where you are using super keyword like <? super someClass>
This is not same as List<Object>, because here you are specifying a particular class that can be added to this collection but <?> means this list can refer a collection of any type. Because the compiler will not be sure about the type so we can not perform add operation on this collection.
Hey, it is a valid question and it does confuse a lot of people.
The simple answer is that Generics in java only provide "compile time safety" and not run-time safety. This is because of what is called as "Type Erasure" i.e. After java compiles the code, it removes the Generic type information from the bytecode and the run-time code is non-generic. The add operation is considered unsafe during run-time.
Please read my answer that explains this in a posting with a similar question in this link. It is the last message in that thread. Hope it helps. If not I will be glad to explain it to you further.
If you read the above link good. But let me explain it a little bit further:
<?> provides polymorphic generic reference types. By that what I mean is:
Set<?> q1 = new TreeSet<Object>(); is valid
Set<?> q1 = new TreeSet<String>(); is also valid
Set<?> q1 = new TreeSet<Integer>(); is also valid
You can basically substitute any type within the <> on the right hand side, since you have <?> (a wild card reference) on the left hand side. But as I told all the above types are only applicable during compile time i.e. this is how the code looks at compile time. After the code is compiled, java does "TYPE ERASURE" and to the JVM all of the above code only looks like the one shown below i.e. without any type information. It doesn't really matter what type you had declared on the right hand side. It is all erased. This is the most critical concept you have to burn into your mind Hence all of the above code for the JVM will look like below without any type information:
Set<?> q1 = new TreeSet();
So the problem is the JVM doesn't know that you intended this list to be a collection of Object types or String types or Integer types or any other types. So at run-time when you try to add an entry to the list, the JVM cannot prevent you from mistakingly or intentionally adding any object type you want, since again the JVM doesn't know what type of objects you intented the list to contain - due to TYPE ERASURE. For example if the compiler had allowed you to do the add() operation, then it means you will be able to do:
Set<?> q1 = new TreeSet<String>();
which is invalid, because now you are mistakingly adding a Integer type to a String collection and the JVM can't prevent you from doing that if the compiler had allowed the add operation to begin with. So this is considered unsafe at run-time. Because of that, they designed the compiler not to allow the add operation which is unsafe.
Does that make it clear? Again the points to remember are:
- Generics types are only applicable during compile time
- Type erasure is done after compilation and the type information is not available to the JVM
- add operation is considered unsafe because if it is allowed the JVM can't prevent you from adding an object type that doesn't belong to the collection. Hence the compiler won't allow it.
consider this example - assume add() is allowed and compiles fine:
How do you think, what happens in line 5 ?
And because this can happen, compiler prevents to add elements to collections of unknown <?> type.
There is another problem with this code - elements of treeset are expected to implement Comparable interface,
ie. they must implement compareTo( obj, obj ) method - treeset uses this method to compare objects in the tree.
Object type doesn't implement COmparable.