There is a method :
public static <T> void addandDisp(Collection<T> cs, T t)
which is being called in the following way :
List<? extends Object> ls2 = new LinkedList<Number>();
addandDisp(ls2,new Object());
This gives a compile time error. On the other hand, if we had only one parameter, then the call is successful. Why is that ?
Moreover, this is successful :
List<? super String> ls1 = new LinkedList<String>();
addandDisp(ls1,new String());
while this is not :
List<? super String> ls1 = new LinkedList<Object>();
addandDisp(ls1,new Object());
What is the underlying logic ?
According to oracle documentation, the following points are the disadvantage of generics: Cannot instantiate Generic types with primitive types. Cannot create instances of type parameters. Cannot declare static fields whose types are type parameters.
Many people are unsatisfied with the restrictions caused by the way generics are implemented in Java. Specifically, they are unhappy that generic type parameters are not reified: they are not available at runtime. Generics are implemented using erasure, in which generic type parameters are simply removed at runtime.
I would assume that addandDisp
means add and display.
When you have a collection, defined as:
List<? extends Object> ls2 = new LinkedList<Number>();
This means that the compiler will allow you to assign the colletion to all the possible unknown subtypes of Object
. Since you have the operation add
, the compiler denies to give you green light, because it doesn't know if the provided object's type meets the restriction to be of the unknown subtype of Object. This is called covariance.
Similary, when you have a definition like this:
List<? super String> ls1 = new LinkedList<String>();
The compiler allows you to assing ls1
to:
LinkedList<String>(); //you can add Strings to the list
LinkedList<Object>(); //you can add Strings and Objects to the list
In this case, the compiler will be completely aware if the object you're trying to pass meets the condition to be subtype of the generic type of the collection. This is called contravariance.
More info:
Here is another approach, but others gave a quite detailed response.
The first example:
List<? extends Object> ls0 = new LinkedList<Number>();
addandDisp(ls0, new Object());
Here ls0
may be of any type that is a subclass of Object
, like a Number
as your example shows.
Now what if this could work? It would mean that if you can put an Object
into any of the collections.
Imagine I want to iterate your previous list of Number
objects:
for (Number n : numbers) {
...
}
If I could put an Object
here, then Plaff! I would get a class cast exception immediately.
The second example
List<? super String> ls1 = ...;
addandDisp(ls1,new String());
Let's play a bit with the object hierarchy: String
is a subclass of CharSequence
which is a subclass of Object
.
You get ls1
here which is an unknown supertype of String (e.g., a CharSequence
). Is it all right to put a String
into a list of character sequences? Yup, it's okay, since they share the same interface for sure! You can handle them through it in the same way.
The third example
List<? super String> ls1 = ...;
addandDisp(ls1,new Object());
Let's suppose the previous situation: you assign ls1
a list of CharSequence
s. Can you add an object to a List<CharSequence>
? Nope, for the same reason as the first example fails.
Hope that helps clarifying your issues a bit :-)
On the other hand, if we had only one parameter, then the call is successful. Why is that ?
Your method has a single type parameter. When you invoke a method, you either provide a type argument explicitly or have one be inferred implicitly.
When you invoke it like
addandDisp(ls2, new Object());
the compiler needs to extract a type to bind to T
from both method arguments, since both method parameters rely on the method's type parameter, T
.
The issue here is that ? extends Object
and Object
do not produce a single type that can be bound to T
safely. Imagine you had
public static <T> void addandDisp(Collection<T> cs, T t) {
cs.add(t);
}
...
List<? extends Object> ls2 = new LinkedList<Number>();
addandDisp(ls2, new Object());
This add
should not be allowed since an Object
should not be used where a Number
would have been expected. Type safety would break.
If you have a single parameter
public static <T> void addandDisp(Collection<T> cs) {
Then the compiler uses the single argument to infer the type argument. There is then no ambiguity about what to use.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With