Null Safety Quiz
KOTLIN › Language Idioms
What is the value and type of b?.length when b is declared as val b: String? = null?
- 0 of type Int
- null of type Int?
- It throws a NullPointerException
- It is a compile-time error
Answer: null of type Int?
A safe call short-circuits to null when the receiver is null, and the result type of ?. is nullable, so the expression is null typed as Int?.
Given val len = name!!.length where name is null, what happens?
- len becomes null and the assignment completes normally
- len becomes 0 because Kotlin treats null as empty
- A NullPointerException is thrown at runtime
- It fails to compile because name can be null
Answer: A NullPointerException is thrown at runtime
The !! operator throws a NullPointerException at runtime whenever the value it is applied to is null.
Which expression safely casts obj to String, yielding null instead of an exception if it is not a String?
- obj as? String
- obj as String
- obj is String
- obj!! as String
Answer: obj as? String
as? is the safe cast: it returns null on failure (result type String?), whereas as throws ClassCastException, is is a boolean check, and !! does not perform a cast.
Why can a Kotlin call on a Java method's return value still throw a NullPointerException even with no !! in sight?
- Kotlin turns off null safety in any file that imports Java classes
- Safe calls are skipped entirely when the receiver comes from Java code
- Elvis operators stop working whenever a value crosses into Java code
- It’s a platform type with unknown nullability, so checks are relaxed
Answer: It’s a platform type with unknown nullability, so checks are relaxed
Unannotated Java types become platform types (T!); the compiler trusts you and omits null checks, so an actually-null value throws at runtime.
Which idiom returns the string's length, or 0 when the nullable string s is null?
- s!!.length ?: 0
- s?.length ?: 0
- s.length ?: 0
- s ?: 0.length
Answer: s?.length ?: 0
s?.length yields Int? (null when s is null), and the elvis operator then substitutes 0 for that null. s!!.length would throw, and s.length does not compile on a nullable.
Inside if (x != null) { ... }, why can you call x.length without a safe call when x is String?
- The compiler smart-casts x to a non-null String there
- The if block implicitly wraps every x access in !!
- String? and String become the same type at runtime
- Kotlin re-checks x for null on each property access there
Answer: The compiler smart-casts x to a non-null String there
After a null check on a stable value, the compiler smart-casts x to the non-null type String for the scope where the check holds, so member access needs no operator.
What does user?.let { sendEmail(it.email) } do when user is null?
- Throws a NullPointerException
- Calls sendEmail with null
- Skips the let block entirely and evaluates to null
- Fails to compile because let requires a non-null receiver
Answer: Skips the let block entirely and evaluates to null
The safe call means let is only invoked when user is non-null; when user is null the whole expression short-circuits to null and the block never runs.
Why can you call s.isNullOrEmpty() directly on a value of type String? without any safe call or prior null check?
- The compiler auto-unwraps the nullable to String before the call runs
- isNullOrEmpty internally runs !! on its receiver before checking
- isNullOrEmpty is an extension on String?, so a null this is ok
- The compiler smart-casts s to a non-null String at that call site
Answer: isNullOrEmpty is an extension on String?, so a null this is ok
Functions like isNullOrEmpty() and isNullOrBlank() are declared as extensions on a nullable receiver, so the receiver may be null and the function body handles that case itself.
Given val config: String? = ..., what does requireNotNull(config) return and how does it fail?
- Returns non-null String; throws IllegalArgumentException if null
- It returns String? unchanged and never throws on a null value
- It returns a Boolean telling whether config was non-null
- Returns String but throws ClassCastException when config is null
Answer: Returns non-null String; throws IllegalArgumentException if null
requireNotNull returns the value as its non-null type and throws IllegalArgumentException when the argument is null; the sibling checkNotNull throws IllegalStateException instead.
Given val nums: List<Int?>, which call yields a List<Int> with the nulls removed and the element type narrowed to non-null?
- nums.filter { it != null }
- nums.filterNotNull()
- nums.map { it }
- nums.filterNot { it == null }
Answer: nums.filterNotNull()
Only filterNotNull() changes the static element type to the non-null Int; filter and filterNot keep the declared List<Int?> type because a runtime predicate does not refine the element type.
In val token: String = header ?: return, where header is String?, why is token typed as non-null String?
- Because val properties are always non-null in Kotlin, even from String?
- Because the elvis operator copies the nullable type onto its left operand
- Because return performs an implicit !! on header, making it String
- Because return is Nothing, so ?: keeps the left side’s non-null type
Answer: Because return is Nothing, so ?: keeps the left side’s non-null type
The right operand return has type Nothing, a subtype of every type, so the combined elvis expression takes the non-null type of the left operand, leaving token as String.
How does the equality expression a == b behave in Kotlin when a is null?
- It does a null-safe structural check, returning a Boolean, no NPE
- It throws a NullPointerException at runtime because a is null
- It compares references only, always returning false when a is null
- It fails to compile unless a is declared a non-null type
Answer: It does a null-safe structural check, returning a Boolean, no NPE
The == operator compiles to a null-safe equals call (roughly a?.equals(b) ?: (b === null)), so it handles null receivers and returns a Boolean without throwing.
Why does the compiler refuse to smart-cast a top-level mutable var x: String? to String inside if (x != null) { x.length }?
- Top-level properties can never be null, so the check is unnecessary here
- var properties are always treated as platform types, even when declared as String?
- Because a mutable var can change after the check, so non-null can’t be guaranteed
- Smart casts only work on function parameters, not on any kind of property
Answer: Because a mutable var can change after the check, so non-null can’t be guaranteed
For a mutable (non-local) var the value could change after the null check, so the compiler cannot prove it remains non-null and declines the smart cast; copy it into a local val or use ?.let.