370 likes | 539 Views
241-423 Advanced Data Structures and Algorithms. Objective explain how subtyping/subclassing and generics are combined in Java introduce covariance and contravariance. Semester 2, 2013-2014. 7 . Subtypes/subclassing and Generics. Contents. The Substitution Principle
E N D
241-423 Advanced Data Structures and Algorithms Objective explain how subtyping/subclassing and generics are combined in Java introduce covariance and contravariance Semester 2, 2013-2014 7. Subtypes/subclassing and Generics
Contents • The Substitution Principle • Subtyping of An Entire Collection • Subtyping/subclassing Terminology • Covariance: ? extends • Contravariance: ? super • The Get and Put Principle (PECS) • No Instance Creation with ?
1. The Substitution Principle • You can assign an object of any subclass to a class reference (variable). Integer i = new Integer(6); Number n = i; Number extends Float Integer n Integer object Number reference i
Apple a = new Apple(); Fruit f = a; Fruit extends Apple Strawberry f Apple object Fruit reference a
Subtyping of Collection Elements • We can add an integer or a double to a collection of Numbers, because Integer and Double are subclasses of Number. List<Number> nums = new ArrayList<Number>(); nums.add(2); nums.add(3.14); // OK
Number references nums . . . List<Number> reference List<Number> object 3.14 2 Integer object Double object
2. Subtyping of An Entire Collection • You might think that since Integer is a subclass of Number, then List<Integer> is a subclass of List<Number>. NO!!! List<Integer> ints = Arrays.asList(2, 3); List<Number> nums = ints; // compile-time err
Integer references ints nums . . . List<Number> reference List<Integer> object 3 2 Integer object Double object
Why? • It's to prevent run-time errors such as the following: List<Integer> ints = new Integer[] {1,2}; List<Number> nums = ints; // compile-time error nums.add(3.12); // run-time error (not reached)
Another Example List<Apple> apples = ...; List<Fruit> fruits = apples; // compile-time error
Arrays are Different • Just to confuse matters, Integer[] isa subclass of Number[]. Integer[] ints = new Integer[] {1,2,3}; Number[] nums = ints; // OK nums[2] = 3.12; // run-time error (will be reached)
3. Subtyping/subclassing Terminology • Covariance: a class reference (variable) can point to a 'narrower' subclass object • e.g. Fruit f = new Apple(); • narrower means "more specific" • Contravariance: a class reference (variable) can point to a 'wider' superclass object • e.g from Integer to Number • not common in Java • Invariant: not able to convert
Arrays are covariant: • Integer[] is also a Number[] • Basic Generic classes are not covariant and not contravariant: they are invariant • For example: • List<Integer> is not a subclass List<Number> • Also List<Number> is not a subclass of List<Integer>
Adding Covariance and Contravariance • It is useful to allow covariance and contravariance in collections, so the Java designers added: • ? extends (extends wildcard) for covariance • ? super (super wildcard) for contravariance
4. Covariance: ? extends List<Integer> ints = Arrays.asList(1,2); List<? extends Number> nums = ints; // OK • "? extends Number" means that it is OK to assign a subclass collection of Number to nums • in this case, a collection of Integers
But we cannot put elements into the collection... List<Integer> ints = Arrays.asList(1,2); List<? extends Number> nums = ints; // OK nums.add(3.12); // compile-time error • This is the array situation, but with one big difference • the error is caught at compile-time
Another ? Extends Example List<Apple> apples = new ArrayList<Apple>(); List<? extends Fruit> fs = apples; // OK fs.add(new Strawberry()); //compile-time err • The code won't compile even if we try to add an Apple or a Fruit instance fs.add(new Apple()); //compile-time err fs.add(new Fruit()); //compile-time err
The restriction is because the compiler only knows that fsrefers to a collection subclass of Fruit, but not which one.
The only thing we can add is null: fruits.add(null); // ok • We can get data out of the structure but only as a Fruit instance (the ? extends class): Fruit get = fruits.get(0);
Summary of ? extends • It allows the assignment of a subclass collection to a superclass collection reference, but no element assignments are possible • quite restrictive • Values can be removed, as instances of the superclass • useful Good for 'getting'
5. Contravariance: ? super List<Object> objs = Arrays.<Object>asList(1,"two"); List<? super Integer> vals = objs; vats.add(3); // ok • ? super allows a superclasscollection of Integer to be assigned to vals • in this case, a list of Objects • Andyou can add subclasselements to the collection later.
Another ? super example List<Fruit> fruits = new ArrayList<Fruit>(); List<? super Apple> apps = fruits; apps.add(new Apple()); // OK apps.add(new GreenApple()); // OK apps.add(new Fruit()); // compile-time error apps.add(new Object()); // compile-time error • Only subclass elements of Apple can be added.
Why? • The compiler knows that appsrefers to a superclass collection of Apple. • It does not know which class, but all superclasses of Apple can store subclasses of Apple as new elements.
Get Restriction with ? Super • You can only get elements out of a ? super object as values of class Object: Object ob1 = fruits.get(0); Object ob2 = fruits.get(1); Fruit f = (Fruit) fruits.get(0); // compile-time err
Another Example List<Object> objs = Arrays.<Object>asList(1,"two"); List<? super Integer> ints = objs; String str = ""; for (Object obj : ints) str += obj.toString(); // Object method // str is "1two"
Summary of ? super • It allows the assignment of a superclass collection to a subclass reference, and further subclass additions are possible • useful • Values can only be removed as Object instances • quite restrictive Good for 'putting'
6. The Get and Put Principle (PECS) • use ? extends when you only getvalues out of a collection; i.e. is a producer • use ? super when you only putvalues into a collection; i.e. is a consumer • don't use a wildcard when you both get and put • PECS comes from "Producer Extends, Consumer Super"
? extends Function example PECS public static double sum( Collection<? extends Number> nums) { double s = 0.0; for (Number num : nums) s += num.doubleValue(); return s; } producer (a Number method)
The following calls are legal: List<Integer> ints = Arrays.asList(1, 2, 3); double d = sum(ints); // == 6.0; List<Double> doubles = Arrays.asList(2.78, 3.14); double d = sum(doubles); // == 5.92; List<Number> nums = Arrays.<Number>asList(1, 2, 2.78, 3.14); double d = sum(nums); // == 8.92; • The first two calls would not be legal if ? extends was not used.
? super function example PECS public static void count( Collection<? super Integer> ints, int n) { for (int i = 0; i < n; i++) ints.add(i); } consumer
The following calls are legal: List<Integer> ints = new ArrayList<Integer>(); count(ints, 5); // ints.toString().equals("[0, 1, 2, 3, 4]"); List<Number> nums = new ArrayList<Number>(); count(nums, 5); nums.add(5.0); // nums.toString().equals("[0, 1, 2, 3, 4, 5.0]"); List<Object> objs = new ArrayList<Object>(); count(objs, 5); objs.add("five"); // objs.toString().equals("[0, 1, 2, 3, 4, five]"); • The last two calls need ? super in count()
? super No wildcard example Number public static double sumCount( Collection<Number> nums, int n) { count(nums, n); return sum(nums); } • The nums collection is passed to both sum() and count(), so its element class must both extend Number (as sum() requires) and be super to Integer (as count() requires). Integer ? extend
? super Number • The only two classes that satisfy both of these constraints are Number and Integer, and I picked the first. • A sample call: List<Number> nums = new ArrayList<Number>(); double sum = sumCount(nums,5); Integer ? extend
Using both ? extends and ? super PECS public static <T> void copy( List<? super Integer> dst, List<? extends Integer> src) { for (int i = 0; i < src.size(); i++) dst.set(i, src.get(i)); } • the destination list may already contains elements of any class that is a superclass of Integer • the source list will have elements of any class that is a subclass of Integer.
A sample call: List<Object> objs = Arrays.<Object>asList(2, 3.14, "four"); List<Integer> ints = Arrays.asList(5, 6); Collections.copy(objs, ints); // objs is "[5, 6, four]"
7. No Instance Creation with ? • For example, the following are illegal: List<?> list = newArrayList<?>(); // compile-time error Map<String, ? extends Number> map = newHashMap<String, ? extends Number>(); // compile-time error
Usually we create a concrete collection first, even if we use wildcards later: List<Number> nums = new ArrayList<Number>(); List<? super Number> sink = nums; // OK for (int i=0; i<10; i++) sink.add(i);