Why is the bound written as <T : Comparable<T>> in functions like a generic max(), and what does this self-referential bound enforce?
answer
- Recursive / F-bounded: T inside its own bound
- Requires Comparable<T> => compareTo(T)
- >= desugars to compareTo >= 0
- Self-ref = comparable to its OWN type
- maxOf/minOf/sorted use it
basics
~10 s<T : Comparable<T>> means T must be comparable to its own type. It lets you call a.compareTo(b) on two T values, which is what max/min need to order them.
solid answer
~40 s`<T : Comparable<T>>` is a **recursive (F-bounded) generic** constraint: the type parameter `T` appears inside its own bound. It requires that `T` implements `Comparable<T>`, i.e. that any `T` can be compared to another `T` via `compareTo`. This is exactly what ordering functions need: `fun <T : Comparable<T>> maxOf(a: T, b: T): T = if (a >= b) a else b` — the `>=` desugars to `a.compareTo(b) >= 0`, available only because the bound supplies `Comparable<T>`. Without the bound, `T`'s only members come from `Any?`, and `compareTo` would not type-check. The self-reference ensures you compare a `T` against the *same* `T`, not some unrelated `Comparable<Other>`. Standard library `kotlin.comparisons.maxOf`, `minOf`, and `sorted()` on `Iterable<T : Comparable<T>>` use this pattern.
code
kotlin · 9 linesfun <T : Comparable<T>> clamp(value: T, low: T, high: T): T =
when {
value < low -> low
value > high -> high
else -> value
}
clamp(5, 1, 10) // Int : Comparable<Int>
clamp("m", "a", "z") // String : Comparable<String>go deeper
Recognizes the bound is needed so values can be compared with </>.
Explains that Comparable<T> supplies compareTo and that operators desugar to it.
Names the F-bounded/recursive pattern and explains why self-reference (vs Comparable<*>) is required.
Discusses Comparable's contravariance, comparison to a Comparator-based API, and design trade-offs for ordering generics in a library.
## The pattern ```kotlin fun <T : Comparable<T>> maxOf(a: T, b: T): T = if (a >= b) a else b ``` The bound `T : Comparable<T>` is **recursive** — `T` appears both as the parameter being bounded and as the type argument of `Comparable`. This is the **F-bounded polymorphism** idiom. ## What `Comparable<T>` provides `Comparable<in T>` declares a single member: ```kotlin operator fun compareTo(other: T): Int ``` It returns a negative number, zero, or a positive number when the receiver is less than, equal to, or greater than `other`. Kotlin's comparison operators `<`, `<=`, `>`, `>=` desugar to `compareTo` calls. So `a >= b` compiles only if `a` has a `compareTo(b)` — which the bound guarantees. ## Why self-referential? If you wrote merely `<T : Comparable<*>>`, you would know `T` is comparable to *something*, but not to another `T`. The function `maxOf(a: T, b: T)` must compare `a` to `b`, both of type `T`. Tying the type argument of `Comparable` back to `T` enforces 'comparable to its own type': ```kotlin class Version(val n: Int) : Comparable<Version> { override fun compareTo(other: Version) = n - other.n } maxOf(Version(1), Version(2)) // T = Version, satisfies Comparable<Version> ``` ## Without the bound it fails ```kotlin fun <T> brokenMax(a: T, b: T): T = if (a >= b) a else b // ERROR: >= unavailable on Any? ``` Because an unbounded `T` only has `Any?`'s members, `>=`/`compareTo` don't exist. ## Nullability `Comparable<T>` is itself non-null-friendly: declaring `<T : Comparable<T>>` does *not* by itself forbid `T?`, but `Comparable<T>` is the bound, so `T` must be a non-null type implementing it (a nullable type can't implement an interface). In practice these helpers operate on non-null `T`. ## Standard library `maxOf`, `minOf`, `coerceIn`, `sorted()` (`Iterable<T>.sorted(): List<T>` where `T : Comparable<T>`), and `TreeSet`-backed `sortedSetOf` all rely on this exact bound. ## Variance note `Comparable` is declared `Comparable<in T>` (contravariant). That contravariance lets a `Comparable<Number>` satisfy a need for `Comparable<Int>`, which broadens what types fit the recursive bound, but the core requirement — comparing a `T` to a `T` — remains.
- What does the operator a >= b actually compile to under this bound?a.compareTo(b) >= 0. The bound Comparable<T> supplies compareTo, and Kotlin desugars relational operators into compareTo comparisons against 0.
- Why not just use <T : Comparable<*>>?Comparable<*> only guarantees T compares to some unknown type, so you couldn't safely compare two T values to each other; the self-reference Comparable<T> ties it to the same type.
It's like requiring 'must be able to race against its own twin' — not just 'can race someone', but specifically race another of the exact same kind.
saying these in an interview costs you the question
- Calling compareTo on an unbounded <T> and expecting it to compile
- Thinking Comparable<*> is equivalent to Comparable<T>
- Not knowing >= desugars to compareTo
- Claiming the bound is needed for equals/hashCode rather than ordering
- Confusing Comparable<T> (natural ordering) with a Comparator<T> parameter