When Expression trong Kotlin
🎯 Mục tiêu: Nắm vững
when- công cụ pattern matching mạnh mẽ của Kotlin, thay thế cho switch-case và if-else phức tạp.
💡 Khái niệm
when là phiên bản nâng cấp của switch trong Java/C, nhưng mạnh mẽ hơn nhiều:
- Có thể match nhiều loại điều kiện
- Có thể dùng như expression (trả về giá trị)
- Không cần
break - Hỗ trợ smart cast
val x = 2
when (x) {
1 -> println("Một")
2 -> println("Hai")
3 -> println("Ba")
else -> println("Khác")
}📝 Cú pháp cơ bản
When với giá trị cụ thể
val day = 3
val dayName = when (day) {
1 -> "Thứ Hai"
2 -> "Thứ Ba"
3 -> "Thứ Tư"
4 -> "Thứ Năm"
5 -> "Thứ Sáu"
6 -> "Thứ Bảy"
7 -> "Chủ Nhật"
else -> "Không hợp lệ"
}
println(dayName) // Thứ TưNhiều giá trị trong một branch
val char = 'a'
val type = when (char) {
'a', 'e', 'i', 'o', 'u' -> "Nguyên âm"
in 'a'..'z' -> "Phụ âm"
in 'A'..'Z' -> "Chữ hoa"
else -> "Khác"
}When với ranges
val score = 85
val grade = when (score) {
in 90..100 -> "A"
in 80 until 90 -> "B"
in 70 until 80 -> "C"
in 60 until 70 -> "D"
in 0 until 60 -> "F"
else -> "Invalid"
}⭐ When as Expression
val x = 3
val result = when (x) {
1 -> "One"
2 -> "Two"
else -> "Many"
}
// Với block
val description = when (x) {
1 -> {
println("X is one")
"Single"
}
2 -> {
println("X is two")
"Double"
}
else -> {
println("X is something else")
"Multiple"
}
}[!TIP] Khi dùng
whenas expression, phải cóelse(trừ khi compiler chứng minh được tất cả cases đã được cover).
🔍 When không có argument
Khi không có argument, when hoạt động như chuỗi if-else:
val age = 25
val hasLicense = true
val canDrive = when {
age < 18 -> "Chưa đủ tuổi"
!hasLicense -> "Chưa có bằng lái"
else -> "Được lái xe"
}So sánh với if-else
// if-else
val result = if (x > 0) {
"Positive"
} else if (x < 0) {
"Negative"
} else {
"Zero"
}
// when (rõ ràng hơn)
val result = when {
x > 0 -> "Positive"
x < 0 -> "Negative"
else -> "Zero"
}🎯 When với Type Checking (is)
fun describe(obj: Any): String = when (obj) {
is Int -> "Integer: ${obj * 2}" // Smart cast!
is String -> "String length: ${obj.length}"
is Boolean -> if (obj) "True" else "False"
is List<*> -> "List of ${obj.size} items"
else -> "Unknown: ${obj::class.simpleName}"
}
fun main() {
println(describe(42)) // Integer: 84
println(describe("Hello")) // String length: 5
println(describe(true)) // True
println(describe(listOf(1,2))) // List of 2 items
}🔒 Exhaustive When với Sealed Class & Enum
Enum - không cần else
enum class Direction { NORTH, SOUTH, EAST, WEST }
fun move(direction: Direction): String = when (direction) {
Direction.NORTH -> "Đi lên"
Direction.SOUTH -> "Đi xuống"
Direction.EAST -> "Đi phải"
Direction.WEST -> "Đi trái"
// Không cần else vì đã cover hết
}Sealed class - không cần else
sealed class Result {
data class Success(val data: String) : Result()
data class Error(val message: String) : Result()
object Loading : Result()
}
fun handleResult(result: Result): String = when (result) {
is Result.Success -> "Data: ${result.data}"
is Result.Error -> "Error: ${result.message}"
Result.Loading -> "Loading..."
// Không cần else!
}[!NOTE] Với sealed class và enum, compiler biết tất cả subclasses nên không cần
else. Nếu thêm subclass mới, compiler sẽ báo lỗi ở những chỗ chưa xử lý.
🛠️ Thực hành
Bài tập 1: Máy tính đơn giản
fun main() {
val a = 10
val b = 3
val operator = "/"
// TODO: Tính kết quả dựa trên operator
}Lời giải:
fun main() {
val a = 10.0
val b = 3.0
val operator = "/"
val result = when (operator) {
"+" -> a + b
"-" -> a - b
"*" -> a * b
"/" -> if (b != 0.0) a / b else Double.NaN
"%" -> a % b
else -> Double.NaN
}
println("$a $operator $b = $result")
}Bài tập 2: Phân loại BMI
fun main() {
val bmi = 23.5
// TODO: Phân loại theo BMI:
// < 18.5: Thiếu cân
// 18.5-24.9: Bình thường
// 25-29.9: Thừa cân
// >= 30: Béo phì
}Lời giải:
fun main() {
val bmi = 23.5
val category = when {
bmi < 18.5 -> "Thiếu cân"
bmi < 25.0 -> "Bình thường"
bmi < 30.0 -> "Thừa cân"
else -> "Béo phì"
}
println("BMI: $bmi → $category")
}Bài tập 3: Mô tả kiểu dữ liệu
fun main() {
val items = listOf(42, "Hello", 3.14, true, null, listOf(1, 2))
// TODO: In mô tả cho mỗi item
}Lời giải:
fun main() {
val items: List<Any?> = listOf(42, "Hello", 3.14, true, null, listOf(1, 2))
for (item in items) {
val description = when (item) {
null -> "Null value"
is Int -> "Integer: $item (doubled: ${item * 2})"
is String -> "String: \"$item\" (length: ${item.length})"
is Double -> "Double: ${"%.2f".format(item)}"
is Boolean -> "Boolean: ${if (item) "Yes" else "No"}"
is List<*> -> "List with ${item.size} elements"
else -> "Unknown type"
}
println(description)
}
}📱 Trong Android
// Xử lý navigation
when (menuItemId) {
R.id.nav_home -> navController.navigate(R.id.homeFragment)
R.id.nav_profile -> navController.navigate(R.id.profileFragment)
R.id.nav_settings -> navController.navigate(R.id.settingsFragment)
}
// Xử lý API response
when (response) {
is NetworkResult.Success -> showData(response.data)
is NetworkResult.Error -> showError(response.message)
is NetworkResult.Loading -> showLoading()
}
// View click handling
view.setOnClickListener { v ->
when (v.id) {
R.id.btnSubmit -> submitForm()
R.id.btnCancel -> cancel()
R.id.btnReset -> resetForm()
}
}⚠️ Lưu ý quan trọng
[!WARNING] Không có fall-through như switch trong Java:
// Kotlin - mỗi branch độc lập when (x) { 1 -> println("One") // Chỉ chạy khi x == 1 2 -> println("Two") // Chỉ chạy khi x == 2 } // Nếu muốn nhiều case cùng xử lý: when (x) { 1, 2 -> println("One or Two") }
[!CAUTION] Thứ tự các branch quan trọng:
val x = 5 // ❌ Sai - case cụ thể bị che when (x) { in 1..10 -> "1-10" // Luôn match trước! 5 -> "Five" // Không bao giờ chạy else -> "Other" } // ✅ Đúng - case cụ thể trước when (x) { 5 -> "Five" in 1..10 -> "1-10" else -> "Other" }
✅ Checklist - Tự kiểm tra
Sau bài học này, bạn có thể:
- Sử dụng when với giá trị cụ thể
- Sử dụng when với ranges (
in 1..10) - Sử dụng when với type checking (
is) - Sử dụng when không có argument
- Hiểu exhaustive when với sealed class/enum
- Biết when luôn cần
elsekhi dùng làm expression (trừ sealed/enum)
Tiếp theo: Vòng lặp for