Comparable ========== interface Comparable \br{ public int compareTo(T o); } Integer int0 = 0; Integer int1 = 1; assert int0.compareTo(int1) < 0; String str0 = "zero"; String str1 = "one"; assert str0.compareTo(str1) > 0; Integer i = 0; String s = "one"; assert i.compareTo(s) < 0; // compile-time error Number m = new Integer(2); Number n = new Double(3.14); assert m.compareTo(n) < 0; // compile-time error Maximum of a collection ======================= public static > T max(Collection coll) { T candidate = coll.iterator().next(); for (T elt : coll) \br{ if (candidate.compareTo(elt) < 0) candidate = elt; } return candidate; List ints = Arrays.asList(0,1,2); assert Collections.max(ints) == 2; List strs = Arrays.asList("zero","one","two"); assert Collections.max(strs).equals("zero"); List nums = Arrays.asList(0,1,2,3.14); assert Collections.max(nums) == 3,14; // compile-time error // polymorphism \Lambda T // bounded polymorphism \Lambda T <: C // F-bounded polymorphism \Lambda T <: F(T) > > T max(Collection coll) > T max(Collection coll) A Fruity Example ---------------- class Fruit {...} class Apple extends Fruit implements Comparable} {...} class Orange extends Fruit implements Comparable} {...} vs class Fruit implements Comparable {...} class Apple extends Fruit {...} class Orange extends Fruit {...} abstract class Fruit \br{ 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 { public Apple(int size) { super("Apple", size); } public int compareTo(Apple a) { return super.compareTo(a); } } class Orange extends Fruit implements Comparable { 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 apples = Arrays.asList(a1,a2); assert Collections.max(apples).equals(a2); List oranges = Arrays.asList(o3,o4); assert Collections.max(oranges).equals(o4); List mixed = Arrays.asList(a1,o3); assert Collections.max(mixed).equals(o3); // compile-time error } } abstract class Fruit implements Comparable { 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 apples = Arrays.asList(a1,a2); assert Collections.max(apples).equals(a2); // ok - thanks to List oranges = Arrays.asList(o3,o4); assert Collections.max(oranges).equals(o4); // ok - thanks to List mixed = Arrays.asList(a1,o3); assert Collections.max(mixed).equals(o3); // ok } }