🔌 Interfaces & Abstraction · Intermediate

Comparable as an interface example in Java

Natural ordering via compareTo.

🧩 The mysterySort ["kiwi", "Banana", "apple"] in Java and "Banana" comes first, ahead of "apple". Bug? No — a contract called Comparable.

A built-in sense of order

Implement **Comparable<T> and your class gets a natural ordering** through compareTo. It returns a negative number if this comes first, zero if they tie, positive if this comes after. Collections.sort, TreeSet and TreeMap use it automatically.

class Player implements Comparable<Player> {
    int score;
    public int compareTo(Player o) {
        return Integer.compare(score, o.score);
    }
}
🔮 Predict it

Capital letters first?

String already implements Comparable. What prints?

List<String> w = new ArrayList<>(
    List.of("kiwi", "Banana", "apple"));
Collections.sort(w);
System.out.println(w);
  1. [apple, Banana, kiwi]
  2. [Banana, apple, kiwi]
  3. [kiwi, Banana, apple]
Show the answer

String's natural order compares character codes. Uppercase 'B' is 66, lowercase 'a' is 97 — so "Banana" sorts before "apple".

Flip the order

Want highest first? Swap the arguments: Integer.compare(o.rank, rank). Now a Card with rank 3 compared to one with rank 8 returns a positive number, so the 3 sorts *after* the 8 — descending order.

public int compareTo(Card o) {
    return Integer.compare(o.rank, rank);
}
⚠️ The trap

The subtraction shortcut

return price - o.price; looks clever but overflows for huge or very negative values: Integer.MIN_VALUE - 1 wraps to a large positive number and flips the order. Always use **Integer.compare(a, b)** — it can't overflow.

public int compareTo(Item o) {
    return price - o.price;   // risky!
}
🔮 Predict it

A TreeSet of strangers

Gem doesn't implement Comparable. What happens?

class Gem { }
void main() {
    var set = new TreeSet<Gem>();
    set.add(new Gem());
}
  1. Adds the gem fine
  2. Throws ClassCastException
  3. Compile error
Show the answer

It compiles — TreeSet<Gem> accepts any type. But a TreeSet needs an order: with no Comparator it casts elements to Comparable, and that fails at runtime, even on the very first add.

💼 In the real world

Ordering in real apps

Leaderboards, orders by date, prices: String, Integer and LocalDate already implement Comparable, so they sort out of the box. Try to keep compareTo **consistent with equals** — otherwise a TreeSet and a HashSet can disagree about which items are duplicates.

Key takeaways

  1. int compareTo(T other): negative / zero / positive
  2. String, Integer and LocalDate already implement it
  3. Use Integer.compare(a, b), not a - b, to avoid overflow
  4. Keep compareTo consistent with equals where possible
🤯 Did you know?

new BigDecimal("2.0").equals(new BigDecimal("2.00")) is false, but compareTo returns 0. A HashSet keeps both values; a TreeSet keeps only one.

Practice questions

What does this print?

List<String> words =
    new ArrayList<>(List.of("pear", "Apple", "fig"));
Collections.sort(words);
System.out.println(words);
  1. [Apple, fig, pear]
  2. [fig, pear, Apple]
  3. [pear, Apple, fig]
  4. [Apple, pear, fig]
Check your answer

[Apple, fig, pear]. String's natural order compares character codes. 'A' (65) is smaller than 'f' (102) and 'p' (112).

What does this print?

class Card implements Comparable<Card> {
    int rank;
    Card(int rank) { this.rank = rank; }
    public int compareTo(Card o) {
        return Integer.compare(o.rank, rank);
    }
}
void main() {
    Card a = new Card(3), b = new Card(8);
    System.out.println(a.compareTo(b) > 0);
}
  1. true
  2. false
  3. Compile error
  4. Throws ClassCastException
Check your answer

true. The arguments are swapped (o.rank first), which reverses the order: Integer.compare(8, 3) is positive, so the 3 sorts after the 8.

Next: what if your interface had a guest list — only these three classes may implement it, nobody else?