Generics and Subtyping What is wrong with the following: List<Integer>[] arrayList1 = new ArrayList<Integer>[10]; ArrayList<Integer>[] arrayList2 = new ArrayList<Integer>[10]; both cause a compile-time "generic array creation" error. Why? my examples are in SubTypingTests.java
Superclass Variables A superclass variable can be assigned a subclass object: Number num = new Integer(0); // ok Number is a superclass of Integer
Type Constructors Types come in two sorts: basic types: int, char, Integer, String, etc. type constructors: these are ways to build more complex types from the basic ones e.g. arrays and collections Integer[], ArrayList<String>
Covariance A type constructor is covariant if it preserves the super/subclass ordering of its component types. Example using []: Animal is a superclass of Cat Animal[] is a superclass of Cat[]
Array Subtyping is Covariant type S[] is a subtype of T[] when S is a subtype of T e.g. Number[] nums = new Integer[] {1,2,3}; // ok nums[0] = 3.14; // compiles, but raises an // ArrayStoreException at run-time
The array element type (Integer) is remembered by Java at run time called a reified type
Invariance A type constructor is invariant if it does not support the super/subclass ordering of its componenet types. Generics are invariant for their parameterized types List< Integer> is a superclass of ArrayList<Integer> ✔ List<Number> is invariant for ArrayList<Integer> ✘ ArrayList<Number> is invariant for ArrayList<Integer> ✘ invariance triggers compile-time errors
Superclass vars and Generics If a collection variable and collection object have the same parameterized type, then superclass variables are still allowed: List<Integer> iList = new ArrayList<Integer>(); // ok List is a superclass of ArrayList compare to the first slide!
Subtyping for Generics is Invariant List<S> is not a subtype of List<T> except where S and T are the same This means that superclass variables are not allowed if S and T are different, even if the collection (List) is the same. e.g. ArrayList<Number> nums = new ArrayList<Integer>(); // causes a compile-time error nums.put(0, 3.14); // Java never gets here
Type Erasure Parameterized type information is discarded after compilation has finished. e.g. at run-time, the JRE only knows: List<String> ==> List ArrayList<Fruit> ==> ArrayList Why? For binary compatibility with collections code written before Java 5
Problems with Type Erasure If generic subtype covariance was allowed, then the following code would incorrectly work: ArrayList<Number> nums = new ArrayList<Integer>(); nums.put(0, 3.14); // no run-time error, but there should be!! The reason is that the JRE only knows about ArrayList, not ArrayList<Integer> at run-time.
Back to the Question causes a compile time error. Why? List<Integer>[] arrLists = new ArrayList<Integer>[10]; causes a compile time error. Why? If the code was allowed to compile then type erasure would store ArrayList[] as the type of the object.
Then the following could happen: List<Double> doubleList = new ArrayList<Double>(); doubleList.add(Double.valueOf(1.23)); arrLists[0] = doubleList; // no error occurs, but should!! The last assignment would work because both objects (doubleList and arrLists[0]) are of type ArrayList at run-time.
Wildcards add covariant subtyping to generics List<S> is a subtype of List<? extends T> when S is a subtype of T e.g. List<Integer> ints = Arrays.asList(1,2,3); List<? extends Number> numsExt = ints; numsExt.put(2, 3.14); // compile-time error, not run-time // as happens with arrays
Contravariant Subtyping for Generics List<S> is a subtype of List<? super T> when S is a supertype of T Arrays do not support contravariant subtyping