Skip to content

Instantly share code, notes, and snippets.

@tedyoung
Forked from mallipeddi/gist:74274
Created March 20, 2018 21:11
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tedyoung/aca78496d424790bb0f19d36a4618f76 to your computer and use it in GitHub Desktop.
Save tedyoung/aca78496d424790bb0f19d36a4618f76 to your computer and use it in GitHub Desktop.
Demonstrates the concepts of covariance and contravariance in the Java type system
import java.util.*;
/*
* Covariance and contravariance.
*
* A type operator (an operator which takes as input a type and returns another type as output) is
* said to be
* 1) `covariant` if it preserves the ordering of types and orders types
* from more specific ones to more generic ones.
* 2) `contravariant` if it preserves the ordering of types and orders types
* from more generic ones to more specific ones.
* 3) `invariant` if no ordering of types is preserved.
*
* Example of covariance:
* String <= Object (`<=` means lhs is more specific than rhs).
* and String[] <= Object[] (hence array operator [] is covariant in Java).
*
* Example of invariance:
* String <= Object
* and List<String> =/= List<Object> (no ordering is preserved in generic types in Java hence invariant).
*
* Why would you care about covariance and contravariance while programming? Answer: "substitutability".
* For instance if a Java generic type was covariant, you can subsitute a more specific generic type
* in place of a more generic generic type.
* Example:
* List<Object> = someObj.someMethod(); // someObj.someMethod returns List<String>
*
* Similarly if a Java generic type was contravariant, you can substitute a more generic generic type
* in place of a more specific generic type.
* Example:
* someObj.someMethod(List<Object>); // someMethod expects List<String> as argument
*
* Arrays in Java and C# are covariant by design. This would be fine if arrays were
* immutable (ie only supported get() operation). But arrays are mutable in Java and C# (ie)
* they support reading/get() and writing/set() elems in the array.
*
* Example:
* String[] a = new String[1];
* Object[] b = a;
* Object o = b[0]; // get(0) does not break type-safety
* b[0] = 1; // set(0) will break type-safety (raises runtime error).
*
* In order to ensure type-safety, either arrays should be made "immutable and covariant" or "mutable and invariant".
*
* Generic types both in C# and Java are invariant by default. In C#4.0, they're adding support so that generic types
* can be made covariant and contravariant as necessary provided the generic interfaces obey certain rules.
*
* Read more:
* http://en.wikipedia.org/wiki/Covariance_and_contravariance_(computer_science)
* http://etymon.blogspot.com/2007/02/java-generics-and-covariance-and.html
* http://acodingfool.typepad.com/blog/2009/02/the-new-and-improved-csharp-40-part-2.html
*
*/
public class CovarianceTest {
public static void main(String[] args) {
// allowed
String s1 = "foobar";
Object o1 = s1;
// allowed
// but breaks type-safety (read above for explanation)
String[] arr1 = new String[2];
Object[] arr2 = arr1;
// allowed
ArrayList<String> al1 = new ArrayList<String>();
List<String> l1 = al1;
l1.add(new String("foo"));
// not allowed
// ArrayList<Object> al2 = al1;
// allowed - generics with wildcard ?
ArrayList<?> al3 = al1;
Object o2 = al3.get(0);
// not allowed
// al3.add(new String("foo"));
// allowed - generics with wildcard extends
List<Integer> l2 = new ArrayList<Integer>();
List<? extends Number> l3 = l2;
Number n1 = l3.get(0);
// not allowed
// l3.add(new Integer(1));
// allowed - generics with wildcard super
List<Number> l4 = new ArrayList<Number>();
List<? super Integer> l5 = l4;
l5.add(new Integer(1));
// not allowed
// Number n2 = l5.get(0);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment