How do !! and platform types interact when calling Java from Kotlin, and why does Java interop drive overuse of !!?
answer
- Java -> Kotlin un-annotated = platform type String!
- platform type = use as T or T?, no enforcement
- !! at the boundary hides real Java nulls
- Kotlin honours @Nullable/@NotNull and JSpecify
- type the seam as T? and handle null once
basics
~20 sJava values whose nullability Kotlin can't see are 'platform types'. Kotlin lets you use them without forcing null checks, so developers reach for !! to convert them to non-null — sometimes hiding real nulls and causing crashes later.
solid answer
~50 sWhen Kotlin calls Java code, the compiler often cannot tell whether a returned reference is nullable, so it assigns it a **platform type**, written `String!` in diagnostics. A platform type is permissive: you may treat it as either `String` or `String?` with no compiler enforcement. If you assign it to a non-null Kotlin type and the Java value is actually null, you get an NPE — and people frequently add `!!` to make that conversion explicit or to satisfy the compiler. The hazard is that `!!` (and platform types generally) bypass null safety, so a null sneaking out of Java surfaces as a runtime `NullPointerException`, often far from the call site. The disciplined approach is to read Java nullness annotations (`@Nullable`/`@NotNull`, JSpecify) which Kotlin honours and turn into real `T?`/`T`, and to explicitly type the value as `String?` at the boundary and handle null there, rather than assert it away with `!!`.
code
kotlin · 8 lines// Java (no annotations): public String findById(long id) { ... may return null ... }
// Risky: platform type asserted non-null; NPE if Java returned null
val user: User = repo.findById(1)!!
// Disciplined: treat the seam as nullable and handle it once
val user: User? = repo.findById(1)
val name = user?.name ?: "anonymous"go deeper
Knows Java calls can return null and that !! can crash, even if 'platform type' is unfamiliar.
Defines platform types, recognizes String! in diagnostics, and types the boundary as T? instead of asserting.
Explains how Kotlin honours @Nullable/@NotNull/JSpecify and designs the seam to handle null once, keeping !! out of the interior.
Drives an interop policy: annotate Java APIs, treat un-annotated seams as nullable, and forbid interior !! through review/lint.
## Platform types: the source of the pressure Kotlin's type system tracks nullability, but **Java's does not** (a bare `String` in Java may or may not be null). When Kotlin calls an un-annotated Java method, it cannot know the intent, so it gives the result a **platform type**, denoted with a single trailing exclamation, e.g. `String!`. You cannot write `String!` yourself; it only appears in compiler messages and inferred types. ```kotlin // Java: public String getName() { ... } val name = javaObj.name // inferred platform type String! ``` A platform type is **relaxed**: the compiler lets you use it as non-null `String` *or* nullable `String?` with no warning. That flexibility is the trap. ## Where !! comes in To move a platform-typed value into a strict non-null Kotlin type, developers often write: ```kotlin val name: String = javaObj.name!! // assert non-null ``` If `getName()` actually returns null, the `!!` (or even the plain assignment to `String`) throws a `NullPointerException`. So Java interop systematically nudges people toward `!!` to 'close the gap' the missing Java nullness leaves — even when the value really can be null. ## The disciplined alternatives 1. **Honour nullness annotations.** Kotlin reads `@Nullable`/`@NotNull` (JetBrains, Android, javax) and **JSpecify** `@Nullable`. An annotated Java method produces a proper `String?` or `String`, eliminating the platform type and the temptation to `!!`. ```kotlin // Java: @Nullable String getName() -> Kotlin sees String? val name: String? = javaObj.name // forced to handle null safely ``` 2. **Type the boundary explicitly.** Annotate the variable as nullable and decide at the seam: ```kotlin val name: String? = javaObj.name val display = name ?: "unknown" ``` 3. **Assert with a message when it truly cannot be null** (documented invariant), via `checkNotNull`/`requireNotNull` rather than bare `!!`. ## Why this matters at scale Platform types let nulls leak across the Java/Kotlin seam silently, and `!!` converts the leak into a crash that is hard to attribute. The right model is: **treat every un-annotated Java boundary as nullable**, handle it once at the seam, and push non-null types inward. That keeps `!!` out of the interior of the codebase entirely.
- What is the type denoted String! and can you declare a variable of that type yourself?It is a platform type from un-annotated Java; you cannot write String! in source — it only appears in inferred/diagnostic output. You pin it by declaring String or String?.
- How can a Java library author eliminate platform types for Kotlin callers?Annotate APIs with @Nullable/@NotNull (or JSpecify @Nullable / @NullMarked); Kotlin then infers proper nullable/non-null types instead of platform types.
A platform type is like an unlabelled package from Java: Kotlin won't force you to check inside, so a null can be hiding in there until you open it.
saying these in an interview costs you the question
- Doesn't know what a platform type is
- Thinks Kotlin always knows Java's nullability
- Routinely !!s every Java call result without considering @Nullable
- Believes platform types force a null check (they don't)
- Unaware Kotlin honours JSpecify/@Nullable annotations