skip to content

Definitely Non-Null Types

T & Any strips nullability from a type parameter whose bound is already nullable. It exists mainly for the Java interop boundary, where you need to override a method whose signature is annotated @Nullable but your implementation never returns null.

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

questions

5

Show how `T & Any` is used to override a Java generic method annotated `@NotNull`. Why is it needed there?

level: middleimportance: must knowfreq 30%

answer

  1. Java type variable -> Kotlin param with Any? bound -> bare T is nullable
  2. @NotNull return can't be expressed by bare T in override
  3. override fun f(x: T?): T & Any
  4. Removes need for platform type or unchecked cast
  5. Only needed at the interop/override boundary

basics

~20 s

When a Java method returns a generic value marked @NotNull, Kotlin couldn't express that the return is non-null in an override. T & Any lets the Kotlin override say 'this return is definitely not null'.

solid answer

~40 s

Consider a Java interface `interface Box<T> { @NotNull T getOrDefault(@Nullable T value); }`. The type variable `T` in Java carries no nullability of its own, but `@NotNull` annotates the *return*. When you override this in Kotlin, the Kotlin parameter `T` has a nullable upper bound (Java type variables map to `Any?`-bounded parameters), so you cannot write a bare `T` return that is non-null. Before Kotlin 1.7 the override could only use a platform/nullable type, losing the `@NotNull` guarantee. With definitely-non-null types you write `override fun getOrDefault(value: T?): T & Any`, faithfully reproducing the Java contract: argument nullable, return non-null. This keeps the Kotlin side null-safe and avoids spurious `!!` at call sites. The `& Any` only appears at the override boundary; pure-Kotlin code with proper `T : Any` bounds usually doesn't need it.

code

kotlin · 11 lines
kotlin
// Java: interface Transformer<T> { @NotNull T transform(@Nullable T input); }

class Identity<T> : Transformer<T> {
    override fun transform(input: T?): T & Any =
        input ?: error("null not allowed")
}

fun main() {
    val r: String = Identity<String>().transform("hi") // precise non-null, no !!
    println(r)
}

go deeper

for a junior

Recognizes that Java @NotNull generics are awkward to override and that T & Any helps.

for a middle

Writes a correct override with T? argument and T & Any return and explains the mapping.

for a senior

Articulates the platform-type/unchecked-cast alternatives it replaces and why null safety improves.

for a principal

Discusses how this interacts with JVM erasure/bridge methods and broader interop nullability annotation handling (JSpecify, strict mode).

## Why Java interop needs this Java has no built-in nullability in its type system; tools express it with **annotations** like `@NotNull` / `@Nullable` (from JetBrains, JSpecify, etc.). Kotlin reads these annotations to refine Java types when calling Java code. The tricky case is a **generic type variable** used as a return type with a nullability annotation: ```java // Java public interface Transformer<T> { @NotNull T transform(@Nullable T input); } ``` Kotlin maps a Java type parameter `T` to a parameter with a **nullable upper bound** (`Any?`), because Java could pass either `String` or `String?`. So in a Kotlin override a bare `T` is potentially nullable, and you cannot directly declare the **non-null** return that `@NotNull` promises. ## The fix: `T & Any` at the override ```kotlin class UpperCaser : Transformer<String> { override fun transform(input: String?): String & Any = input?.uppercase() ?: "" } // Generic override keeping T abstract: class Identity<T> : Transformer<T> { override fun transform(input: T?): T & Any = input ?: error("null not allowed") } ``` The override declares `input: T?` (matching `@Nullable`) and `T & Any` for the return (matching `@NotNull`). Now Kotlin callers get a precise non-null result type — no platform type, no `!!`. ## What the compiler enforces - The override's return must be assignable to a non-null value; returning a possibly-null expression without an elvis (`?:`) or check is a compile error, because the declared type is non-null. - The signature must remain a valid override of the Java method (the JVM erases generics, so the bridge methods line up). ## Without `T & Any` (the old pain) Pre-1.7 you'd either inherit a **platform type** (`T!`) — silently unsafe — or wrap with `@Suppress("UNCHECKED_CAST")` and an unchecked cast `as T`. Definitely-non-null types remove both hacks. ## Key APIs/keywords here - `@NotNull` / `@Nullable` — Java nullability annotations Kotlin honors. - platform type `T!` — Java type with unknown nullability. - `?:` (elvis) — supplies the non-null fallback so the body satisfies the `T & Any` return. - `override` — must keep a compatible signature with the Java declaration.

  • Why does the Kotlin override parameter become `T?` rather than `T` here?
    The Java parameter is `@Nullable T`, so Kotlin sees it as nullable. Matching it as `T?` reproduces the contract; the non-null guarantee belongs only to the `@NotNull` return.
  • If you ignored `T & Any` and returned a platform type, what's the risk?
    Platform types defer null checks to runtime. A null could flow into non-null Kotlin code and throw a `NullPointerException` later, far from its source — exactly what null safety aims to prevent.

saying these in an interview costs you the question

  • Claiming Kotlin overrides Java generics by writing `!!` everywhere instead of using the type
  • Not realizing Java type variables map to nullable-bounded Kotlin parameters
  • Thinking `@NotNull` on the return can be honored by a bare `T` return
  • Saying `T & Any` performs a runtime null check (it's the elvis/`error` in the body that does)

context

open as a page

What does the type `T & Any` mean in Kotlin, and what problem does it solve?

level: juniorimportance: should knowfreq 35%

basics

~10 s

T & Any means "the type T, but guaranteed not null." If a generic type T could be null, writing T & Any forces it to be a non-null version of that type.

open as a page

When should you use `T & Any` versus just bounding the type parameter with `T : Any`? How do they differ?

level: middleimportance: should knowfreq 22%

basics

~20 s

T : Any forces every use of T to be non-null for all callers. T & Any keeps T able to be nullable but marks one specific spot (a parameter, return, or local) as non-null.

open as a page

Explain the precise semantics of `T & Any`: what is its erasure, why does the compiler sometimes warn, and where is it disallowed?

level: seniorimportance: should knowfreq 14%

basics

~20 s

T & Any is a compile-time-only non-null version of T. At runtime it erases to T's bound. The compiler warns when it's redundant (T already non-null) and forbids it where T isn't a type parameter with a nullable bound.

open as a page

As a library author exposing a generic API consumed from both Kotlin and Java, when would you reach for `T & Any`, and what are the design and migration consequences?

level: principalimportance: nice to knowfreq 8%

basics

~20 s

Use T & Any when you must keep T flexible (often for Java overrides) but a specific input or output is always non-null. It documents the contract precisely without forcing all callers to drop nullable types.

open as a page