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?