Java lesson on generics are leading me to variance concept. This causes me some headaches as I cannot find a very simple demonstration of what it is.
I have read several similar questions on stackoverflow, but I found them too difficult to understand for a Java learner. Actually the problem is that the explanation of generics requires variance to be understood, and the concept of variance is demonstrated relying heavily on generics understanding.
I had some hope reading this, but in the end I shared C. R.'s feeling:
The title reminds me of the days learning general relativity. – C.R. Dec 22 '13 at 7:34
Four theory questions are very confusing to me, and I cannot find good and simple explanations. Here they are, with my current partial understanding (I fear experts will have a great fun reading this).
Your help to correct and clarify is welcome (remember this is for beginners, not for experts).
Covariance can be translated as "different in the same direction," or with-different, whereas contravariance means "different in the opposite direction," or against-different. Covariant and contravariant types are not the same, but there is a correlation between them. The names imply the direction of the correlation.
In C#, covariance and contravariance enable implicit reference conversion for array types, delegate types, and generic type arguments. Covariance preserves assignment compatibility and contravariance reverses it.
Invariance: refers to the property of objects being left unchanged by symmetry operations. Covariance: refers to equations whose form is preserved by a change of coordinate system.
In the field of physics, the adjective covariant is often used informally as a synonym for invariant. For example, the Schrödinger equation does not keep its written form under the coordinate transformations of special relativity.
This is not specific to OO, but has to do with the properties of certain types.
For example, with the function type
A -> B // functional notation
public B meth(A arg) // how this looks in Java
we have the following:
Let C be a subtype of A, and D be a subtype of B. Then the following is valid:
B b = meth(new C()); // B >= B, C < A
Object o = meth(new C()); // Object > B, C < A
but the follwoing are invalid:
D d = meth(new A()); // because D < B
B b = meth(new Object()); // because Object > A
hence, to check whether a call of meth is valid, we must check
This is all well known and intuitive. By convention we say that the return type of a function is covariant, and the argument type of a method is contravariant.
With parameterized types, like List, we have it that the argument type is invariant in languages like Java, where we have mutability. We can't say that a list of C's is a list of A's, because, if it were so, we could store an A in a list of Cs, much to the surprise of the caller, who assumes only Cs in the list. However, in languages where values are immutable, like Haskell, this is not a problem. Because the data we pass to functions cannot be mutated, a list of C actually is a list of A if C is a subtype of A. (Note that Haskell has no real subtyping, but has instead the related notion of "more/less polymorphic" types.)
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