Wildcards in Parameterized Classes/Interfaces
in Java |
Prof. David Bernstein |
Computer Science Department |
bernstdh@jmu.edu |
Object
(since it is an ancestor of
all classes) but this isn't "type safe"Object
is appropriateextends
, followed by, what are
unfortunately called, the bounding types (separated by
an &)extends
was probably a bad choice
since any class that either extends the bounding type or
implements the bounding type can be used (which is why
there can be more than one)? extends Type
? super Type
Object
(if unbounded)Number[] n = new Number[10]; n[0] = Integer.valueOf(1); // An Integer "is a" Number n[1] = Double.valueOf(1.0); // A Double "is a" Number
Number[] n = new Number[10]; Integer[] i = new Integer[5]; // Won't compile because a Number[] "is not a" Integer[] i = n; // Will compile because an Integer[] "is a" Number[] n = i;
Number[] n; Integer[] i = new Integer[10]; i[0] = Integer.valueOf(5); i[1] = Integer.valueOf(6); n = i; // Will compile and run n[2] = Integer.valueOf(7); // Will compile but will throw an ArrayStoreException at run-time // to prevent heap pollution n[3] = Double.valueOf(7.5);
Statistics
class that can,
among other things, find the maximum and minimum
of a data setOrdered
interface to characterize
the datamin()
and max()
methods that
are passed
List<Ordered>
objects
Ordered
Interface/** * The requirements of objects that can be ordered. * * @author Prof. David Bernstein, James Madison University * @version 1.0 */ public interface Ordered { /** * Compare this object to the given object. Return -1 if this * is "less than" other, 0 if they are "equal", and 1 if this * is "greater than" other. * * @return -1, 0, or 1 as appropriate */ public abstract int compareTo(Ordered other); }
Statistics
Classimport java.util.*; /** * A utility class for calculating descriptive statistics. * * @author Prof. David Bernstein, James Madison University * @version 1.0 */ public class Statistics { /** * Find the maximum of some data points. * * @param data The data points */ public static Ordered max(List<Ordered> data) { Ordered max; if (data == null || data.size() == 0) throw new IllegalArgumentException(); if (data.size() == 1) return data.get(0); max = data.get(0); for (int i=1; i<data.size(); i++) { Ordered current = data.get(i); if (current.compareTo(max) > 0) max = current; } return max; } }
max()
that is passed
a List
of objects that
implements Ordered
(e.g., List<Person>
where
Person implements Ordered)
will not compile because the List
class must
ensure that its elements are Person
objects,
not Ordered
objects (a type of class
invariance)?
represents an unknown type? extends B
where
B, the upper bound, can be a class or interface
(and "extends" means "extends or implements")Object
(the root node) at the top and descendants at lower
levels
Statistics
Classimport java.util.*; /** * A utility class for calculating descriptive statistics. * * @author Prof. David Bernstein, James Madison University * @version 2.0 */ public class Statistics { /** * Find the maximum of some data points. * * @param data The data points */ public static Ordered max(List<? extends Ordered> data) { Ordered max; if (data == null || data.size() == 0) throw new IllegalArgumentException(); if (data.size() == 1) return data.get(0); max = data.get(0); for (int i=1; i<data.size(); i++) { Ordered current = data.get(i); if (current.compareTo(max) > 0) max = current; } return max; } }
List<? extends Number> n = new ArrayList<Number>(); List<Integer> i = new ArrayList<Integer>(); i.add(Integer.valueOf(5)); i.add(Integer.valueOf(6)); // Will compile because Integer "is a" ? extends Number n = i; // Can't be checked at run-time because of type erasure // so must be prevented at compile time (i.e., won't compile // because "the type is not applicable for the argument") n.add(Double.valueOf(7.5));
compareTo()
method is not type safe
because it only ensures that this
and
other
are both Ordered
(but not necessarily of the same type)Ordered
max()
and min()
methods can
now return an object of appropriate type
Ordered
Interface/** * The requirements of objects that can be ordered. * * @author Prof. David Bernstein, James Madison University * @version 3.0 */ // Note: It can't be <T extends Ordered> because Ordered must have a parameter public interface Ordered<T extends Ordered<T>> { /** * Compare this object to the given object. Return -1 if this * is "less than" other, 0 if they are "equal", and 1 if this * is "greater than" other. * * @return -1, 0, or 1 as appropriate */ public abstract int compareTo(Ordered<T> other); }
Statistics
Classimport java.util.*; /** * A utility class for calculating descriptive statistics. * * @author Prof. David Bernstein, James Madison University * @version 3.0 */ public class Statistics { /** * Find the maximum of some data points. * * @param data The data points */ public static <T extends Ordered<T>> T max(List<? extends T> data) { T max; if (data == null || data.size() == 0) throw new IllegalArgumentException(); if (data.size() == 1) return data.get(0); max = data.get(0); for (int i=1; i<data.size(); i++) { T current = data.get(i); if (current.compareTo(max) > 0) max = current; } return max; } }
Rectangle
classShape
interface that is realized by the
Rectangle
classList<Rectangle>
// The method public void populateBoundsList(List<Rectangle> bounds) { // In an appropriate loop { Rectangle r; // Construct the Rectangle bounds.add(r); } } // The invoker finder.populateBoundsList(new ArrayList<Rectangle>());
Rectangle
class, it may only need
the capabilities of the Shape
interfaceList<Shape>
// The method public void populateBoundsList(List<Shape> bounds) { // In an appropriate loop { Rectangle r; // Construct the Rectangle bounds.add(r); } } // The new invoker will compile finder.populateBoundsList(new ArrayList<Shape>()); // The old invoker will not compile finder.populateBoundsList(new ArrayList<Rectangle>());
List<? extends Shape>
// The method public void populateBoundsList(List<? extends Shape> bounds) { // In an appropriate loop { Rectangle r; // Construct the Rectangle // Won't compile because bounds is "read only" to prevent // heap pollution bounds.add(r); } } // The new invoker will compile finder.populateBoundsList(new ArrayList<Shape>()); // The old invoker will compile finder.populateBoundsList(new ArrayList<Rectangle>());
? super B
where
B is the lower boundObject
(the root node) at the top and descendants at lower
levels// The flexible version of the method public void populateBoundsList(List<? super Rectangle> bounds) { // In an appropriate loop { Rectangle r; // Construct the Rectangle bounds.add(r); } } // The new invoker will compile because it is assured that // the elements will implement Shape finder.populateBoundsList(new ArrayList<Shape>()); // The old invoker will compile because it is assured that // the elements will be Rectangle objects finder.populateBoundsList(new ArrayList<Rectangle>());
List<Integer> i = new ArrayList<Integer>(); List<Number> n = new ArrayList<Number>(); List<? super Integer> other; other = i; // Will compile because i can treat the element as an Integer other.add(Integer.valueOf(1)); // Won't compile because the element could be any ancestor // of Integer Integer i2i = other.get(0); Number i2n = other.get(0); other = n; // Will compile because n can treat the element as a Number other.add(Integer.valueOf(1)); // Won't compile because the element could be any ancestor // of Integer Integer n2i = other.get(0); Number n2n = other.get(0);
T
(i.e., the method gets objects from the collection) then
it can be of type <? extends T>
(because it can produce more specialized objects)T
(i.e., the method adds objects to the collection) then
it can be of type <? super T>
(because it can consume more generalized objects)