Reputation: 1951
It seems either this concept is not straight forward to understand, or the so many long articles on the web are not really explaining well. I appreciate if someone can explain in a clear and short way.
I have read the examples in this blog and this video.
The conclusion I draw so far is:
In Java, arrays are covariant and generics are invariant.
Arrays are covariant:
Number[] myNums = new Integer[3]; // compile ok
but.. if I do this, run time error though compile ok:
myNums[0] = 2.1; // compile ok, run time not ok
What is the point of having array covariant if run time will be NOT ok? This question may actually refer to "What's the point of covariant?"
Generics are invariant:
List<Number> myNums = new ArrayList<Integer>(); // compile not ok
But amazingly, there's a way to make generics covariant/cotravariant, use wildcard:
List<? extends Number> myNums1 = new ArrayList<Integer>(); // convariance, compile ok
List<? super Integer> myNums2 = new ArrayList<Number>(); // contravariance, compile ok
Even there's the way to make it covariant or contravariant, I still cannot do things like
myNums1.add(New Integer(1));
What is the point of all of this?
Please, is there anyone help me to clear out all this confusion?
Upvotes: 2
Views: 547
Reputation: 131456
In Java, arrays are covariant and generics are invariant.
Yes for arrays. For Java, it is right for generics without wildcard but generics with wildcard goes over that limitation.
What is the point of having array covariant if run time will be NOT ok?
No value. It is an array specific drawback that was not reproduced such as in generics.
What is the point of all of this?
About Java generics, the thing is that these are not absolutely invariant, covariant or contravariant because the invariant is the safer way but it is also the most restrictive in terms of programming features. I illustrate below.
So Java (as well as other strong typed languages) made the choice to provide distinct generics flavors : invariant, covariant and contravariant while ensuring the type safety in any case (while you respect compiler warnings of course).
But amazingly, there's a way to make generics covariant/cotravariant, use wildcard:
Not really amazing. Without covariance, you lose all possibilities to pass a variable declared with generic type in a method that accepts a supertype of this variable. If this method does only reading on the generic variable that is passed as parameter and that the compiler can ensure that : you have no type storing issue at runtime since you don't add anything in it. So you want to use an upper bounded wildcard.
List<Integer> integers = ...;
List<Long> longs = ...;
List<Float> floats = ...;
doThat(integers); // compile ok
doThat(longs); // compile ok
doThat(floats); // compile ok
//...
public void doThat(List<? extends Number> list){
for (Number n : list){ // compile ok : reading
//...
}
// but the compiler doesn't allow to store
list.add(Integer.valueOf(1));
}
Similarly without contravariance you lose all possibilities to add safely things in a generic type. If the compiler ensures that the contravariance is safe because we can only add supertype things in the generic type variable passed to , why do you want to lose this ability ? So you want to use a lower bounded wildcard.
List<Integer> integers = ...;
List<Long> longs = ...;
List<Float> floats = ...;
List<Number> numbers = ...;
doThat(integers); // compile ok
doThat(numbers); // compile ok
doThat(longs); // compile fails
doThat(floats); // compile fails
//...
public void doThat(List<? super Integer> list){
// the compiler allows to add that as type safe
list.add(Integer.valueOf(1));
// but it doesn't allow to add no safe things
list.add(Float.valueOf(1f));
}
Upvotes: 1