Section 3.3. A Fruity Example


3.3. A Fruity Example

The Comparable<T> interface gives fine control over what can and cannot be compared. Say that we have a Fruit class with subclasses Apple and Orange. Depending on how we set things up, we may prohibit comparison of apples with oranges or we may permit such comparison.

Example 3.2 prohibits comparison of apples with oranges. Here are the three classes it declares:

 class Fruit {...} class Apple extends Fruit implements Comparable<Apple> {...} class Orange extends Fruit implements Comparable<Orange> {...} 

Each fruit has a name and a size, and two fruits are equal if they have the same name and the same size. Following good practice, we have also defined a hash method, to ensure that equal objects have the same hash code. Apples are compared by comparing their sizes, and so are oranges. Since Apple implements Comparable<Apple>, it is clear that you can compare apples with apples, but not with oranges. The test code builds three lists, one of apples, one of oranges, and one containing mixed fruits. We may find the maximum of the first two lists, but attempting to find the maximum of the mixed list signals an error at compile time.

Example 3.1 permits comparison of apples with oranges. Compare these three class declarations with those given previously (all differences between Examples 3.2 and 3.1 are highlighted):

Example 3-1. Permitting comparison of apples with oranges

 abstract class Fruit implements Comparable<Fruit> {   protected String name;   protected int size;   protected Fruit(String name, int size) {     this.name = name; this.size = size;   }   public boolean equals(Object o) {     if (o instanceof Fruit) {       Fruit that = (Fruit)o;       return this.name.equals(that.name) && this.size == that.size;     } else return false;   }   public int hash() {     return name.hash()*29 + size;   }   public int compareTo(Fruit that) {     return this.size < that.size ? - 1 :            this.size == that.size ? 0 : 1 ;   } } class Apple extends Fruit {   public Apple(int size) { super("Apple", size); } } class Orange extends Fruit {   public Orange(int size) { super("Orange", size); } } class Test {   public static void main(String[] args) {     Apple a1 = new Apple(1);  Apple a2 = new Apple(2);     Orange o3 = new Orange(3);  Orange o4 = new Orange(4);     List<Apple> apples = Arrays.asList(a1,a2);     assert Collections.max(apples).equals(a2);     List<Orange> oranges = Arrays.asList(o3,o4);     assert Collections.max(oranges).equals(o4);     List<Fruit> mixed = Arrays.<Fruit>asList(a1,o3);     assert Collections.max(mixed).equals(o3);  // ok   } } 

Example 3-2. Prohibiting comparison of apples with oranges

 abstract class Fruit {   protected String name;   protected int size;   protected Fruit(String name, int size) {     this.name = name; this.size = size;   }   public boolean equals(Object o) {     if (o instanceof Fruit) {       Fruit that = (Fruit)o;       return this.name.equals(that.name) && this.size == that.size;     } else return false;   }   public int hash() {     return name.hash()*29 + size;   }   protected int compareTo(Fruit that) {     return this.size < that.size ? -1 :            this.size == that.size ? 0 : 1 ;   } } class Apple extends Fruit implements Comparable<Apple> {   public Apple(int size) { super("Apple", size); }   public int compareTo(Apple a) { return super.compareTo(a); } } class Orange extends Fruit implements Comparable<Orange> {   public Orange(int size) { super("Orange", size); }   public int compareTo(Orange o) { return super.compareTo(o); } } class Test {   public static void main(String[] args) {     Apple a1 = new Apple(1);  Apple a2 = new Apple(2);     Orange o3 = new Orange(3);  Orange o4 = new Orange(4);     List<Apple> apples = Arrays.asList(a1,a2);     assert Collections.max(apples).equals(a2);     List<Orange> oranges = Arrays.asList(o3,o4);     assert Collections.max(oranges).equals(o4);     List<Fruit> mixed = Arrays.<Fruit>asList(a1,o3);     assert Collections.max(mixed).equals(o3);  // compile-time error   } } 

 class Fruit implements Comparable<Fruit> {...} class Apple extends Fruit {...} class Orange extends Fruit {...} 

As before, each fruit has a name and a size, and two fruits are equal if they have the same name and the same size. Now any two fruits are compared by ignoring their names and comparing their sizes. Since Fruit implements Comparable<Fruit>, any two fruits may be compared. Now the test code can find the maximum of all three lists, including the one that mixes apples with oranges.

Recall that at the end of the previous section we extended the type signature of compareTo to use super:

 <T extends Comparable<? super T>> T max(Collection<? extends T> coll) 

The second example shows why this wildcard is needed. If we want to compare two oranges, we take T in the preceding code to be Orange:

 Orange extends Comparable<? super Orange> 

And this is true because both of the following hold:

 Orange extends Comparable<Fruit>  and  Fruit super Orange 

Without the super wildcard, finding the maximum of a List<Orange> would be illegal, even though finding the maximum of a List<Fruit> is permitted.

Also note that the natural ordering used here is not consistent with equals (see Section 3.1). Two fruits with different names but the same size compare as the same, but they are not equal.




Java Generics and Collections
Java Generics and Collections
ISBN: 0596527756
EAN: 2147483647
Year: 2006
Pages: 136

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net