CS 252

Lab #10

The purpose of this lab is to understand wildcard types in Java.

We start with this Pair class.

import java.util.AbstractList;

public class Pair<T> extends AbstractList<T>
{
   public Pair() { first = null; second = null; }
   public Pair(T first, T second) { this.first = first;  this.second = second; }

   public int size() { return 2; }

   public T get(int n) { return n == 0 ? first : second; }

   public T set(int n, T t) 
   {
      T r = get(n);
      if (n == 0) first = t; else second = t; 
      return r;
   }

   private T first;
   private T second;
}

In order to avoid the tedious Employee/Manager examples, we will instead use the infinitely more exciting

import java.math.*;

public class LabeledDecimal extends BigDecimal
{
   public LabeledDecimal(String label, String digits)
   {
      super(digits);
      this.label = label;
   }

   public String getLabel() { return label; }

   public String toString() { return label + "=" + super.toString(); }

   private String label;
}

For example,

new LabeledDecimal("pi", "3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825"); 

Part 1. We want to understand conversion rules with Java generics. As you know, if S is a subtype of T, then the assignment

S s = . . .;
T t = s;

does not require a cast. For example, String is a subtype of Object and String[] is a subtype of Object, but ArrayList<String> is not a subtype of ArrayList<Object>. This can easily be demonstrated by trying to compile a program such as

public class Test
{
   String str;
   Object obj;
   String[] strs;
   Object[] objs;
   ArrayList<String> strAL;
   ArrayList<Object> objAL;

   void test() 
   {
      obj = str; // no error
      objs = strs; // no error
      objAL = strAL; // compiler error
   }
}

A: Investigate the subtype relationships between Object, String, BigDecimal, LabeledDecimal.

B: Investigate the subtype relationships between Pair<Object>, Pair<BigDecimal>, Pair<LabeledDecimal>.

C: Investigate the subtype relationships between ArrayList<? extends BigDecimal>, ArrayList<? super BigDecimal>, ArrayList<BigDecimal>.

In your lab report, tell me which subtype relationships were valid and which were not.


Part 2. We want to enhance the Pair class.

A. Implement a method copyFrom in the Pair class that sets this pair to another pair. Use wildcards so that the following call succeeds:

Pair<BigDecimal> p1 = new Pair<BigDecimal>();
LabeledDecimal ld1 = new LabeledDecimal("pi", "3.14");
LabeledDecimal ld2 = new LabeledDecimal("sqrt(2)", "1.414");
Pair<LabeledDecimal> p2 = new Pair<LabeledDecimal>(ld1, ld2);
p1.copyFrom(p2);
System.out.println(p1);

B: Implement a method copyTo in the Pair class that sets another pair to this pair. Use wildcards so that the following call succeeds:

LabeledDecimal ld1 = new LabeledDecimal("pi", "3.14");
LabeledDecimal ld2 = new LabeledDecimal("sqrt(2)", "1.414");
Pair<LabeledDecimal> p1 = new Pair<LabeledDecimal>(ld1, ld2);
Pair<BigDecimal> p2 = new Pair<BigDecimal>();
p1.copyTo(p2);
System.out.println(p2);

C: Consider this attempt at a min method for the Pair class

   public T min(Comparator<T> comp)
   {
      if (comp(first, second) < 0) 
         return first; 
      else 
         return second;
   }

Try it out with this comparator:

public class ContrivedComparator implements Comparator<BigDecimal>
{
   public int compare(BigDecimal a, BigDecimal b)
   {
      return a.precision() - b.precision();
   }
}

Make a

Pair<BigDecimal> p1 = new Pair<BigDecimal>(new BigDecimal("3.14", "1.414");

Can you compute p1.min(new ContrivedComparator())? What is it? Why?

Now try the same with

LabeledDecimal ld1 = new LabeledDecimal("pi", "3.14");
LabeledDecimal ld2 = new LabeledDecimal("sqrt(2)", "1.414");
Comparator comp = new ContrivedComparator();
System.out.println(comp(ld1, ld2)); // not an error!
Pair<LabeledDecimal> p2 = new Pair<LabeledDecimal>(ld1, ld2);
System.out.println(p2.min(comp));

What error do you get? Why? How can you use wildcards to fix the min method (without changing ContrivedComparator, of course).

D. Consider a method min that yields the smaller of the two pair values:

   public T min() 
   {
      if (first.compareTo(second) < 0)
         return first;
      else
         return second;
   }

Why doesn't this code compile?

Ok, add extends Comparable<T> to the Pair type declaration and check that it compiles. Now try this code:

LabeledDecimal ld1 = new LabeledDecimal("pi", "3.14");
LabeledDecimal ld2 = new LabeledDecimal("sqrt(2)", "1.41");
Pair<BigDecimal> p1 = new Pair<BigDecimal>(ld1, ld2);
System.out.println(p1.min());

Did it compile? What did it print?

Now try a Pair<Object> instead.

LabeledDecimal ld1 = new LabeledDecimal("pi", "3.14");
LabeledDecimal ld2 = new LabeledDecimal("sqrt(2)", "1.41");
Pair<Object> p1 = new Pair<Object>(ld1, ld2);
System.out.println(p1.min());

What happens? Why?

Now try a Pair<LabeledDecimal>

LabeledDecimal ld1 = new LabeledDecimal("pi", "3.14");
LabeledDecimal ld2 = new LabeledDecimal("sqrt(2)", "1.41");
Pair<LabeledDecimal> p1 = new Pair<LabeledDecimal>(ld1, ld2);
System.out.println(p1.min());

What happens? Why?

Fix the Pair class so that min works with a Pair<LabeledDecimal>. What is your fix?