For instance, if A < B (B extends A), What is the relationship betwern Array[A] and Array[B]?
If you are just reading the array, you would want Array[A] < Array[B]. If you are writing to the array, you would want Array[B]<Array[A]. If you are doing both, you want Array[A] to have no relation to Array[B].
This problem doesn't come up in ML style languages because they do not make use of inheritence.
He's right. It's easy to understand one level of variance: you can replace a return type by a subtype and a parameter type by a supertype. (I wouldn't be surprised that many programmer don't undestand this.)
Two levels already requires some deep thinking (assuming definition-site variance: `List<Object>` or `List<String>` as a return type / parameter type, was is allowed to replace it?
More than that? (`List<List<String>>`) Hahaha, good luck.
And by the way, Java has notations to specify the variance of type, but only at the use-site, which is different from doing it at the definition site (both enable expressing things the other can't do... but you can actually have both, as I think is the case in Kotlin, though there are some limitations).
Which of course doesn't change the fact that imperative languages trying to combine generics + inheritance + mutability are in for a world of hurt.
Except for OCaml and Scala (or any other ML supporting subtyping), where you could simply define type's variance.
def writeFirst: (xs: Array[B], x:B): xs[0]=x;
var as: Array[A] = ???
var b:B = ???
var a:A = ???
a=b; //This is fine, since B extends A.
as[0]=a //Obviously fine, since a:A
as[0]=b //This better be fine, otherwise the above 2 lines just punched a hole in our type system.
writeFirst(as,b); //If I am allowed to do the above, I should be allowed to do this.
For completeness, the opposite example: def getFirst(xs: Array[B]):B = xs[0];
var as:Array[A]
var b:B = as[0] //This shouldn't work. Not all A's are B's
var b:B = getFirst(as) //Simmilarly, this shouldn't work.I must be missing something.