skip to content

Platform Types

What happens to nullability when values cross from Java: the compiler stops enforcing it, and it is on you to restore the guarantee through annotations or explicit handling. This is where Kotlin's null safety is at its weakest, so interviewers probe it deliberately.

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

explore

questions

13

A Java method returns a value Kotlin sees as a platform type (e.g. String!). How do you safely give it a default when it is null, and what does the ?: (Elvis) operator do here?

level: juniorimportance: must knowfreq 78%

answer

  1. Java return = platform type T!, no compiler null check
  2. ?: returns left if non-null, else right
  3. Assign to explicit Kotlin type at the boundary
  4. RHS can be throw/return, not just a value
  5. Result type is non-null when fallback guarantees a value

basics

~10 s

Use the Elvis operator: val name = javaCall() ?: "unknown". If the left side is null, you get the value on the right. It gives a safe default instead of risking a crash.

solid answer

~40 s

Java has no null-tracking, so Kotlin treats Java return values as platform types (written T! in errors). The compiler trusts you and inserts no null check, so a null can slip in and later blow up. The defensive move is to assign it to a Kotlin type immediately and supply a fallback with the Elvis operator ?:. `val name: String = javaCall() ?: "unknown"` evaluates the left expression once; if it is non-null that value is used, otherwise the right side is. The right side can be any expression of a compatible type, including `throw`, `return`, or a computed default. This pulls the unknown nullability into an explicit, non-null Kotlin type at the boundary, so the rest of your code never has to think about it.

code

kotlin · 5 lines
kotlin
// Java boundary: getCity() returns String! (platform type)
val city: String = address.city ?: "N/A"

// Fallback that fails fast instead of defaulting:
val port: Int = config.port ?: throw IllegalStateException("port missing")

go deeper

for a junior

Knows Elvis gives a default and prevents a crash on null.

for a middle

Explains platform types, why the boundary needs conversion, and the resulting non-null type.

for a senior

Uses ?: with throw/return to fail fast and standardizes converting platform types at the entry point.

for a principal

Establishes team-wide boundary-hardening conventions and wraps untrusted Java libraries behind Kotlin-typed adapters.

## The problem: platform types When Kotlin calls Java, the Java type system carries no nullability information (ignoring annotations). Kotlin therefore assigns a **platform type**, shown in diagnostics as `String!` (the `!` means "nullable or not — compiler won't check"). The compiler does **not** force you to null-check it, which is convenient but dangerous: a `null` can flow into a non-null Kotlin variable and throw a `NullPointerException` later, far from the source. ## The Elvis operator `?:` The Elvis operator takes the form `a ?: b`. It evaluates `a`; if `a` is non-null it returns `a`, otherwise it returns `b`. The right-hand side is only evaluated when needed (short-circuit). ```kotlin // Java: String User.getNickname() -> Kotlin sees String! val nickname: String = user.nickname ?: "anonymous" ``` Here the result type is the **non-null** `String`, because the fallback guarantees a value. ## Why do it at the boundary The rule of thumb: **convert platform types to explicit Kotlin types the moment they enter your code**. Declaring `val nickname: String = ...` forces the compiler to insert an `Intrinsics.checkNotNullExpressionValue` style check, so if `null` does arrive you fail right there, not three layers deep. ## Right-hand side can short-circuit control flow The fallback can throw or return: ```kotlin val id: Long = record.id ?: throw IllegalStateException("record has no id") val token = header() ?: return null ``` ## Related operators - `?.` (safe call) — call a member only if the receiver is non-null, else the whole expression is null. - `?.let { }` — run a block only when non-null. - `!!` — assert non-null, throwing `NullPointerException` if wrong (avoid; it discards information about *why*). Elvis is the workhorse for supplying defaults; `!!` is the lazy, blunt alternative.

  • What is the type of `user.nickname ?: "anonymous"` when nickname is String!?
    String (non-null) — the Elvis fallback supplies a guaranteed value, so the expression cannot be null.
  • Is the right-hand side of ?: always evaluated?
    No. It is evaluated lazily, only when the left side is null (short-circuit).

Elvis is a safety net under a tightrope: if the value falls (is null), the net (default) catches it instead of letting it hit the ground.

saying these in an interview costs you the question

  • Claiming the compiler forces a null check on Java return values
  • Using !! everywhere instead of a meaningful fallback
  • Thinking ?: returns the right side when left is non-null
  • Believing the RHS is always evaluated
  • Saying platform types are the same as nullable types

context

open as a page

When you call a Java method from Kotlin, how do @Nullable and @NotNull annotations on that Java method change the type Kotlin sees?

level: juniorimportance: must knowfreq 70%

