2

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.

  1. 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?"

  2. Generics are invariant:

    List<Number> myNums = new ArrayList<Integer>(); // compile not ok 
    
  3. 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 
    
  4. 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?

cchantep
  • 9,118
  • 3
  • 30
  • 41
jack
  • 1,787
  • 14
  • 30
  • You need to first take a look at this: https://stackoverflow.com/questions/18666710/why-are-arrays-covariant-but-generics-are-invariant – Gaurav Mall Aug 25 '19 at 07:55
  • 1
    @GauravMall Thank you for the link, I know what's allowed and what's not allowed in syntax but I'm just a bit confused about the term, if covariance of array will have run time error when writing like this `myNums[0] = 2.1;` as the example above, why is it still called covariance? What's the definition indeed for this term? – jack Aug 26 '19 at 06:08
  • I think the bellow answers should cover your question. Blogs and Articles have a lot of info about covariance, but sometimes they are badly written. Maybe the answer here helps you. – Gaurav Mall Aug 26 '19 at 06:10

1 Answers1

1

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));
}        
davidxxx
  • 125,838
  • 23
  • 214
  • 215
  • Thank you @davidxxx, if I understand correctly, covariance of array in Java is a historical drawback, covariance of generic allows read but not write, contravariance of generic accepts its own type or its super type and allows write but not read. – jack Aug 26 '19 at 05:36
  • But then the question is, is this really useful? I could not think of an example that this feature of generics ease the life of developers. – jack Aug 26 '19 at 05:42
  • You are welcome. Your understanding is correct. That is a legacy structure. And at that time, making array invariant would also be an important limitation in terms of inheritance features. If your question is : is array useful ? Sure but only in specific cases. Mainly performance and brevity. Look at the `ArrayList` implementation. It uses an array under the hood. The table used in the `HashMap` implementation is also an array. And in a very different context, look at the array we use as attributes for Java annotation. – davidxxx Aug 26 '19 at 12:43
  • In the collections case note that the covariance risk doesn't exist from the client side since the implementation of these collections guarantee the type safety thanks to the fact that these classes be generic. – davidxxx Aug 26 '19 at 12:52