Suppose we will write a method max that will take an array of objects and return the maximum one.
public static Object max(Object[] items) {
int maxDex = 0;
for (int i = 0; i < items.length; i += 1) {
if (items[i] > items[maxDex]) {
maxDex = i;
}
}
return items[maxDex];
}
public static void main(String[] args) {
Dog[] dogs = {new Dog("Elyse", 3), new Dog("Sture", 9), new Dog("Benjamin", 15)};
Dog maxDog = (Dog) max(dogs);
maxDog.bark();
}
The problem is that the Dog object can't work with the > operater. To fix this problem, we may change the function slightly.
public static Dog maxDog(Dog[] dogs) {
if (dogs == null || dogs.length == 0) {
return null;
}
Dog maxDog = dogs[0];
for (Dog d : dogs) {
if (d.size > maxDog.size) {
maxDog = d;
}
}
return maxDog;
}
However, another problem is that we couldn't generalize this function to other type of objects.
compareTo
We can create an interface that guarantees that any implementing class, like Dog, contains a comparison method, which we'll call compareTo.
Let's write our own interface.
public interface OurComparable {
public int compareTo(Object o);
}
Return a negative number if this < o.
Return 0 if this equals o.
Return a positive number if this > o.
Now, we could let our Dog class implements the OurComparable interface.
public class Dog implements OurComparable {
private String name;
private int size;
public Dog(String n, int s) {
name = n;
size = s;
}
public void bark() {
System.out.println(name + " says: bark");
}
public int compareTo(Object o) {
Dog uddaDog = (Dog) o;
return this.size - uddaDog.size;
}
}
Then, we could generalize the max function by taking in OurComparable objects.
public static OurComparable max(OurComparable[] items) {
int maxDex = 0;
for (int i = 0; i < items.length; i += 1) {
int cmp = items[i].compareTo(items[maxDex]);
if (cmp > 0) {
maxDex = i;
}
}
return items[maxDex];
}
Comparables
Although OurComparable interface seems solved the issue, it's awkward to use and there's no existing classes implement OurComparable. The solution is that we could use the existed interface Comparable.
public interface Comparable<T> {
public int compareTo(T obj);
}
Notice that Comparable<T> means that it takes a generic type. This will help us avoid having to cast an object to a specific type.
public class Dog implements Comparable<Dog> {
...
public int compareTo(Dog uddaDog) {
return this.size - uddaDog.size;
}
}
Comparator
We could only implement one compareTo method for each class. However, if we want to add more orders of comparasion, we could implement Comparator interface.
public interface Comparator<T> {
int compare(T o1, T o2);
}
This shows that the Comparator interface requires that any implementing class implements the compare method.
To compare two dogs based on their names, we can simply defer to String's already defined compareTo method.
import java.util.Comparator;
public class Dog implements Comparable<Dog> {
...
public int compareTo(Dog uddaDog) {
return this.size - uddaDog.size;
}
private static class NameComparator implements Comparator<Dog> {
public int compare(Dog a, Dog b) {
return a.name.compareTo(b.name);
}
}
public static Comparator<Dog> getNameComparator() {
return new NameComparator();
}
}