skip to content

Safe Cast as?

as? returns null on a type mismatch instead of throwing ClassCastException like the plain as cast. Pairing it with Elvis gives you the typed-or-default expression that shows up everywhere in equals() implementations and deserialization code.

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

questions

5

What is the difference between the `as` and `as?` operators in Kotlin, and what happens at runtime when the value is not of the target type?

level: juniorimportance: must knowfreq 78%

answer

  1. as = throw ClassCastException; as? = return null
  2. as? result is ALWAYS nullable (String?)
  3. Idiom: as? T ?: default
  4. Use as when type is guaranteed, as? when uncertain
  5. null as? String == null (no throw)

basics

~10 s

as forces a type conversion and crashes with an exception if the value isn't that type. as? tries the same conversion but gives back null instead of crashing when the type doesn't match.

solid answer

~40 s

Both are cast operators. The unsafe cast `as` converts a value to a target type and throws a `ClassCastException` at runtime if the value is not an instance of that type. The safe cast `as?` attempts the same conversion but returns `null` instead of throwing when the type does not match. Because of that, the result type of `as?` is always nullable: `x as? String` has type `String?`, even if `x` was non-null. Use `as` only when you are certain of the type (e.g. right after an `is` check or by contract); use `as?` when the type is uncertain and you want to handle the mismatch gracefully, typically with the Elvis operator: `val s = x as? String ?: "default"`.

code

kotlin · 7 lines
kotlin
fun lengthOrZero(x: Any): Int {
    val s = x as? String   // type: String?
    return s?.length ?: 0
}

lengthOrZero("hello") // 5
lengthOrZero(42)      // 0 (no exception)

go deeper

for a junior

Knows as throws and as? returns null, and that the result of as? is nullable.

for a middle

Can articulate the result-type nullability and pair as? with ?: for a clean default.

for a senior

Frames the choice as intent: failure-as-bug (as) vs failure-as-expected (as?), avoiding exception-driven control flow.

for a principal

Connects it to API design — pushing type uncertainty to boundaries (deserialization) and keeping core code type-safe via sealed hierarchies instead of casts.

## Casting in Kotlin A *cast* tells the compiler to treat a value as a particular type. Kotlin has two cast operators that differ in how they handle a *type mismatch* — when the actual runtime type is not compatible with the target type. ### Unsafe cast: `as` ```kotlin val any: Any = 42 val s = any as String // throws ClassCastException at runtime ``` `as` performs an **unsafe cast**. If the value is an instance of the target type, you get it back typed as that type. If not, the JVM throws a `ClassCastException`. The result type is exactly the target type you wrote. ### Safe cast: `as?` ```kotlin val any: Any = 42 val s: String? = any as? String // returns null, no exception ``` `as?` performs a **safe cast**. On a type mismatch it evaluates to `null` instead of throwing. A crucial consequence: the result of `as?` is **always nullable**. `any as? String` has type `String?` even when `any` is non-null, because the cast can produce `null`. ### Typed-or-default with `?:` The idiomatic pairing is `as?` followed by the Elvis operator `?:` to supply a fallback: ```kotlin fun describe(x: Any): String = (x as? String)?.uppercase() ?: "not a string" ``` Here `x as? String` yields `String?`; `?.uppercase()` runs only if non-null; `?:` provides the default when the cast failed. ### When to use which - Use `as` when the type is guaranteed (e.g. by contract, or immediately after an `is` smart-cast where a plain `as` would be redundant anyway). A failure here is a *programming error* you want surfaced as an exception. - Use `as?` when the type is genuinely uncertain (deserialization, heterogeneous collections, framework callbacks) and a mismatch is an expected outcome to handle, not a bug. ### Nullable target types `as?` also tolerates a `null` input: `null as? String` returns `null`. Note `as` to a nullable type (`x as String?`) succeeds for `null` input but still throws for a non-null value of the wrong type — that is different from `as?`.

  • What is the static type of `value as? Int` when `value: Any`?
    `Int?` — the safe cast is always nullable because it can produce `null` on mismatch.
  • Does `as?` ever throw a ClassCastException?
    No. By design it returns `null` on a type mismatch instead of throwing; that is its entire purpose versus `as`.

as is shoving a key into a lock and snapping it if it doesn't fit; as? tries the key and just hands it back to you if it doesn't turn.

saying these in an interview costs you the question

  • Saying `as?` returns the same non-null type as the target
  • Claiming `as?` can throw ClassCastException on mismatch
  • Confusing `as?` with the safe-call `?.` operator
  • Believing `as` returns null on failure

context

open as a page

Write an idiomatic expression that returns the length of `input: Any` if it is a `String`, and `-1` otherwise, using `as?`. Explain why `as?` is preferred over an `is` check plus `as` here.

level: middleimportance: must knowfreq 70%

basics

~10 s

Write (input as? String)?.length ?: -1. as? does the type test and conversion in one step and gives null on mismatch, so you don't need a separate is check followed by a cast.

open as a page

What is the difference between `x as String?` and `x as? String`? When `x` is `null` or the wrong type, how does each behave?

level: middleimportance: should knowfreq 40%

basics

~20 s

x as String? is an unsafe cast to a nullable type: it allows null but still throws if x is a non-null wrong type. x as? String is a safe cast: it returns null for both null input and wrong types, never throwing.

open as a page

Because of JVM type erasure, what does `value as? List<String>` actually check at runtime, and what is the implication for casting generic types safely?

level: seniorimportance: should knowfreq 48%

basics

~20 s

On the JVM the generic part is erased, so as? List<String> only checks that the value is some List, not that its elements are Strings. A List<Int> would pass the cast even though the elements are wrong.

open as a page

In a hot deserialization path you see heavy use of `as?` with Elvis defaults. How do you reason about correctness and cost, and when would you redesign away from runtime casting?

level: principalimportance: nice to knowfreq 24%

basics

~20 s

as? is a cheap type check, so cost is rarely the issue. The real concern is design: lots of as? means types are uncertain everywhere. Push validation to the boundary and use typed models so the core code never casts.

open as a page