What is the difference between `x as String?` and `x as? String`? When `x` is `null` or the wrong type, how does each behave?
answer
- as String? = unsafe cast to nullable; throws on wrong non-null type
- as? String = safe cast; null on wrong type, never throws
- Both return null for null input
- Differ only on non-null wrong type: throw vs null
- ? on operator (as?) ≠ ? on target type (String?)
basics
~20 sx 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.
solid answer
~40 s`x as String?` is an **unsafe cast to a nullable type**. It succeeds when `x` is a `String` or when `x` is `null`, but it still throws `ClassCastException` if `x` is a non-null value of a different type (e.g. an `Int`). `x as? String` is the **safe cast operator**: it returns the value when the type matches, and `null` in every other case — both for `null` input and for a non-null type mismatch — and never throws. So they agree on `null` input and on a matching `String`, but diverge on a non-null wrong type: `as String?` throws, `as?` returns `null`. Choose `as?` when a type mismatch is an expected, recoverable case; choose `as String?` only when `null` is legal but a wrong non-null type is a bug you want surfaced.
code
kotlin · 9 linesfun demo(x: Any?) {
val viaTarget: String? = try { x as String? } catch (e: ClassCastException) { "THROW" }
val viaSafe: String? = x as? String
println("$viaTarget | $viaSafe")
}
demo(null) // null | null
demo("hi") // hi | hi
demo(42) // THROW | nullgo deeper
Recognizes as? is the safe one but may conflate as String? with it.
Cleanly separates operator-? from target-? and states the throw-vs-null divergence on wrong types.
Chooses between fail-fast (as String?) and recover (as? String) based on whether a wrong type is a bug or expected.
Uses the distinction deliberately at API boundaries to encode invariants — null allowed, wrong type rejected loudly.
## `as String?` vs `as? String` These look similar but the `?` lives in different places and means different things. ### `x as String?` — unsafe cast, nullable target Here `String?` is the **target type** of an ordinary unsafe `as` cast. The cast considers `null` an acceptable value of `String?`, so: - `x == null` → succeeds, result is `null`. - `x` is a `String` → succeeds. - `x` is a **non-null wrong type** (e.g. `Int`) → throws **`ClassCastException`**. ```kotlin val a: Any? = null val b: Any? = "hi" val c: Any? = 42 a as String? // null (ok) b as String? // "hi" (ok) c as String? // throws ClassCastException ``` ### `x as? String` — safe cast Here `?` is part of the **operator** `as?`. It returns `null` whenever the cast can't produce a `String`: - `x == null` → `null`. - `x` is a `String` → the string. - `x` is a non-null wrong type → `null` (no throw). ```kotlin a as? String // null b as? String // "hi" c as? String // null (no exception) ``` ### Side-by-side | input | `as String?` | `as? String` | |-------|--------------|--------------| | `null` | `null` | `null` | | `"hi"` | `"hi"` | `"hi"` | | `42` (wrong type) | **throws** | `null` | Both produce a `String?` static type. The only behavioral difference is the wrong-non-null-type row. ### Choosing - Use **`as? String`** when a wrong type is an *expected* outcome you handle (typically with `?:`). - Use **`as String?`** when `null` is a legitimate value but a non-null value of the wrong type indicates a *bug* you want to fail fast on. ### Common confusion Don't read `as String?` as 'safe cast' — the safety token is `as?`, not a trailing `?` on the type. Misplacing the `?` silently changes throw-vs-null behavior.
- Do `as String?` and `as? String` ever behave identically?Yes — for `null` input and for an actual `String`, both yield the same result. They differ only when `x` is a non-null value of the wrong type.
- Which would you use to fail fast on an unexpected non-null type while allowing null?`as String?` — it permits `null` but throws `ClassCastException` on a non-null wrong type, surfacing the bug.
saying these in an interview costs you the question
- Saying `as String?` never throws (it does, on wrong non-null types)
- Treating the trailing `?` on the type as making the cast safe
- Claiming `as? String` throws on null input
- Thinking the two are fully interchangeable