In Kotlin, what is the default upper bound of an unbounded type parameter like <T>, and what does that imply about whether T can hold null?
answer
- Unbounded <T> => upper bound Any?
- Any? = Any + null
- <T : Any> forbids null
- T inside body may be null
- Caller can pass Box<String?>
basics
~10 sAn unbounded <T> defaults to the upper bound Any?, which includes null. So T can hold a null value unless you restrict it.
solid answer
~40 sWhen you write a generic like class Box<T> or fun <T> id(x: T): T, the type parameter T has no explicit upper bound, so Kotlin uses the implicit upper bound Any? (the type at the top of the nullable hierarchy). Because Any? includes null, T is implicitly nullable: a caller can supply Box<String?> or call id(null), and inside the generic code a value of type T may be null. This is why you cannot safely call members like x.length on a T without a null check or a smart-cast. To forbid nulls you constrain the parameter with <T : Any>, making the upper bound the non-null Any. The distinction matters because T (the parameter itself) and T? (an explicitly nullable use of T) behave differently in generic code.
code
kotlin · 6 linesclass Box<T>(val value: T) // T : Any? implicitly
val nullable = Box<String?>(null) // legal
fun <T : Any> nonNullBox(v: T) = Box(v)
nonNullBox("x") // OK
// nonNullBox(null) // compile errorgo deeper
Knows unbounded <T> defaults to Any? and that null is therefore allowed.
Can show how <T : Any> forbids null and changes body assumptions, with a code example.
Explains why the compiler treats T as possibly-null and the interaction with smart-casts and member access.
Frames the default Any? bound as a deliberate language design choice and discusses API design implications for library type parameters.
## The implicit upper bound Every generic type parameter in Kotlin has an **upper bound** — the most general type it is allowed to be. When you declare a parameter without a bound, like `class Box<T>` or `fun <T> identity(x: T): T`, Kotlin fills in the implicit upper bound `Any?`. `Any?` is the root of Kotlin's *nullable* type hierarchy — it is `Any` (the non-null root) plus `null`. Because the bound is `Any?`, **a caller may substitute a nullable type for `T`**, and `null` itself is a legal value of `T`. ```kotlin class Box<T>(val value: T) val a = Box("hi") // T = String val b = Box<String?>(null) // T = String?, value is null — legal ``` ## Why T is treated as possibly-null inside the body Inside generic code, the compiler must assume `T` could be a nullable type, so a value of type `T` is treated as possibly null. You cannot dereference it without handling null: ```kotlin fun <T> firstChar(x: T): Char { // return x.length // ERROR: x may be null, and T isn't even necessarily String return x.toString().first() // toString() is on Any?, returns "null" for null } ``` Even the safe-call operator `?.` is meaningful here because `x` of type `T` might be null. ## Forbidding null with `<T : Any>` To say "T must be a non-null type," add the explicit bound `Any`: ```kotlin fun <T : Any> requireNonNull(x: T): T = x requireNonNull("hi") // OK, T = String // requireNonNull(null) // ERROR: null does not satisfy T : Any ``` Now callers cannot pass `Box<String?>` and cannot pass a literal `null`; inside the body `x` is known non-null. ## Key takeaways - Unbounded `<T>` == `<T : Any?>` == possibly nullable. - `<T : Any>` forbids null and makes `T` non-null inside the body. - This is purely about the *parameter*; an explicit `T?` is a separate, always-nullable use discussed elsewhere.
- What does <T : Any> change inside the function body?T is known to be non-null, so values of type T can be dereferenced without a null check and smart-casts treat them as non-null.
- Is <T> the same as <T : Any?>?Yes — they are equivalent; the explicit Any? bound just spells out the default.
An unbounded <T> is like a job posting with no requirements — 'null' applicants are welcome until you add 'must be non-null' (: Any).
saying these in an interview costs you the question
- Claiming unbounded <T> defaults to Any (non-null)
- Saying T can never be null without a bound
- Confusing the bound Any? with the wildcard/star projection
- Thinking <T : Any> changes the runtime type erasure behavior