Skip to Content
Kotlin📘 Ngôn ngữ KotlinNull Safety và Nullable Types

Null Safety trong Kotlin

1. Giới thiệu

Null Safety là một trong những tính năng quan trọng và đặc biệt nhất của Kotlin, giúp loại bỏ NullPointerException (NPE) - lỗi phổ biến nhất trong Java.

Trong Python, bạn có thể gặp lỗi NoneType:

name = None print(name.upper()) # AttributeError: 'NoneType' object has no attribute 'upper'

Kotlin giải quyết vấn đề này ngay tại thời điểm biên dịch!

2. Nullable vs Non-Nullable Types

2.1. Non-Nullable (Không thể null)

Mặc định, tất cả các biến trong Kotlin không thểnull:

var name: String = "Kotlin" // name = null // Lỗi biên dịch: Null can not be a value of a non-null type String

2.2. Nullable (Có thể null)

Để cho phép biến có giá trị null, thêm dấu ? sau kiểu dữ liệu:

var name: String? = "Kotlin" name = null // OK

3. Làm việc với Nullable Types

3.1. Kiểm tra null trước khi sử dụng

var name: String? = "Alice" // Cách 1: Kiểm tra if truyền thống if (name != null) { println(name.length) // OK, Kotlin biết name không null } // Cách 2: Sử dụng if expression val length = if (name != null) name.length else 0

3.2. Safe Call Operator ?.

Toán tử ?. chỉ thực thi nếu giá trị không null:

var name: String? = "Alice" // Cách thông thường if (name != null) { println(name.length) } // Sử dụng safe call println(name?.length) // In ra length nếu name != null, ngược lại in ra null

Ví dụ phức tạp hơn:

var person: Person? = getPerson() // Chuỗi safe call val city = person?.address?.city?.uppercase() // Tương đương với: var city: String? = null if (person != null) { if (person.address != null) { if (person.address.city != null) { city = person.address.city.uppercase() } } }

3.3. Elvis Operator ?:

Toán tử Elvis cung cấp giá trị mặc định khi giá trị là null:

var name: String? = null val length = name?.length ?: 0 // Nếu name null, trả về 0 println(length) // 0 // Ví dụ khác val displayName = name ?: "Guest" println(displayName) // "Guest"

Kết hợp safe call và Elvis:

val result = person?.name?.uppercase() ?: "UNKNOWN"

3.4. Not-Null Assertion Operator !!

Toán tử !! cho Kotlin biết bạn chắc chắn giá trị không null. Nếu null, sẽ throw NPE:

var name: String? = "Alice" val length = name!!.length // OK, length = 5 name = null val length2 = name!!.length // Ném ra KotlinNullPointerException!

Cảnh báo:

  • Tránh sử dụng !! trừ khi bạn hoàn toàn chắc chắn
  • Đây là cách duy nhất gây ra NPE trong Kotlin
  • Nên sử dụng ?. hoặc ?: thay thế

3.5. Safe Cast as?

Chuyển đổi kiểu an toàn, trả về null nếu không thành công:

val obj: Any = "Hello" val str: String? = obj as? String // OK, str = "Hello" val num: Int? = obj as? Int // null (không throw exception) // So sánh với cast thông thường val num2: Int = obj as Int // ClassCastException!

4. Let function với Nullable

Function let rất hữu ích khi làm việc với nullable:

var name: String? = "Alice" name?.let { // Code block này chỉ chạy nếu name != null // 'it' là giá trị non-null của name println("Name length: ${it.length}") println("Uppercase: ${it.uppercase()}") }

Ví dụ thực tế:

fun processUser(userId: String?) { userId?.let { // Chỉ xử lý khi userId không null val user = findUser(it) user?.let { u -> sendEmail(u.email) updateLastLogin(u.id) } } }

5. Nullable Collections

5.1. Nullable vs Collection of Nullables

// List có thể null val list1: List<String>? = null // List không null, nhưng chứa các phần tử nullable val list2: List<String?> = listOf("A", null, "B") // List có thể null VÀ chứa các phần tử nullable val list3: List<String?>? = null

5.2. Filter Not Null

val list: List<String?> = listOf("A", null, "B", null, "C") val nonNullList: List<String> = list.filterNotNull() println(nonNullList) // [A, B, C]

6. Platform Types

Khi làm việc với Java, Kotlin không biết kiểu có nullable hay không. Đây gọi là Platform Types (ký hiệu Type!):

// Code Java public String getName() { return null; } // Code Kotlin val name = javaObject.getName() // Platform type String! // Bạn phải tự quyết định xử lý null

7. So sánh với Python

KotlinPythonGhi chú
String?Optional[str]Nullable type
name?.lengthname.length if name else NoneSafe call
name ?: "default"name or "default"Elvis operator
name!!name (không kiểm tra)Not-null assertion

8. Ví dụ thực tế

data class User( val id: Int, val name: String, val email: String?, val phone: String? ) fun main() { val user = User(1, "Alice", "alice@example.com", null) // Safe call với Elvis operator val email = user.email ?: "No email" val phone = user.phone ?: "No phone" println("Email: $email") // Email: alice@example.com println("Phone: $phone") // Phone: No phone // Sử dụng let user.email?.let { println("Sending email to: $it") } // Chaining safe calls val emailLength = user.email?.trim()?.length ?: 0 println("Email length: $emailLength") // Filter nullable list val users = listOf( User(1, "Alice", "alice@email.com", null), User(2, "Bob", null, "123456"), User(3, "Charlie", "charlie@email.com", "789012") ) val usersWithEmail = users.filter { it.email != null } println("Users with email: ${usersWithEmail.size}") }

9. Best Practices

  1. Ưu tiên non-nullable types khi có thể
  2. Sử dụng ?. thay vì !! trong hầu hết trường hợp
  3. Kết hợp ?. với ?: để có giá trị mặc định
  4. Sử dụng let để xử lý nullable một cách elegant
  5. Tránh !! trừ khi bạn 100% chắc chắn
  6. Kiểm tra null sớm trong function với require() hoặc check()
fun processUser(user: User?) { requireNotNull(user) { "User cannot be null" } // Từ đây trở đi, user là non-null println(user.name) }

10. Null Safety trong Function

// Return nullable fun findUser(id: Int): User? { return if (id > 0) User(id, "User $id") else null } // Parameter nullable fun greet(name: String?) { println("Hello, ${name ?: "Guest"}") } // Sử dụng val user = findUser(1) user?.let { greet(it.name) } ?: greet(null)

📝 Tóm tắt

  • Null Safety là tính năng đặc trưng của Kotlin
  • Non-nullable (mặc định): String
  • Nullable: String? (thêm dấu ?)
  • Safe call: ?. - gọi an toàn
  • Elvis operator: ?: - giá trị mặc định
  • Not-null assertion: !! - ném NPE nếu null (tránh dùng)
  • Safe cast: as? - chuyển kiểu an toàn
  • Let function: ?.let { } - xử lý khi không null
  • Null Safety giúp loại bỏ NPE ngay tại compile-time
Last updated on