Bài 2.14. Cấu trúc ra quyết định
Cấu trúc rẽ nhánh (if-else và when) trong Kotlin là các công cụ điều khiển luồng chính, cho phép thực hiện các khối mã khác nhau dựa trên điều kiện. Chúng rất quan trọng trong việc xử lý logic điều kiện, như kiểm tra giá trị, xử lý đầu vào người dùng, hoặc điều hướng trong ứng dụng Android. Bài học này cung cấp cái nhìn chi tiết, chuyên nghiệp về if-else và when, bao gồm mục đích, cú pháp, ví dụ minh họa, lưu ý, lỗi thường gặp, và bài tập thực hành, giúp người học áp dụng hiệu quả trong các dự án thực tế.
📋 1. Nội dung bài học
🛠️ 2. Mục đích sử dụng
Cấu trúc rẽ nhánh trong Kotlin được sử dụng để:
- Điều khiển luồng chương trình: Thực hiện các khối mã khác nhau dựa trên điều kiện logic.
- Xử lý logic điều kiện: Kiểm tra giá trị đầu vào, trạng thái ứng dụng, hoặc dữ liệu từ API.
- Thay thế toán tử ba ngôi: Biểu thức
iftrong Kotlin có thể trả về giá trị, thay thế toán tử ba ngôi (?:) trong các ngôn ngữ khác. - Xử lý nhiều trường hợp: Biểu thức
whenhỗ trợ kiểm tra nhiều giá trị hoặc điều kiện một cách ngắn gọn và rõ ràng. - Tăng tính dễ đọc và bảo trì: Cả
ifvàwhengiúp tổ chức mã logic một cách dễ hiểu, đặc biệt trong các ứng dụng Android sử dụng Jetpack Compose.
🔀 3. Biểu thức if
Trong Kotlin, if là một biểu thức (expression), nghĩa là nó trả về một giá trị, không chỉ là một câu lệnh (statement). Điều này cho phép sử dụng if để gán giá trị hoặc trả về trực tiếp, thay thế toán tử ba ngôi (?:) trong Java/C++.
📝 3.1. Cú pháp tổng quát
if (condition) {
// Khối mã khi điều kiện đúng
} else {
// Khối mã khi điều kiện sai
}
- Trong đó:
condition: Biểu thức logic trả vềtruehoặcfalse.- Nếu chỉ có một lệnh trong khối
ifhoặcelse, có thể bỏ{}. - Khi sử dụng như biểu thức,
iftrả về giá trị của lệnh cuối cùng trong khối được thực thi. - Nhánh
elselà bắt buộc khiifđược dùng như biểu thức để gán giá trị hoặc trả về.
📊 3.2. Sơ đồ khối hoạt động
- Sơ đồ khối mô tả cấu trúc hoạt động của cấu trúc if-else:
💡 3.3. Ví dụ minh họa
3.3.1. So sánh hai số
Tìm giá trị lớn nhất giữa hai số, sử dụng if như biểu thức:
fun main() {
val a = 100
val b = 300
println("Giá trị max = ${findMax(a, b)}")
}
fun findMax(a: Int, b: Int): Int {
return if (a > b) a else b // if như biểu thức, thay thế toán tử ba ngôi
}
Kết quả:
Giá trị max = 300
3.3.2. Sử dụng khối mã
In thông báo và trả về giá trị lớn nhất:
fun main() {
val a = 100
val b = 300
println("Giá trị max = ${findMax(a, b)}")
}
fun findMax(a: Int, b: Int): Int {
val max = if (a > b) {
println("Chọn a là giá trị lớn nhất.")
a
} else {
println("Chọn b là giá trị lớn nhất.")
b
}
return max
}
Kết quả:
Chọn b là giá trị lớn nhất.
Giá trị max = 300
3.3.3. If lồng nhau
Kiểm tra số dương, âm, hoặc bằng 0:
fun main() {
val number = -5
val result = if (number > 0) {
"Số dương"
} else if (number < 0) {
"Số âm"
} else {
"Số 0"
}
println("Kết quả: $result")
}
Kết quả:
Kết quả: Số âm
⚠️ 3.4. Lưu ý và lỗi thường gặp
- Thiếu nhánh
elsekhi dùng như biểu thức: Nếuifđược dùng để gán giá trị hoặc trả về, nhánhelselà bắt buộc.// Sai: Thiếu else val max = if (a > b) a // Lỗi biên dịch: 'if' must have both main and 'else' branches - Nhầm lẫn giá trị trả về của khối: Giá trị của khối
ifhoặcelselà giá trị của lệnh cuối cùng trong khối.val result = if (a > b) { println("Lớn hơn") a // Giá trị trả về là a } else { println("Nhỏ hơn hoặc bằng") b // Giá trị trả về là b } - Sử dụng
{}không cần thiết: Nếu chỉ có một lệnh, bỏ{}để mã ngắn gọn hơn.// Không tối ưu if (a > b) { return a } else { return b } // Tốt hơn if (a > b) a else b - Nhầm lẫn với Java: Kotlin không có toán tử ba ngôi (
?:), nhưngifthay thế hoàn toàn chức năng này.
🔄 4. Biểu thức when
Biểu thức when trong Kotlin là một cách mạnh mẽ và linh hoạt để xử lý nhiều điều kiện, tương tự switch trong Java/C++, nhưng hiện đại hơn và hỗ trợ nhiều kiểu kiểm tra.
📝 4.1. Cú pháp tổng quát
when (expression) {
value1 -> // Khối mã cho value1
value2 -> // Khối mã cho value2
else -> // Khối mã mặc định
}
- Trong đó:
expression: Giá trị hoặc biểu thức cần kiểm tra (có thể bỏ qua nếu dùngwhennhư câu lệnh).value1,value2: Các giá trị hoặc biểu thức được so sánh vớiexpression.- Nhánh
elselà bắt buộc khiwhenlà biểu thức và không bao quát hết các trường hợp. - Mỗi nhánh có thể là một lệnh hoặc khối mã (
{}), với giá trị cuối cùng của khối là giá trị trả về.
📊 4.2. Sơ đồ khối hoạt động
- Sơ đồ khối minh họa biểu thức when:
💡 4.3. Ví dụ minh họa
4.3.1. When như câu lệnh
In tên tháng dựa trên số tháng:
fun main() {
val month = 4
printMonthName(month)
}
fun printMonthName(month: Int) {
when (month) {
1 -> println("Tháng Một")
2 -> println("Tháng Hai")
3 -> println("Tháng Ba")
4 -> println("Tháng Tư")
5 -> println("Tháng Năm")
else -> println("Không phải các tháng từ 1-5")
}
}
Kết quả:
Tháng Tư
4.3.2. When như biểu thức
Trả về tên mùa dựa trên tháng:
fun main() {
val month = 5
val season = printSeason(month)
println("Mùa: $season")
}
fun printSeason(month: Int): String {
return when (month) {
in 1..3 -> "Mùa Xuân"
in 4..6 -> "Mùa Hạ"
in 7..9 -> "Mùa Thu"
in 10..12 -> "Mùa Đông"
else -> "Tháng Không Hợp Lệ"
}
}
Kết quả:
Mùa: Mùa Hạ
4.3.3. When với enum
Sử dụng when với enum (không cần else nếu bao quát hết các trường hợp):
enum class Season {
SPRING, SUMMER, AUTUMN, WINTER
}
fun main() {
val season = Season.SUMMER
val seasonName = printSeason(season)
println("Mùa: $seasonName")
}
fun printSeason(season: Season): String {
return when (season) {
Season.SPRING -> "Mùa Xuân"
Season.SUMMER -> "Mùa Hạ"
Season.AUTUMN -> "Mùa Thu"
Season.WINTER -> "Mùa Đông"
}
}
Kết quả:
Mùa: Mùa Hạ
4.3.4. When với nhiều giá trị trong một nhánh
Kết hợp nhiều giá trị bằng dấu phẩy:
fun main() {
val month = 5
val season = printSeason(month)
println("Mùa: $season")
}
fun printSeason(month: Int): String {
return when (month) {
1, 2, 3 -> "Mùa Xuân"
4, 5, 6 -> "Mùa Hạ"
7, 8, 9 -> "Mùa Thu"
10, 11, 12 -> "Mùa Đông"
else -> "Tháng Không Hợp Lệ"
}
}
Kết quả:
Mùa: Mùa Hạ
4.3.5. When không có đối số
Thay thế chuỗi if-else-if:
fun main() {
val x = 5
val y = 10
val result = when {
x.isOdd() -> "x là số lẻ"
y.isEven() -> "y là số chẵn"
else -> "Tổng x + y là số lẻ"
}
println(result)
}
fun Int.isOdd() = this % 2 != 0
fun Int.isEven() = this % 2 == 0
Kết quả:
x là số lẻ
4.3.6. When với toán tử is và in
Kiểm tra kiểu hoặc phạm vi:
fun describe(obj: Any): String {
return when (obj) {
is Int -> "Là số nguyên: $obj"
in 1..10 -> "Trong khoảng 1-10"
is String -> "Là chuỗi: $obj"
else -> "Không xác định"
}
}
fun main() {
println(describe(5))
println(describe("Hello"))
println(describe(15))
}
Kết quả:
Là số nguyên: 5
Là chuỗi: Hello
Không xác định
⚠️ 4.4. Lưu ý và lỗi thường gặp
- Thiếu nhánh
elsekhi cần thiết: Khiwhenđược dùng như biểu thức, nhánhelselà bắt buộc trừ khi đối số làenumhoặcsealed classvà tất cả trường hợp đã được bao quát.// Sai: Thiếu else fun invalidWhen(month: Int): String { return when (month) { 1 -> "Tháng Một" 2 -> "Tháng Hai" // Lỗi biên dịch: 'when' expression must be exhaustive } } - Nhầm lẫn giá trị trả về của khối: Giá trị của nhánh
whenlà giá trị của lệnh cuối cùng trong khối.val result = when (month) { 1 -> { println("Tháng Một") "January" // Giá trị trả về } else -> "Unknown" } - Sử dụng
whenkhông cần thiết: Đối với các điều kiện đơn giản,ifcó thể ngắn gọn hơn. Chỉ dùngwhenkhi có nhiều nhánh. - Không tận dụng
inhoặcis: Sử dụng toán tửinhoặcisđể kiểm tra phạm vi hoặc kiểu, thay vì viết nhiều điều kiện riêng lẻ. - Nhầm lẫn với
switchtrong Java:whenlinh hoạt hơn, hỗ trợ biểu thức, phạm vi, và không yêu cầubreaknhưswitch.
⚠️ 5. Lưu ý và lỗi thường gặp
Ngoài các lỗi đã đề cập, dưới đây là các lưu ý chung khi sử dụng if và when:
- Hiệu suất với chuỗi điều kiện dài: Với nhiều điều kiện,
whenthường dễ đọc hơn chuỗiif-else-ifdài. Tuy nhiên, tránh lạm dụngwhencho các điều kiện đơn giản. - Kiểm tra null: Khi làm việc với các giá trị có thể null, sử dụng
ifhoặcwhenvớinullcheck.val str: String? = null val length = if (str != null) str.length else 0 - Sử dụng biểu thức thay vì câu lệnh: Ưu tiên dùng
ifvàwhennhư biểu thức để trả về giá trị, giúp mã ngắn gọn và dễ đọc. - Tính đầy đủ của
when: Với các kiểu nhưenumhoặcsealed class, đảm bảo bao quát tất cả trường hợp để tránh lỗi biên dịch hoặc logic. - Tránh lặp lại logic: Sử dụng biến tạm hoặc hàm phụ để tránh viết lại logic điều kiện trong nhiều nhánh.
📚 6. Bài tập thực hành
- Sau đây là nội dung bài các bài tập thực hành của bài học này:
Xem đầy đủ: nhấn vào đây
Lời giải mẫu: nhấn vào đây
Slide bài học: nhấn vào đây
✅ 7. Kết luận
Cấu trúc rẽ nhánh if-else và when trong Kotlin là những công cụ mạnh mẽ để xử lý logic điều kiện, từ các trường hợp đơn giản như so sánh hai số đến các kịch bản phức tạp như phân loại dữ liệu hoặc điều hướng trong ứng dụng Android. Việc hiểu rõ mục đích, cú pháp, và các lỗi thường gặp giúp bạn viết mã hiệu quả, dễ bảo trì, và tránh các vấn đề phổ biến như thiếu nhánh else hoặc logic không đầy đủ. Các ví dụ và bài tập thực hành trong bài học này được thiết kế để bạn áp dụng if và when vào các kịch bản thực tế, từ ứng dụng console đến các ứng dụng Android sử dụng Jetpack Compose. Hãy thực hành đều đặn để nắm vững kỹ năng này!



