|
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)