skip to content

Upper Bounds

An upper bound restricts what a type parameter may be, as in <T : Comparable<T>>, and the implicit bound is Any?. The trick worth remembering is that <T : Any> is how you forbid nullable type arguments.

part ofKotlinoverview, primer and where to startread it →
on this pageshow

questions

5

What is the default upper bound of a Kotlin type parameter like <T> when you don't specify one, and why does this matter?

level: juniorimportance: must knowfreq 70%

answer

  1. No bound = implicit Any?
  2. Any? is the top type, includes null
  3. Any (no ?) forbids nulls
  4. Bound limits callable members
  5. <T> is maximally permissive

basics

~10 s

If you write <T> with no bound, T can be any type including a nullable one. The hidden default is Any?, so values of T may be null.

solid answer

~40 s

An unconstrained type parameter <T> has the implicit upper bound Any? — the top type that includes every Kotlin type and allows null. So `fun <T> firstOrNull(list: List<T>): T?` may receive String?, Int?, etc., and inside the function you cannot assume T is non-null. To forbid nulls you must declare <T : Any>, whose bound is the non-null top type Any. The bound limits what arguments callers may pass for T and what operations you may perform on a value of type T (only members of the bound). Because Any? is the loosest possible bound, <T> is maximally permissive; tightening it with `<T : SomeType>` documents and enforces a contract at compile time.

code

kotlin · 5 lines
kotlin
fun <T> echo(x: T): T = x          // T : Any? — null allowed
fun <T : Any> echoNotNull(x: T): T = x

val a = echo<String?>(null)        // compiles, a: String?
// val b = echoNotNull<String?>(null) // ERROR: String? !<: Any

go deeper

for a junior

Knows that omitting a bound means Any? and that null is therefore allowed.

for a middle

Explains the difference between Any? and Any and how the bound limits callable members.

for a senior

Frames the bound as a compile-time contract affecting both call sites and the implementation's available API surface.

for a principal

Discusses how Any? as default interacts with platform types, null-safety guarantees, and API design defaults for libraries.

## What an upper bound is A generic **type parameter** (like `T`) is a placeholder a caller fills with a real type. An **upper bound** restricts which types are allowed: `T` must be that bound or a subtype of it. The syntax is `<T : Bound>`. ## The implicit `Any?` bound If you omit the bound, Kotlin supplies one automatically: ```kotlin fun <T> identity(x: T): T = x // same as fun <T : Any?> identity(x: T): T ``` `Any?` is the **top type** of the Kotlin type hierarchy — every type is a subtype of it, *including* nullable types like `String?`. So with a bare `<T>`: - callers may substitute `String`, `Int`, `String?`, `List<Foo>?`, anything; - inside the function the value might be `null`, so you cannot safely call members without a null check (`?.`, `!!`, or a smart cast). ## Why it matters The bound controls two things: 1. **What callers may pass** for `T`. 2. **What you may do** with a value of type `T` — only the members declared on the bound are available. With `Any?` you essentially only have `toString()`, `equals()`, `hashCode()` (the members of `Any`) plus null-safe access. ## Forbidding null: `<T : Any>` `Any` (without `?`) is the **non-null top type**. Declaring `<T : Any>` means `T` cannot be a nullable type: ```kotlin fun <T : Any> requireValue(x: T): T = x requireValue("hi") // OK // requireValue<String?>(null) // compile error: String? is not a subtype of Any ``` ## Summary - Bare `<T>` ⇒ implicit `<T : Any?>` ⇒ nullable allowed. - `<T : Any>` ⇒ non-null only. - A custom bound like `<T : Number>` allows only `Number` subtypes and exposes `Number`'s members.

  • If T is bounded by Any?, what members can you call on a value of type T without extra work?
    Effectively the members of Any (toString, equals, hashCode) but only through null-safe calls, because the value may be null; any direct member access requires a null check or smart cast.
  • Does <T> and <T : Any?> compile to anything different?
    No — they are identical. <T> is just shorthand; the compiler fills in Any? as the bound.

An unbounded <T> is like a parking spot with no size limit — any vehicle fits, even an empty space (null); <T : Any> posts a 'no empty spaces' sign.

saying these in an interview costs you the question

  • Saying the default bound is Any (non-null) instead of Any?
  • Claiming <T> cannot hold null
  • Thinking the default bound is Object/Comparable
  • Believing an unbounded T exposes arbitrary members like length
  • Confusing upper bound with variance (in/out)

context

open as a page

How do you write a generic function that refuses nullable type arguments, and what exactly does <T : Any> guarantee at compile time?

level: middleimportance: must knowfreq 60%

basics

~10 s

Add the bound <T : Any>. Then callers cannot use a nullable type like String? for T, so the value is guaranteed non-null and you can use it without null checks.

open as a page

How does the upper bound of a type parameter affect which members and operations you can call on a value of that type inside the function body?

level: middleimportance: should knowfreq 40%

basics

~10 s

Inside the function, a value of type T only exposes the members of its upper bound. With no bound (Any?) you get almost nothing; with <T : Number> you get Number's methods like toInt().

open as a page

Why is the bound written as <T : Comparable<T>> in functions like a generic max(), and what does this self-referential bound enforce?

level: seniorimportance: should knowfreq 45%

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.

open as a page

When designing a public generic API, how do you decide between leaving a type parameter unbounded (<T>), bounding to <T : Any>, or bounding to a specific type, and what are the compatibility consequences of changing it later?

level: principalimportance: nice to knowfreq 25%

basics

~20 s

Pick the loosest bound that still lets your code work. Use <T> when null is fine, <T : Any> to ban null, a specific bound when you need that type's methods. Tightening a bound later can break callers.

open as a page