How do you write a generic function that refuses nullable type arguments, and what exactly does <T : Any> guarantee at compile time?
answer
- <T : Any> = non-null top type
- String? not a subtype of Any
- Body value of type T is non-null
- T? re-adds nullability per parameter
- Compile-time only, not runtime check
basics
~10 sAdd 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.
solid answer
~40 sDeclaring `<T : Any>` sets the upper bound to `Any`, the non-null top type. Because a nullable type such as `String?` is NOT a subtype of `Any` (it is a subtype of `Any?`), the compiler rejects any attempt to instantiate `T` with a nullable type. Inside the body, a parameter of type `T` is therefore non-null — no `?.`, `!!`, or null check needed. This is the idiomatic way to express 'works for any non-null value'. Note the subtlety: even with `<T : Any>`, declaring a parameter as `T?` re-introduces nullability for that specific parameter. Standard library examples include `checkNotNull`/`requireNotNull` returning `T : Any`, and `let`-style helpers. The guarantee is purely static: it constrains the call site's type argument, not runtime data validation.
code
kotlin · 4 linesfun <T : Any> unwrapAll(items: List<T?>): List<T> = items.filterNotNull()
val clean: List<String> = unwrapAll(listOf("a", null, "b"))
// T = String (non-null); list elements typed T? at the call boundarygo deeper
Recognizes that <T : Any> blocks null but may not articulate the Any vs Any? subtyping.
Explains that String? is not a subtype of Any and that a T value is then non-null in the body.
Knows the T? re-nullability subtlety and maps it to stdlib helpers like requireNotNull.
Reasons about API contracts: when to expose <T : Any> vs <T> in a public library and the platform-type interactions from Java interop.
## The goal You want a generic that accepts *any* type but never a nullable one. The tool is the **upper bound** `<T : Any>`. ## Why `Any` forbids null Kotlin's type hierarchy has two top types: - `Any?` — the nullable top type; *every* type, nullable or not, is a subtype. - `Any` — the **non-null** top type; only non-null types are subtypes. `String?` is a subtype of `Any?` but **not** of `Any`. So when the bound is `Any`, the compiler rejects `f<String?>(...)`: ```kotlin fun <T : Any> store(value: T): T = value store("hi") // OK, T = String store(42) // OK, T = Int // store<String?>(null) // ERROR: String? is not a subtype of Any ``` ## What you gain in the body With `<T : Any>`, a parameter declared as `value: T` is statically non-null. You can call its members directly without `?.` or `!!`: ```kotlin fun <T : Any> describe(value: T): String = value.toString() // no null check needed ``` ## The `T?` subtlety The bound constrains what `T` itself can be, but you can still *opt back into* nullability by writing `T?`: ```kotlin fun <T : Any> firstOrNull(items: List<T>): T? = if (items.isEmpty()) null else items[0] ``` Here `T` is non-null (no null elements), yet the return type `T?` allows the function to return `null`. ## Standard-library parallels `requireNotNull(value: T?): T` and `checkNotNull` take a nullable input and return `T` (a non-null type) — they rely on `T : Any` semantics to express 'the result is non-null'. ## Guarantee scope This is a **compile-time** constraint on the type argument. It is not runtime validation: nothing scans data for nulls; the compiler simply refuses nullable type arguments and lets you treat `T` values as non-null.
- With <T : Any>, can a parameter still legitimately be nullable?Yes — write the parameter type as T?. The bound restricts T itself to non-null, but T? explicitly re-introduces nullability for that position.
- Does <T : Any> do any runtime null validation on the arguments?No. It is a purely static constraint on the type argument; it prevents nullable type arguments at compile time but does not insert runtime null checks.
<T : Any> is a bouncer who turns away 'nobody' (null) at the door; once inside, you never have to ask 'is anyone there?'.
saying these in an interview costs you the question
- Claiming String? is a subtype of Any
- Saying <T : Any> inserts runtime null checks
- Believing you still need ?. on a T value when bound is Any
- Confusing <T : Any> with <T : Any?>
- Thinking the bound prevents T? parameter declarations