Upload
whitney-little
View
221
Download
1
Embed Size (px)
Citation preview
ADSA: Subtypes/7 1
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
ADSA: Subtypes/7 2
Contents
1. The Substitution Principle
2. Subtyping of An Entire Collection
3. Subtyping/subclassing Terminology
4. Covariance: ? extends
5. Contravariance: ? super
6. The Get and Put Principle (PECS)
7. No Instance Creation with ?
ADSA: Subtypes/7 3
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
Float Integer
extends
Number
reference
Integer
object
n
i
ADSA: Subtypes/7 4
Apple a = new Apple();
Fruit f = a;Fruit
Apple Strawberry
extends
Fruit
reference
Apple
object
f
a
ADSA: Subtypes/7 5
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
ADSA: Subtypes/7 6
List<Number>
reference
Integer
object
nums . . .
Double
object
List<Number> object
Number
references
3.142
ADSA: Subtypes/7 7
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
ADSA: Subtypes/7 8
List<Number>
reference
Integer
object
nums . . .
Double
object
List<Integer> object
Integer
references
32
ints
ADSA: Subtypes/7 9
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)
ADSA: Subtypes/7 10
Another Example
List<Apple> apples = ...;
List<Fruit> fruits = apples; // compile-time error
ADSA: Subtypes/7 11
• Just to confuse matters, Integer[] is a subclass of Number[].
Integer[] ints = new Integer[] {1,2,3};
Number[] nums = ints; // OK
nums[2] = 3.12; // run-time error (will be reached)
Arrays are Different
ADSA: Subtypes/7 12
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
ADSA: Subtypes/7 13
• 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>
ADSA: Subtypes/7 14
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
ADSA: Subtypes/7 15
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
ADSA: Subtypes/7 16
• 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
ADSA: Subtypes/7 17
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
ADSA: Subtypes/7 18
• The restriction is because the compiler only knows that fs refers to a collection subclass of Fruit, but not which one.
ADSA: Subtypes/7 19
• 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);
ADSA: Subtypes/7 20
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'
Good for
'getting'
ADSA: Subtypes/7 21
5. Contravariance: ? superList<Object> objs = Arrays.<Object>asList(1,"two");
List<? super Integer> vals = objs;
vats.add(3); // ok
•? super allows a superclass collection of Integer to be assigned to vals
– in this case, a list of Objects
•And you can add subclass elements to the collection later.
ADSA: Subtypes/7 22
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.
ADSA: Subtypes/7 23
Why?
• The compiler knows that apps refers 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.
ADSA: Subtypes/7 24
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
ADSA: Subtypes/7 25
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"
ADSA: Subtypes/7 26
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'
Good for
'putting'
ADSA: Subtypes/7 27
6. The Get and Put Principle (PECS)
– use ? extends when you only get values out of a collection; i.e. is a producer
– use ? super when you only put values 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"
ADSA: Subtypes/7 28
? extends Function example
public static double sum( Collection<? extends Number> nums)
{
double s = 0.0;
for (Number num : nums)
s += num.doubleValue();
return s;
}
PECS
producer(a Number method)
ADSA: Subtypes/7 29
• 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.
ADSA: Subtypes/7 30
? super function example
public static void count( Collection<? super Integer> ints, int n)
{
for (int i = 0; i < n; i++)
ints.add(i);
}
PECS
consumer
ADSA: Subtypes/7 31
• 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()
ADSA: Subtypes/7 32
No wildcard example
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).
Number
Integer
? extend
? super
ADSA: Subtypes/7 33
• 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);
Number
Integer
? extend
? super
ADSA: Subtypes/7 34
Using both ? extends and ? super
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.
PECS
ADSA: Subtypes/7 35
• 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]"
ADSA: Subtypes/7 36
7. No Instance Creation with ?
• For example, the following are illegal:
List<?> list = new ArrayList<?>();
// compile-time error
Map<String, ? extends Number> map = new HashMap<String, ? extends Number>();
// compile-time error
ADSA: Subtypes/7 37
• 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);