What is the default upper bound of a Kotlin type parameter like <T> when you don't specify one, and why does this matter?
answer
- No bound = implicit Any?
- Any? is the top type, includes null
- Any (no ?) forbids nulls
- Bound limits callable members
- <T> is maximally permissive
basics
~10 sIf 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 sAn 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 linesfun <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? !<: Anygo deeper
Knows that omitting a bound means Any? and that null is therefore allowed.
Explains the difference between Any? and Any and how the bound limits callable members.
Frames the bound as a compile-time contract affecting both call sites and the implementation's available API surface.
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)