Solution: A Second Order

Comparable gave Card one order, and that order is built into the type. Now suppose you want to group a hand by suit instead, so all the clubs are grouped together, then the diamonds, and so on. That is a second order, and Card has already used its one compareTo for rank. So this order has to come from outside, as a Comparator.

Solution

A Comparator<Card> is a separate class with one method, compare(a, b), that returns the same negative / zero / positive answer compareTo would return. The difference is that the rule is defined outside Card.

public class SuitComparator implements Comparator<Card> {

  @Override
  public int compare(Card a, Card b) {
    if (a.getSuit() != b.getSuit()) {
      return Character.compare(a.getSuit(), b.getSuit());
    }
    return Integer.compare(a.getRank(), b.getRank());
  }
}

Notice that SuitComparator reads the cards only through their public getters. It does not depend on Card’s internals, and Card never mentions SuitComparator. You can write a separate comparator for each order you want, and Card stays as it is.

The comparator falls back to rank when the two suits match. Without that fallback, every club would compare equal to every other club, and a sort would group the suits correctly but leave the cards inside each suit in the order they already had.

So use Comparable for the one order that is built into the type. Use a Comparator for any other order you want, including an order on a type whose source code you cannot change.