skip to content

What is the Comparable interface in Kotlin, and what does implementing compareTo give you?

level: juniorimportance: must knowfreq 70%

answer

  1. compareTo returns negative / zero / positive by sign
  2. operator fun -> enables < > <= >= and ..
  3. Natural order = one default order baked into the type
  4. Use compareValuesBy to avoid overflow-prone subtraction
  5. Comparable = built-in, Comparator = external

basics

~10 s

Comparable means a type has a built-in natural order. You write compareTo, which returns a negative number, zero, or positive number. Then you can sort the items and use < and > on them.

solid answer

~40 s

Comparable<T> is the standard library interface that defines a type's natural ordering via a single method, compareTo(other: T): Int. It must return a negative Int when this is less than other, 0 when equal in order, and positive when greater. Implementing it unlocks Kotlin's comparison operators (<, >, <=, >=) and the range operator (..), because the compiler desugars a < b to a.compareTo(b) < 0. It also lets you call sorted(), sortedDescending(), min(), max(), and use the value in a sorted collection like TreeSet or a sorted map. Many built-in types (Int, String, BigDecimal, LocalDate) already implement Comparable, so they sort and compare out of the box.

code

kotlin · 8 lines
kotlin
data class Money(val cents: Long) : Comparable<Money> {
    override fun compareTo(other: Money): Int = cents.compareTo(other.cents)
}

val prices = listOf(Money(500), Money(150), Money(999))
println(prices.sorted())       // ascending by cents
println(prices.max())          // Money(999)
println(Money(150) < Money(500)) // true

go deeper

for a junior

Knows Comparable means natural order and compareTo returns negative/zero/positive enabling sorting and < >.

for a middle

Explains the operator desugaring (a < b -> a.compareTo(b) < 0) and uses compareValuesBy instead of unsafe subtraction.

for a senior

Discusses the compareTo/equals consistency contract and its impact on TreeSet/TreeMap membership.

for a principal

Frames natural order as an API design choice: when a type should expose one canonical order vs leaving ordering to callers via Comparator.

## What Comparable is `Comparable<T>` is a Kotlin standard-library interface with exactly one method: ```kotlin public interface Comparable<in T> { public operator fun compareTo(other: T): Int } ``` Implementing it declares that your type has a **natural ordering** — a single, default way to rank instances. ## The compareTo contract `compareTo` returns an `Int` interpreted by **sign**, not magnitude: - **negative** -> `this` comes before `other` (this < other) - **zero** -> they are equal in ordering - **positive** -> `this` comes after `other` (this > other) The contract requires it to be: *consistent* (a < b implies b > a), *transitive* (a < b and b < c implies a < c), and ideally *consistent with equals* (compareTo == 0 should agree with `equals`). Returning `0` while `equals` says unequal is legal but causes surprises in `TreeSet`/`TreeMap`, which use compareTo, not equals, to decide membership. ## What you unlock Because `compareTo` is marked `operator`, the compiler rewrites comparison operators: - `a < b` becomes `a.compareTo(b) < 0` - `a >= b` becomes `a.compareTo(b) >= 0` - the range operator `a..b` and `in` checks on ranges also use it You also get `sorted()`, `sortedDescending()`, `min()`, `max()`, `coerceIn`, and sorted containers like `sortedSetOf` / `TreeSet`. ## Example ```kotlin data class Version(val major: Int, val minor: Int) : Comparable<Version> { override fun compareTo(other: Version): Int = compareValuesBy(this, other, { it.major }, { it.minor }) } val a = Version(1, 2) val b = Version(1, 5) println(a < b) // true -> a.compareTo(b) < 0 println(listOf(b, a).sorted()) // [Version(1,2), Version(1,5)] ``` `compareValuesBy` is a stdlib helper that compares by each selector in turn — a clean way to avoid hand-rolled subtraction (which can overflow for raw Int math). ## Comparable vs Comparator `Comparable` is the *one* natural order baked into the type. A `Comparator` is an *external*, swappable ordering you supply at the call site (e.g. `sortedWith(comparator)`). Use Comparable for the obvious default; use Comparator when you need alternative orderings.

  • Why prefer cents.compareTo(other.cents) over returning (cents - other.cents).toInt()?
    Subtraction can overflow Int/Long for far-apart values, giving the wrong sign. compareTo (or compareValuesBy) is overflow-safe.
  • Does implementing Comparable affect equals/hashCode?
    No, they are independent. But for TreeSet/TreeMap, compareTo (not equals) decides equality, so keep them consistent to avoid surprises.

Comparable is like the height marked on each person; once everyone has it, you can line them up shortest to tallest automatically.

saying these in an interview costs you the question

  • Saying compareTo returns true/false instead of an Int
  • Returning a - b and ignoring integer overflow
  • Claiming the magnitude (not the sign) of the result matters
  • Confusing Comparable (one natural order) with Comparator (external order)
  • Thinking you must override < and > directly instead of compareTo

context