Sometimes we need to get objects in order. For example showing them in a list or processing them in a certain order requires that we first specify criteria by which objects are ordered.
Java traditionally offers two ways to do this: the Comparable interface and separate Comparator classes. We argue that Comparable is a bad design idea in Java and should never be implemented.
Consider the following class definition of a User entity:
Here we implemented Comparable, implying that users are “naturally” sorted by name. This seems to make sense as long as only this order is important. However, User::compareTo describes a relation between users, not a property of a single user. There will be different compareTo relations down the road.
What if, for example, another feature requires that we show users sorted by registrationDate? We cannot just implement Comparable twice, so now we have to implement a Comparator for registrationDate. But why would we treat the implementations of the two orders differently? To maintain consistency we have to move the comparison by name to its own Comparator.
Also consider this client code: does Collections.sort(users) sort by name or registrationDate? The order is important for the context in which the users collection is used. Implementing Comparable hides that information away in the User class. Using a Comparator explicitly would look like this:
To Collections.sort(), a Comparator is a strategy class that tells it how to order things. Keeping the order relation separated is just good design and leads to more readable code. Now it is easy to implement other comparisons:
Here we made use of the new Java 8 default method Collection::sort, which allows us to simply write users.sort(...). Interestingly, it has a mandatory parameter of type Comparator. It doesn’t even give us the choice to use Comparable! It is difficult to tell if this was a conscious design decision on part of the Java language designers. We think it’s a good decision nonetheless.