JMU
Wildcards in Parameterized Classes/Interfaces
in Java


Prof. David Bernstein
James Madison University

Computer Science Department
bernstdh@jmu.edu


Review
Review (cont.)
Topics of this Lecture
Reification
Type Erasure
Heap Pollution
Review of Arrays
Review of Arrays (cont.)
Assignment of Elements
    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
  
Review of Arrays (cont.)
Assignment of Arrays
    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; 
  
Review of Arrays (cont.)
Compile-Time vs. Run-Time
    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);
  
Review of Arrays (cont.)
Motivating Covariance
Motivating Covariance (cont.)

The Ordered Interface

javaexamples/parameterized/wildcards/v1/Ordered.java
/**
 * 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);
}        
Motivating Covariance (cont.)

The Statistics Class

javaexamples/parameterized/wildcards/v1/Statistics.java
import 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;
  }
}        
Motivating Covariance (cont.)
Using Covariance
Using Covariance (cont.)
Using Covariance (cont.)

A Revised Statistics Class

javaexamples/parameterized/wildcards/v2/Statistics.java
import 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;
  }
}        
Using Covariance (cont.)
Using Covariance (cont.)
Mimicking the Array Example with Lists
      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));
  
Using Covariance on More Complicated Types
Using Covariance on More Complicated Types (cont.)

A Revised Ordered Interface

javaexamples/parameterized/wildcards/v3/Ordered.java
/**
 * 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);
}        
Using Covariance on More Complicated Types(cont.)

A Further Revised Statistics Class

javaexamples/parameterized/wildcards/v3/Statistics.java
import 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;
  }
}
        
Motivating Contravariance
Motivating Contravariance (cont.)
The Initial Implementation
  // 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>());
  
Motivating Contravariance (cont.)
Motivating Contravariance (cont.)
The Incorrect "Fix"
  // 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>());
  
Motivating Contravariance (cont.)
Motivating Contravariance (cont.)
The Incorrect "Fix"
  // 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>());
  
Motivating Contravariance (cont.)
Using Contravariance
Using Contravariance (cont.)
Motivating Contravariance (cont.)
The Example Revisited
  // 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>());
  
Using Contravariance (cont.)
Using Contravariance (cont.)
A Different Version of the Example with Lists
      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);
  
Putting it All Together