Clean compareTo methods with Google Guava
A common task in object oriented programming is comparing two objects for the purpose of sorting. In Java the useful Comparable<T>
Interface exists. I’ve found myself implementing the compareTo
method from this Interface plenty of times. But there is something that bothers me everytime: the complexity of the code this implementation creates. Let me explain.
One of my goals is to always write easily readable code. But this is really hard when implementing a compareTo
method for an object with more than two fields. Let’s take a look at an example.
public class Fruit implements Comparable {
private String name;
private String family;
private int calories;
@Override
public int compareTo( Fruit otherFruit ) {
int result = name.compareTo( otherFruit.name );
if( result == 0 ) {
result = family.compareTo( otherFruit.family );
if( result == 0 ) {
result = Ints.compare( calories, otherFruit.calories );
}
}
return result;
}
}
As you can see we have a class called Fruit
with three fields. In its compareTo
method you can see that all three fields are used for comparison. I think this is a pretty common solution. The downside of this implementation is that every field can influence the compare result. If one field doesn’t have the same value as its counterpart Fruit
’s field, the whole Fruit
is not the same. This means that after every comparison of a field you need to check the result. This becomes a major drawback when the class evolves. With every new or removed field the compareTo
method has to change. In general this is manageable, but the implementation of this method becomes uglier and uglier and transforms into a monster over time. In these cases Google Guava provides a solution called ComparisonChain
.
Basically a ComparisonChain
is just a util that provides a fluent API to write clean compareTo
methods. And, as you might agree, clean means readable and maintanable. Let’s convert the example above using Guavas ComparisonChain
.
public class Fruit implements Comparable {
private String name;
private String family;
private int calories;
@Override
public int compareTo( Fruit otherFruit ) {
return ComparisonChain.start()
.compare( name, otherFruit.name )
.compare( family, otherFruit.family )
.compare( calories, otherFruit.calories )
.result();
}
}
The code that performs the checking for the result is completely gone. It’s done for us now by Guava’s implementation. In addition to the nice Interface another cool thing about ComparisionChain
’s is that they compare lazily. This means that values will only be compared if the previous comparison was zero. From my point of view the result of using this is much more readable code. As always, feel free to disagree in a comment. ;)