basics

~20 s

If the Java method is marked @NotNull, Kotlin treats the result as a normal non-null type. If it is @Nullable, Kotlin treats it as nullable and makes you check for null. Without an annotation, Kotlin can't tell.

open as a page

What is a Kotlin platform type, how is it written, and why does calling Java code sometimes risk a NullPointerException even in Kotlin?

level: juniorimportance: must knowfreq 70%

basics

~20 s

When Kotlin calls Java code that doesn't say whether a value can be null, Kotlin can't tell either. It treats the value as a 'platform type' and skips its null checks, so a null can slip through and crash at runtime.

open as a page

At the Java interop boundary, when do you use requireNotNull versus checkNotNull, and how do they differ from the !! operator?

level: middleimportance: must knowfreq 70%

basics

~10 s

requireNotNull checks arguments and throws IllegalArgumentException if null. checkNotNull validates internal state and throws IllegalStateException. Both return the non-null value. !! just throws a generic NullPointerException with no message.

open as a page

Contrast how Kotlin imports a Java method that is un-annotated versus one annotated @NotNull. What changes at compile time and at runtime?

level: middleimportance: must knowfreq 60%

basics

~10 s

Un-annotated, Kotlin uses a flexible platform type and skips null checks, so a hidden null blows up later. With @NotNull, Kotlin imports a strict non-null type and trusts it, checking nullability at compile time.

open as a page

When a Java method returns an unannotated value, how do you choose between pinning it to a non-null T versus a nullable T? in Kotlin, and what does each choice generate at the bytecode level?

level: middleimportance: must knowfreq 55%

basics

~20 s

Look at what the Java method really does. If it can return null, store it as T? and use safe calls. If it never returns null, store it as T; Kotlin then adds a check that crashes immediately at the boundary if you were wrong.

open as a page

You receive a List<String> from a Java API. Even though the type looks non-null, why might individual elements be null, and how do you defensively process them with ?.let and Elvis?

level: middleimportance: should knowfreq 58%

basics

~20 s

Java collections can contain null elements even when Kotlin shows them as non-null platform types. Guard each element with ?.let to run code only when present, and use ?: for a default when it is null.

open as a page

When hardening the Java interop boundary, when should defensive null handling fail fast (throw) versus tolerate null with a fallback? How do you decide, and what are the trade-offs?

level: seniorimportance: should knowfreq 48%

basics

~20 s

Throw early when a null means something is truly broken or violates a contract, so bugs surface immediately. Use a fallback when null is a legitimate, expected case you can handle meaningfully. Don't silently swallow nulls that hide defects.

open as a page

What does @ParametersAreNonnullByDefault (and JSR-305 default annotations) do for Kotlin interop, and how would you override it for one nullable parameter?

level: seniorimportance: should knowfreq 40%

basics

~20 s

It tells Kotlin that, unless marked otherwise, every parameter in that scope is non-null, so you don't have to annotate each one. To make a single parameter nullable, you add @Nullable to just that parameter.

open as a page

How do platform types behave with Java generics and collections — e.g. a Java method returning List<String> — and what subtle NPE traps appear when iterating or destructuring such results in Kotlin?

level: seniorimportance: should knowfreq 35%

basics

~20 s

Both the collection and the things inside it become platform types. So a Java List<String> can be null, and any element can be null too, even though Kotlin lets you treat them as non-null. Iterating may NPE on a null element.

open as a page

How do requireNotNull/checkNotNull enable a smart cast to non-null afterward, and what stdlib mechanism makes that possible? How does this differ from wrapping a Java value in your own helper?

level: seniorimportance: nice to knowfreq 30%

basics

~20 s

These functions are declared with Kotlin contracts that tell the compiler 'if this returns, the value is not null'. So after calling them you can use the original variable as non-null. A plain helper you write won't smart-cast unless you also declare a contract.

open as a page

How do nullability annotations interact with Java generics and array element types when imported into Kotlin, and what are the common gotchas?

level: seniorimportance: nice to knowfreq 25%

basics

~20 s

Annotations can mark not just the outer type but also the element type inside a generic or array. If only the container is marked, the elements may still be platform types, so a list can be non-null while its items aren't checked.

open as a page

When a Kotlin class implements a Java interface with unannotated method parameters/returns, what nullability must the override declare, and how can a team make the compiler stricter about platform types?

level: seniorimportance: nice to knowfreq 22%

basics

~20 s

When you override an unannotated Java method, Kotlin lets you choose whether the types are nullable or not, but the choice becomes a hard contract for your callers. Teams can also turn on a compiler flag to warn whenever a platform type is misused.

open as a page