What is the difference between the as and as? cast operators in Kotlin, and what happens when each fails?
answer
- as = throw CCE on mismatch
- as? = null on mismatch, result is nullable
- null as NonNull throws; null as? yields null
- as? + Elvis ?: is the idiom
- prefer is smart-cast / as? over bare as
basics
~20 sas forces a value to a type and crashes with an error if it is not that type. as? tries the same but gives back null instead of crashing when the value does not match.
solid answer
~40 sas is the unsafe cast operator: x as Foo asserts the runtime type and throws ClassCastException if x is not a Foo. as? is the safe cast operator: x as? Foo returns the value typed as Foo when it matches and null otherwise, so its result type is always nullable (Foo?). Casting null with as to a non-null type also throws; null as? Foo just yields null. Because as? never throws, it pairs naturally with the Elvis operator, e.g. val f = x as? Foo ?: return. Prefer as? plus null handling, or a smart-cast via is, over a bare as that can blow up at runtime.
code
kotlin · 9 linesval obj: Any = listOf(1, 2, 3)
// unsafe: throws ClassCastException
// val s = obj as String
// safe: yields null, no crash
val s: String? = obj as? String // null
val list = obj as? List<*> ?: emptyList<Any?>()
println(list.size) // 3go deeper
States that as throws ClassCastException and as? returns null on mismatch.
Knows as? result type is nullable and pairs it with Elvis; handles the null-receiver cases.
Prefers is smart-casts/as? over bare as, explains when a loud CCE is actually the right design choice.
Frames cast choice as an error-handling policy decision and guides API contracts so casts are rarely needed.
## The two cast operators Kotlin has two infix cast operators that convert a reference from one static type to another. - **`as` (unsafe cast)**: `value as TargetType` asserts that `value` really is a `TargetType` at runtime. If it is, the expression has static type `TargetType`. If it is **not**, the JVM throws a `ClassCastException` (`CCE`). - **`as?` (safe cast)**: `value as? TargetType` does the same check but, on failure, evaluates to `null` instead of throwing. Its result type is therefore always **nullable** — `TargetType?`. ## Why the result of `as?` is nullable Because `as?` can legitimately produce `null` (on a type mismatch), the compiler widens the result type to `TargetType?`. This is true even if `TargetType` is itself non-null: ```kotlin val anything: Any = "hello" val s: String = anything as String // ok, smart static type String val n: String? = anything as? String // result type is String?, value "hello" val bad: String? = 42 as? String // null, no exception // val boom = 42 as String // throws ClassCastException ``` ## Null handling A `null` receiver behaves differently per operator: - `null as String` throws `ClassCastException` (null is not a `String`). - `null as String?` succeeds — the target is nullable, so null is allowed. - `null as? String` yields `null` (no throw). ## Idiomatic use with Elvis `as?` composes cleanly with the **Elvis operator** `?:` to provide a fallback or early exit: ```kotlin fun describe(x: Any): String { val text = x as? String ?: return "not a string" return "string of length ${text.length}" } ``` ## When to use which - Use **`is` smart casts** when you branch on type — they are checked and need no operator on the body. - Use **`as?`** when a mismatch is expected/handled and you want null, not a crash. - Use **`as`** only when you are certain of the type (e.g., from an external contract) and a mismatch is a programming bug you want surfaced loudly.
- What is the static type of the expression `x as? Int`?`Int?` — `as?` always produces a nullable type because it returns null on a failed cast.
- Does `null as? String` throw?No. It evaluates to `null`. Only the unsafe `as` to a non-null type throws on a null/mismatched value.
as is a strict bouncer who throws you out if your ID is wrong; as? just shakes their head and lets you walk away (returns null).
saying these in an interview costs you the question
- Saying as? returns the non-null type (it always returns a nullable type)
- Claiming as never throws / catches the error itself
- Thinking null as? Foo throws ClassCastException
- Confusing as (cast) with is (type check)
- Believing as performs a conversion like toInt() rather than a runtime type assertion