🏷️ Enum Class
🎯 Mục tiêu: Hiểu Enum Class - tập hợp các hằng số có tên, với properties, methods, và exhaustive pattern matching.
💡 Enum là gì?
Phần tiêu đề “💡 Enum là gì?”Enum (Enumeration) định nghĩa một tập hợp cố định các giá trị với tên có ý nghĩa. Thay vì dùng magic numbers hoặc strings, dùng enum để code rõ ràng và type-safe.
// ❌ Bad: Magic numbersfun getDirection(code: Int): String = when (code) { 0 -> "North" 1 -> "South" 2 -> "East" 3 -> "West" else -> "Unknown"}
// ✅ Good: Enumenum class Direction { NORTH, SOUTH, EAST, WEST}
fun getDirection(direction: Direction): String = when (direction) { Direction.NORTH -> "North" Direction.SOUTH -> "South" Direction.EAST -> "East" Direction.WEST -> "West" // Không cần else - compiler đảm bảo đã cover hết!}📝 Cú pháp cơ bản
Phần tiêu đề “📝 Cú pháp cơ bản”enum class Day { MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY}
fun main() { val today = Day.FRIDAY
println(today) // FRIDAY println(today.name) // FRIDAY (String) println(today.ordinal) // 4 (index, zero-based)}Built-in Properties và Methods
Phần tiêu đề “Built-in Properties và Methods”enum class Season { SPRING, SUMMER, FALL, WINTER }
fun main() { val season = Season.SUMMER
// Properties println(season.name) // SUMMER println(season.ordinal) // 1
// Get all values Season.values().forEach { println(it) }
// Kotlin 1.9+ (recommended) Season.entries.forEach { println(it) }
// Parse from String val parsed = Season.valueOf("WINTER") println(parsed) // WINTER
// Safe parse (Kotlin 1.9+) val safe = enumValueOf<Season>("WINTER")}🔧 Enum với Properties
Phần tiêu đề “🔧 Enum với Properties”Mỗi enum constant có thể có properties riêng:
enum class Color(val hex: String, val rgb: Triple<Int, Int, Int>) { RED("#FF0000", Triple(255, 0, 0)), GREEN("#00FF00", Triple(0, 255, 0)), BLUE("#0000FF", Triple(0, 0, 255)), WHITE("#FFFFFF", Triple(255, 255, 255)), BLACK("#000000", Triple(0, 0, 0));
// Computed property val isLight: Boolean get() = rgb.first + rgb.second + rgb.third > 382}
fun main() { val color = Color.RED println("${color.name}: ${color.hex}") // RED: #FF0000 println("RGB: ${color.rgb}") // RGB: (255, 0, 0) println("Is light: ${color.isLight}") // Is light: false
// Find by property val whiteColor = Color.entries.find { it.hex == "#FFFFFF" } println(whiteColor) // WHITE}Ví dụ thực tế: HTTP Status
Phần tiêu đề “Ví dụ thực tế: HTTP Status”enum class HttpStatus(val code: Int, val description: String) { OK(200, "Success"), CREATED(201, "Resource created"), BAD_REQUEST(400, "Bad request"), UNAUTHORIZED(401, "Unauthorized"), NOT_FOUND(404, "Not found"), INTERNAL_ERROR(500, "Internal server error");
val isSuccess: Boolean get() = code in 200..299 val isClientError: Boolean get() = code in 400..499 val isServerError: Boolean get() = code in 500..599
companion object { fun fromCode(code: Int): HttpStatus? = entries.find { it.code == code } }}
fun main() { val status = HttpStatus.fromCode(404) println(status?.description) // Not found println(status?.isClientError) // true}🎯 Enum với Abstract Methods
Phần tiêu đề “🎯 Enum với Abstract Methods”Mỗi constant có thể có implementation khác nhau:
enum class Operation { ADD { override fun apply(a: Int, b: Int) = a + b override val symbol = "+" }, SUBTRACT { override fun apply(a: Int, b: Int) = a - b override val symbol = "-" }, MULTIPLY { override fun apply(a: Int, b: Int) = a * b override val symbol = "*" }, DIVIDE { override fun apply(a: Int, b: Int) = if (b != 0) a / b else throw ArithmeticException() override val symbol = "/" };
abstract fun apply(a: Int, b: Int): Int abstract val symbol: String
fun format(a: Int, b: Int): String = "$a $symbol $b = ${apply(a, b)}"}
fun main() { println(Operation.ADD.apply(5, 3)) // 8 println(Operation.MULTIPLY.format(5, 3)) // 5 * 3 = 15
Operation.entries.forEach { op -> println(op.format(10, 2)) } // 10 + 2 = 12 // 10 - 2 = 8 // 10 * 2 = 20 // 10 / 2 = 5}🔀 Exhaustive when
Phần tiêu đề “🔀 Exhaustive when”Khi dùng enum trong when expression, Kotlin đảm bảo bạn phải xử lý tất cả cases:
enum class PaymentStatus { PENDING, PROCESSING, COMPLETED, FAILED, REFUNDED }
fun handlePayment(status: PaymentStatus): String = when (status) { PaymentStatus.PENDING -> "Waiting for payment" PaymentStatus.PROCESSING -> "Processing payment..." PaymentStatus.COMPLETED -> "Payment successful" PaymentStatus.FAILED -> "Payment failed" PaymentStatus.REFUNDED -> "Payment refunded" // ✅ Không cần else - compiler biết đã cover hết!}
// Nếu add new enum value sau này, compiler sẽ báo lỗi ở mọi when// → Không bao giờ miss case mới!🛠️ Thực hành
Phần tiêu đề “🛠️ Thực hành”Bài tập: Tạo Priority enum
// TODO: Tạo enum Priority với:// - LOW, MEDIUM, HIGH, CRITICAL// - level (Int): 1, 2, 3, 4// - color (String): hex color// - method isUrgent(): Boolean (HIGH hoặc CRITICAL)Lời giải:
enum class Priority(val level: Int, val color: String) { LOW(1, "#4CAF50"), // Green MEDIUM(2, "#FFC107"), // Yellow HIGH(3, "#FF9800"), // Orange CRITICAL(4, "#F44336"); // Red
fun isUrgent(): Boolean = this in listOf(HIGH, CRITICAL)
companion object { fun fromLevel(level: Int): Priority? = entries.find { it.level == level }
fun parseOrDefault(name: String, default: Priority = MEDIUM): Priority { return entries.find { it.name.equals(name, ignoreCase = true) } ?: default } }}
fun main() { val priority = Priority.HIGH println("${priority.name}: Level ${priority.level}") // HIGH: Level 3 println("Is urgent: ${priority.isUrgent()}") // Is urgent: true println("Color: ${priority.color}") // Color: #FF9800
// Usage val tasks = listOf( "Fix crash" to Priority.CRITICAL, "Update UI" to Priority.LOW, "Performance" to Priority.MEDIUM )
tasks.filter { it.second.isUrgent() } .forEach { println("🔥 Urgent: ${it.first}") } // 🔥 Urgent: Fix crash}📱 Trong Android
Phần tiêu đề “📱 Trong Android”// Network stateenum class NetworkState { UNKNOWN, CONNECTED, DISCONNECTED, WIFI, MOBILE}
// View visibilityenum class Visibility(val value: Int) { VISIBLE(View.VISIBLE), INVISIBLE(View.INVISIBLE), GONE(View.GONE);
fun applyTo(view: View) { view.visibility = value }}
// Navigationenum class MainScreen(val route: String, val title: String, val icon: Int) { HOME("home", "Home", R.drawable.ic_home), SEARCH("search", "Search", R.drawable.ic_search), PROFILE("profile", "Profile", R.drawable.ic_profile), SETTINGS("settings", "Settings", R.drawable.ic_settings)}
// Themeenum class AppTheme { LIGHT, DARK, SYSTEM;
fun getMode(): Int = when (this) { LIGHT -> AppCompatDelegate.MODE_NIGHT_NO DARK -> AppCompatDelegate.MODE_NIGHT_YES SYSTEM -> AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM }}⚠️ Lưu ý quan trọng
Phần tiêu đề “⚠️ Lưu ý quan trọng”✅ Checklist - Tự kiểm tra
Phần tiêu đề “✅ Checklist - Tự kiểm tra”Sau bài học này, bạn có thể:
- Tạo enum class cơ bản
- Thêm properties cho mỗi enum constant
- Thêm abstract methods với implementation khác nhau
- Sử dụng
whenexhaustive với enum - Sử dụng
entriesđể iterate qua enum values - Tạo companion object methods (parse, find)
- Phân biệt khi nào dùng enum vs sealed class
Tiếp theo: Sealed Class