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 ADSA: Subtypes/7 1 Superclass Variables • A superclass variable can be assigned a subclass object: • Number num = new Integer(0); – Number is a superclass of Integer ADSA: Subtypes/7 // ok 2 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> ADSA: Subtypes/7 3 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[] ADSA: Subtypes/7 4 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 ADSA: Subtypes/7 5 • The array element type (Integer) is remembered by Java at run time – called a reified type ADSA: Subtypes/7 6 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 ADSA: Subtypes/7 7 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! ADSA: Subtypes/7 8 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); ADSA: Subtypes/7 // Java never gets here 9 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 ADSA: Subtypes/7 10 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. ADSA: Subtypes/7 11 Back to the Question • 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. ADSA: Subtypes/7 12 • 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. ADSA: Subtypes/7 13 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 ADSA: Subtypes/7 14 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 ADSA: Subtypes/7 15