← Kotlin EnglishChapter 06 of 13

Null Safety

## Learning Objectives - Understand Kotlin's null safety system - Use safe call operator (?.) - Work with the Elvis operator (?:) - Use the not-null assertion (!!) - Learn about safe casts (as?) ## Nullable Types Kotlin's type system distinguishes between nullable and non-nullable types: ```kotlin fun main() { // Non-nullable - cannot hold null val name: String = "Kotlin" // name = null // Error! // Nullable - can hold null var nullableName: String? = "Kotlin" nullableName = null // OK println(nullableName) } ``` ## Safe Call Operator (?.) The safe call operator returns null if the object is null: ```kotlin fun main() { val str: String? = "Hello" // Safe call - returns length or null val length1 = str?.length println("Length 1: $length1") // 5 str = null val length2 = str?.length println("Length 2: $length2") // null } ``` ### Chaining Safe Calls ```kotlin fun main() { val person: Person? = Person(Address("New York")) // Chained safe calls val city = person?.address?.city println("City: $city") // New York (or null if any part is null) person?.address = null val city2 = person?.address?.city println("City 2: $city2") // null } class Person(var address: Address?) class Address(var city: String?) ``` ## Elvis Operator (?:) The Elvis operator provides a default value when null: ```kotlin fun main() { val str: String? = null // Without Elvis - returns null val length1 = str?.length println("Length: $length1") // null // With Elvis - returns default val length2 = str?.length ?: 0 println("Length: $length2") // 0 } ``` ### Elvis with throw ```kotlin fun main() { val str: String? = null // Elvis can throw exceptions too val nonNull = str ?: throw IllegalArgumentException("String cannot be null") } fun getName(person: Person?): String { return person?.name ?: "Unknown" } ``` ## Not-Null Assertion (!!) Use `!!` when you're certain a value is not null: ```kotlin fun main() { val str: String? = "Hello" // !! asserts value is not null val length = str!!.length println("Length: $length") // 5 // If null, throws NullPointerException val nullStr: String? = null // nullStr!!.length // Throws NullPointerException } ``` ### When to Use - Only when you're absolutely certain the value is not null - Avoid using `!!` in production code - It defeats the purpose of Kotlin's null safety ## Safe Cast (as?) Safe cast returns null instead of throwing ClassCastException: ```kotlin fun main() { val obj: Any = "Hello" // Regular cast - throws if fails // val str: String = obj as String // Safe cast - returns null if fails val str: String? = obj as? String println("Safe cast: $str") // Hello val num: Int? = obj as? Int println("Safe cast to Int: $num") // null } ``` ## Let Function Use `let` to execute code only if value is not null: ```kotlin fun main() { val str: String? = "Hello" // execute only if not null str?.let { println("Length: ${it.length}") } // with default behavior str?.let { it.length } ?: println("String is null") } ``` ### Let with Multiple Null Checks ```kotlin fun main() { val firstName: String? = "John" val lastName: String? = "Doe" // Combining multiple lets firstName?.let { f -> lastName?.let { l -> println("$f $l") } } } ``` ## The ? Operator Summary | Operator | Description | Returns | |----------|-------------|---------| | `?.` | Safe call | `null` if receiver is `null` | | `?:` | Elvis operator | Default if left is `null` | | `!!` | Not-null assertion | Throws NPE if `null` | | `as?` | Safe cast | `null` if cast fails | ## Null Safety with Collections ```kotlin fun main() { val list: List? = listOf("a", "b", "c") // Safe call with size println(list?.size) // 3 list = null // Empty list if null val size = list?.size ?: 0 println(size) // 0 // Null list val emptyList = list?.filter { it.isNotEmpty() } ?: emptyList() println(emptyList) // [] } ``` ## Platform Types When Java code is used from Kotlin, types from Java can be platform types (can be null or non-null): ```kotlin // Java code public class JavaExample { public String getGreeting() { return "Hello"; } } ``` ```kotlin // Kotlin code calling Java fun main() { val javaExample = JavaExample() val greeting: String = javaExample.getGreeting() // Non-null // If Java method returns @Nullable String // Then Kotlin sees it as String? } ``` ## Checking for Null in Conditions ```kotlin fun main() { val str: String? = "Hello" // Regular null check if (str != null) { println(str.length) // Smart cast to String } // After null check, smart cast applies // Works only with local variables } ``` ### Note on Smart Casts Smart casts don't work with mutable variables or properties that could be changed: ```kotlin fun main() { var str: String? = "Hello" // Smart cast doesn't work because var can be changed // str could be changed to null between check and use if (str != null) { // println(str.length) // Error: only safe call allowed } // Safe call works println(str?.length) } ``` ## Summary - Kotlin distinguishes nullable (`T?`) and non-nullable (`T`) types - `?.` safe call returns null if receiver is null - `?:` Elvis operator provides default value - `!!` asserts non-null, throws NPE if wrong - `as?` safe cast returns null on failure - `?.let { }` executes block only if not null - Prefer safe calls over `!!` in production code - Smart casts work with immutable local variables

Comments

Comments powered by Giscus

To enable comments, add your Giscus embed code here.

Learn more about Giscus →