Sealed Class là một loại lớp đặc biệt trong Kotlin giới hạn hệ thống phân cấp kế thừa thành một tập hợp cố định các kiểu con. Tất cả các kiểu con được khai báo trong cùng một tệp và được trình biên dịch biết đến, cho phép sử dụng khối when đầy đủ mà không cần nhánh else bắt buộc. Theo Kotlin Docs, 2026, các lớp sealed là cơ chế chính để biểu diễn các hệ thống phân cấp giới hạn như trạng thái, loại lỗi và sự kiện UI.
Những điểm chính
Sealed Class (lớp sealed) là một lớp trong Kotlin được đánh dấu bằng bổ từ sealed. Nó xác định một hệ thống phân cấp kiểu giới hạn: tất cả các kiểu con có thể được liệt kê trong cùng một tệp và trình biên dịch biết về từng kiểu. Điều này phân biệt sealed class với lớp mở thông thường có kiểu con có thể được khai báo ở bất kỳ đâu.
Mục đích chính của sealed class là biểu diễn an toàn kiểu của một tập hợp hữu hạn các biến thể. Mỗi kiểu con có thể có cấu trúc dữ liệu riêng, làm cho sealed class linh hoạt hơn enum. Trong thời gian chạy, sealed class là một lớp trừu tượng thông thường; trình biên dịch chỉ áp đặt các hạn chế tại thời điểm biên dịch.
Sealed class đặc biệt hữu ích trong kiến trúc ứng dụng Android: trạng thái UI, kết quả yêu cầu mạng, sự kiện điều hướng giống Intent, và tất nhiên hệ thống phân cấp lỗi là các trường hợp sử dụng điển hình.
Tại thời điểm biên dịch, sealed class được tối ưu hóa thành bảng nhảy cho biểu thức when, làm cho nó hiệu quả hơn chuỗi if-else. Kết hợp với data class, mỗi kiểu con có thể chứa không chỉ trạng thái mà còn cả phương thức, cho phép xây dựng các mô hình miền tự ghi chép mà không cần mã soạn sẵn.
Sealed class cũng hiệu quả để biểu diễn máy trạng thái trong ứng dụng di động. Mỗi trạng thái là một kiểu con riêng biệt với các tham số duy nhất và các chuyển đổi giữa các trạng thái được kiểm soát thông qua biểu thức when. Trình biên dịch đảm bảo rằng tất cả các trạng thái có thể đã được xử lý, loại bỏ lỗi thời gian chạy khi thay đổi trạng thái UI hoặc logic nghiệp vụ.
Các nhà phát triển Kotlin mới bắt đầu thường nhầm lẫn sealed class với enum, vì cả hai đều giới hạn tập hợp giá trị. Tuy nhiên, có một sự khác biệt cơ bản: enum là một tập hợp các hằng số cùng kiểu, trong khi sealed class là một hệ thống phân cấp các kiểu khác nhau.
Enum là tối ưu khi tất cả các biến thể là hằng số không có cấu trúc bổ sung. Ví dụ: các ngày trong tuần, trạng thái đơn hàng hoặc loại hành động không có tham số. Mỗi giá trị enum là một singleton với tên cố định.
Sealed class cần thiết khi mỗi biến thể có dữ liệu riêng. Ví dụ: lỗi mạng chứa mã phản hồi, lỗi phân tích chứa chi tiết và lỗi ủy quyền chứa thông báo. Mỗi kiểu con của sealed class là một kiểu riêng biệt với các trường duy nhất.
// Enum — tất cả biến thể của cùng một kiểu
enum class Status { LOADING, SUCCESS, ERROR }
// Sealed class — mỗi biến thể với dữ liệu riêng
sealed class UiState<out T> {
object Loading : UiState<Nothing>()
data class Success<T>(val data: T) : UiState<T>()
data class Error(val message: String) : UiState<Nothing>()
}
Từ Kotlin 1.5, có thể khai báo sealed interface. Điều này mở rộng khái niệm sealed cho các giao diện: sealed interface cũng có một tập hợp cố định các triển khai nhưng hỗ trợ đa kế thừa.
Sealed interface thuận tiện khi các kiểu con cần triển khai nhiều hợp đồng cùng lúc. Ví dụ: một sự kiện UI có thể vừa có thể nhấp vừa có thể theo dõi. Với sealed class, bạn phải chọn một lớp cơ sở; với sealed interface, kiểu con triển khai cả hai.
Sealed class là một lớp, vì vậy mỗi kiểu con chỉ có thể có một cha. Sealed interface giải quyết vấn đề này nhưng không thể chứa trạng thái. Sự lựa chọn giữa chúng phụ thuộc vào nhiệm vụ: cần logic chia sẻ với các trường — sử dụng sealed class; cần tính linh hoạt của hợp đồng — sử dụng sealed interface.
sealed interface ScreenEvent {
data class Refresh(val force: Boolean) : ScreenEvent
data class Navigate(val route: String) : ScreenEvent
data class ShowError(val toast: String) : ScreenEvent
}
sealed interface AnalyticsEvent {
val name: String
val params: Map<String, Any>
}
// Kiểu con triển khai cả hai giao diện
data class LoginClicked(
override val name: String = "login_click",
override val params: Map<String, Any> = emptyMap()
) : ScreenEvent, AnalyticsEvent
Một trong những ứng dụng chính của sealed class trong phát triển di động là hệ thống phân cấp lỗi an toàn kiểu. Thay vì ném các ngoại lệ thuộc các loại khác nhau hoặc sử dụng Exception chung, sealed class tập hợp tất cả các lỗi miền có thể thành một loại duy nhất.
Tạo một sealed class DomainError và liệt kê tất cả các loại thất bại dưới dạng kiểu con. Mỗi kiểu con chỉ chứa dữ liệu liên quan đến loại lỗi cụ thể đó. Trình biên dịch đảm bảo rằng khi xử lý lỗi, bạn sẽ không quên bất kỳ biến thể nào.
Xem xét một ứng dụng có ủy quyền nơi các kịch bản thất bại khác nhau có thể xảy ra: sai mật khẩu, tài khoản bị khóa, sự cố máy chủ. Sealed class kết hợp chúng thành một loại duy nhất với xử lý đầy đủ.
sealed class AuthError {
data class InvalidCredentials(
val attempts: Int
) : AuthError()
data class AccountBlocked(
val until: Long
) : AuthError()
data class NetworkFailure(
val cause: Throwable
) : AuthError()
object ServerError : AuthError()
}
fun handleError(error: AuthError): String = when (error) {
is AuthError.InvalidCredentials ->
"Số lần thử còn lại: ${3 - error.attempts}"
is AuthError.AccountBlocked ->
"Truy cập bị chặn đến ${Date(error.until)}"
is AuthError.NetworkFailure ->
"Kiểm tra kết nối: ${error.cause.localizedMessage}"
AuthError.ServerError ->
"Máy chủ tạm thời không khả dụng"
}
Sealed class đã trở thành một công cụ tiêu chuẩn trong kiến trúc ứng dụng Android. Hãy xem ba mẫu chính mà sealed class không thể thiếu trong phát triển di động.
Cũng đáng chú ý là việc sử dụng sealed class trong Clean Architecture. Mỗi lớp (data, domain, presentation) sử dụng sealed class cho các loại lỗi của mình và các mapper chuyển đổi một sealed class thành sealed class khác. Ví dụ: DataError từ lớp dữ liệu được ánh xạ thành DomainError cho logic nghiệp vụ, sau đó thành UiState cho lớp trình bày. Điều này bảo toàn tính an toàn kiểu ở tất cả các cấp của ứng dụng và đảm bảo không có lỗi nào không được xử lý.
Kiểm thử sealed class đòi hỏi một cách tiếp cận đặc biệt, vì mỗi kiểu con là một loại riêng biệt với trạng thái riêng. Nên viết các kiểm thử tham số hóa lặp qua tất cả các kiểu con của sealed class. Điều này đảm bảo rằng các biểu thức when bao phủ tất cả các biến thể, bao gồm cả những biến thể mới được thêm vào khi mở rộng hệ thống phân cấp.
Đối với kiểm thử UI, sealed class dưới dạng UiState cho phép kiểm tra hiển thị của từng trạng thái: Loading hiển thị spinner, Content hiển thị dữ liệu, Error hiển thị thông báo lỗi. Vì sealed class là hữu hạn, phạm vi kiểm thử của tất cả các trạng thái mang lại sự tin cậy hoàn toàn về tính chính xác của logic UI.
Mặc dù khái niệm đơn giản, các nhà phát triển thường mắc lỗi khi thiết kế hệ thống phân cấp sealed class. Hãy xem các vấn đề chính và cách tránh chúng.
Câu hỏi thường gặp
Có, sealed class có thể chứa các phương thức trừu tượng và mỗi kiểu con có nghĩa vụ triển khai chúng. Điều này thuận tiện khi tất cả các biến thể phải cung cấp một giao diện chung nhưng với logic thực thi khác nhau.
Trong Java 17+, các lớp và giao diện sealed với bổ từ sealed đã được giới thiệu. Android hiện hỗ trợ Java 17 một phần, nhưng trong các dự án Kotlin, sealed class có sẵn từ Kotlin 1.0 mà không có hạn chế.
Có, một sealed class có thể là kiểu con của một sealed class khác. Hệ thống phân cấp sealed class vẫn hữu hạn: trình biên dịch biết tất cả các kiểu con ở mỗi cấp. Điều này cho phép xây dựng các phân loại lỗi chi tiết.
Sealed class không tạo ra chi phí trong thời gian chạy. Trình biên dịch tối ưu hóa biểu thức when với sealed class thành bảng nhảy (tableswitch), nhanh hơn chuỗi if-else. Hiệu suất giống hệt enum.
Mỗi kiểu con của sealed class được kiểm thử riêng biệt. Vì sealed class là hữu hạn, bạn có thể viết một kiểm thử tham số hóa lặp qua tất cả các biến thể. Điều này cung cấp phạm vi bao phủ đầy đủ các nhánh của khối when.
Tổng kết
Chúng tôi sẽ phát triển ứng dụng di động chìa khóa trao tay
IT Sectr tạo các ứng dụng iOS và Android cho các công ty khởi nghiệp và doanh nghiệp từ năm 2017. Chúng tôi sẽ tư vấn và đề xuất giải pháp tốt nhất cho bạn.
Đọc thêm