skip to content

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