Detailed explanation
Comparable<T> defines a type’s natural ordering through compareTo(T other). The result must be negative, zero, or positive when the current object is less than, equal in ordering to, or greater than the other object. It need not be exactly -1, 0, or 1; only its sign matters. A correct implementation establishes a coherent ordering across the supported objects. A poorly implemented method may compile successfully but provide contradictory answers, making algorithms and collections that rely on ordering behave incorrectly.
The contract has three important mathematical requirements. First, sign symmetry requires sign(a.compareTo(b)) to equal -sign(b.compareTo(a)); reversing the operands must reverse the sign. If one direction throws an exception, the reverse must also throw. Second, transitivity requires that if a is greater than b and b is greater than c, then a is greater than c. Third, if a.compareTo(b) is zero, comparisons of a and b against any third object c must have the same sign. These rules also imply that an object must compare as zero with itself. Unchanged comparison-relevant state should produce repeatable results.
In the supplied BadPoint example, x > o.x returns 1, but every other case returns 0. For points with x values 2 and 5, the first comparison returns zero while the reverse returns positive, violating sign symmetry. With a third point whose x is 3, those supposedly equivalent points also compare differently against it. The correct integer-field implementation is return Integer.compare(this.x, o.x);. Avoid returning this.x - o.x because integer overflow can reverse the intended sign. Keep ordering fields stable while objects are stored in sorted collections.
Consistency with equals means a.compareTo(b) == 0 exactly when a.equals(b) is true. This is strongly recommended, not mandatory for Comparable; BigDecimal is a standard documented exception. TreeSet and TreeMap use comparison results—not equals—to determine element or key equivalence. An inconsistent ordering can therefore treat unequal objects as duplicates, while a contract-breaking ordering can cause missed lookups or insertion-order-dependent results. Interviewers expect candidates to distinguish mandatory ordering rules from the equals recommendation and explain concrete collection consequences.
Key points to remember
- compareTo returns a negative, zero, or positive value; exact magnitudes do not matter.
- Reversing operands must reverse the comparison sign, with symmetric exception behaviour.
- Ordering must be transitive, and objects comparing as zero must compare identically in sign against any third object.
- Consistency with equals is strongly recommended but is not a mandatory Comparable requirement.
- Use Integer.compare for integer fields; sorted collections depend on valid, stable comparison results.
Common mistakes candidates make
- Returning only 0 or 1, incorrectly treating every smaller value as equal in ordering.
- Using subtraction for integer comparison, overlooking overflow that can violate the ordering contract.
- Claiming consistency with equals is mandatory, or assuming TreeSet uses equals to identify duplicates.
Likely follow-up questions
What happens when compareTo returns zero for objects that are not equal?
TreeSet treats them as the same element, and TreeMap treats them as the same key. This can satisfy Comparable’s ordering rules while conflicting with the equals-based contracts of Set or Map.
Why is BigDecimal an exception to consistency with equals?
new BigDecimal("1.0").compareTo(new BigDecimal("1.00")) returns zero because the numeric values match. equals returns false because it also considers scale, so these objects behave differently as keys in a TreeMap and a HashMap.
How would you test a compareTo implementation?
Test self-comparison, reversed pairs, transitive triples, and whether zero-comparing pairs have identical comparison signs against third objects. Include integer extremes to expose overflow, and test equals consistency if the class promises it.
Interview tip: State the sign, transitivity, and zero-equivalence rules, demonstrate the broken comparison with two values, then show Integer.compare and explain the TreeSet consequence.
