Sealed Class เป็นคลาสชนิดพิเศษใน Kotlin ที่จำกัดลำดับชั้นการสืบทอดให้อยู่ในชุดย่อยที่。ซับคลาสทั้งหมดถูกประกาศในไฟล์เดียวกันและเป็นที่รู้จักของคอมไพเลอร์ ซึ่งช่วยให้สามารถใช้บล็อก when ที่ครอบคลุมโดยไม่ต้องมีสาขา else ที่บังคับ ตาม Kotlin Docs, 2026 คลาสซีลเป็นกลไกสำคัญสำหรับการแสดงลำดับชั้นที่จำกัด เช่น สถานะ ประเภทข้อผิดพลาด และเหตุการณ์ UI
ประเด็นสำคัญ
Sealed Class (คลาสซีล) คือคลาสใน Kotlin ที่ทำเครื่องหมายด้วยตัวปรับ sealed มันกำหนดลำดับชั้นของประเภทที่จำกัด: ซับคลาสที่เป็นไปได้ทั้งหมดจะถูกระบุในไฟล์เดียวกันและคอมไพเลอร์รู้จักแต่ละตัว สิ่งนี้แยก sealed class ออกจากคลาสเปิดปกติที่ซับคลาสสามารถประกาศได้ทุกที่
วัตถุประสงค์หลักของ sealed class คือ การแสดงชุดตัวเลือกที่จำกัดอย่าง Type-Safe แต่ละซับคลาสสามารถมีโครงสร้างข้อมูลของตัวเอง ทำให้ sealed class ยืดหยุ่นกว่า enum ในรันไทม์ sealed class เป็นคลาสนามธรรมปกติ คอมไพเลอร์กำหนดข้อจำกัดเฉพาะในเวลาคอมไพล์เท่านั้น
Sealed class มีประโยชน์อย่างยิ่งใน สถาปัตยกรรมแอป Android: สถานะ UI ผลลัพธ์คำขอเครือข่าย เหตุการณ์การนำทางคล้าย Intent และแน่นอนลำดับชั้นข้อผิดพลาดเป็นกรณีการใช้งานทั่วไป
ในเวลาคอมไพล์ sealed class จะถูกปรับให้เหมาะสมเป็นตารางกระโดดสำหรับนิพจน์ when ทำให้มีประสิทธิภาพมากกว่า chain if-else เมื่อรวมกับ data class แต่ละซับคลาสสามารถมีไม่เพียงแค่สถานะแต่ยังมีเมธอด ซึ่งช่วยให้สร้างโมเดลโดเมนที่บันทึกตนเองได้โดยไม่ต้องใช้โค้ด Boilerplate
Sealed class ยังมีประสิทธิภาพสำหรับการแสดง สถานะเครื่อง ในแอปพลิเคชันมือถือ แต่ละสถานะเป็นซับคลาสแยกต่างหากพร้อมพารามิเตอร์เฉพาะ และการเปลี่ยนระหว่างสถานะถูกควบคุมผ่านนิพจน์ when คอมไพเลอร์รับประกันว่าสถานะที่เป็นไปได้ทั้งหมดได้รับการจัดการแล้ว กำจัดข้อผิดพลาดรันไทม์เมื่อเปลี่ยนสถานะ UI หรือตรรกะทางธุรกิจ
นักพัฒนา Kotlin มือใหม่มักสับสนระหว่าง sealed class และ enum เนื่องจากทั้งคู่จำกัดชุดของค่า อย่างไรก็ตาม มีความแตกต่างพื้นฐาน: enum คือชุดของค่าคงที่ประเภทเดียวกัน ในขณะที่ sealed class คือลำดับชั้นของประเภทที่แตกต่างกัน
Enum เหมาะสมที่สุดเมื่อตัวเลือกทั้งหมดเป็นค่าคงที่โดยไม่มีโครงสร้างเพิ่มเติม ตัวอย่างเช่น วันในสัปดาห์ สถานะคำสั่งซื้อ หรือประเภทการดำเนินการโดยไม่มีพารามิเตอร์ ค่า enum แต่ละค่าคือซิงเกิลตันที่มีชื่อ
Sealed class จำเป็นเมื่อแต่ละตัวเลือกมีข้อมูลของตัวเอง ตัวอย่างเช่น ข้อผิดพลาดเครือข่ายมีรหัสตอบกลับ ข้อผิดพลาดการแจงส่วนมีรายละเอียด และข้อผิดพลาดการอนุญาตมีข้อความ แต่ละซับคลาสของ sealed class คือประเภทแยกต่างหากพร้อมฟิลด์เฉพาะ
// Enum — ตัวเลือกทั้งหมดประเภทเดียวกัน
enum class Status { LOADING, SUCCESS, ERROR }
// Sealed class — แต่ละตัวเลือกมีข้อมูลของตัวเอง
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>()
}
ตั้งแต่ Kotlin 1.5 เป็นต้นมา สามารถประกาศ sealed interface ได้ สิ่งนี้ขยายแนวคิดซีลไปยังอินเทอร์เฟซ: sealed interface ก็มีชุดการนำไปใช้งานที่เช่นกัน แต่รองรับการสืบทอดหลายรายการ
Sealed interface สะดวกเมื่อซับคลาสจำเป็นต้องนำสัญญาหลายรายการไปใช้พร้อมกัน ตัวอย่างเช่น เหตุการณ์ UI สามารถคลิกได้และติดตามได้พร้อมกัน ด้วย sealed class คุณต้องเลือกคลาสพื้นฐานหนึ่งคลาส แต่ด้วย sealed interface ซับคลาสนำไปใช้ทั้งสอง
Sealed class เป็นคลาส ดังนั้นแต่ละซับคลาสสามารถมีพาเรนต์ได้เพียงตัวเดียว Sealed interface แก้ปัญหานี้แต่ไม่สามารถเก็บสถานะได้ การเลือกระหว่างพวกเขาขึ้นอยู่กับงาน: หากต้องการตรรกะที่ใช้ร่วมกันพร้อมฟิลด์ — ใช้ sealed class; หากต้องการความยืดหยุ่นของสัญญา — ใช้ 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>
}
// ซับคลาสนำอินเทอร์เฟซทั้งสองไปใช้
data class LoginClicked(
override val name: String = "login_click",
override val params: Map<String, Any> = emptyMap()
) : ScreenEvent, AnalyticsEvent
หนึ่งในการใช้งานหลักของ sealed class ในการพัฒนาแอปพลิเคชันมือถือคือ ลำดับชั้นข้อผิดพลาดแบบ Type-Safe แทนที่จะโยนข้อยกเว้นประเภทต่าง ๆ หรือใช้ Exception ทั่วไป sealed class รวบรวมข้อผิดพลาดโดเมนที่เป็นไปได้ทั้งหมดไว้ในประเภทเดียว
สร้าง sealed class DomainError และระบุประเภทความล้มเหลวทั้งหมดเป็นซับคลาส แต่ละซับคลาสมีเฉพาะข้อมูลที่เกี่ยวข้องกับประเภทข้อผิดพลาดนั้นโดยเฉพาะ คอมไพเลอร์รับประกันว่าเมื่อจัดการข้อผิดพลาด คุณจะไม่ลืมตัวเลือกใด ๆ
พิจารณาแอปพลิเคชันที่มีการอนุญาตซึ่งสถานการณ์ความล้มเหลวต่าง ๆ เป็นไปได้: รหัสผ่านผิด บัญชีถูกบล็อก ปัญหาเซิร์ฟเวอร์ Sealed class รวมพวกเขาเป็นประเภทเดียวด้วยการจัดการที่ครอบคลุม
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 ->
"ความพยายามที่เหลือ: ${3 - error.attempts}"
is AuthError.AccountBlocked ->
"การเข้าถึงถูกบล็อกจนถึง ${Date(error.until)}"
is AuthError.NetworkFailure ->
"ตรวจสอบการเชื่อมต่อ: ${error.cause.localizedMessage}"
AuthError.ServerError ->
"เซิร์ฟเวอร์ไม่พร้อมใช้งานชั่วคราว"
}
Sealed class กลายเป็นเครื่องมือมาตรฐานในสถาปัตยกรรมแอปพลิเคชัน Android มาดูสามรูปแบบหลักที่ sealed class ขาดไม่ได้ในการพัฒนาแอปพลิเคชันมือถือ
นอกจากนี้ยังควรสังเกตการใช้ sealed class ใน Clean Architecture แต่ละชั้น (data, domain, presentation) ใช้ sealed class สำหรับประเภทข้อผิดพลาดของตัวเอง และแมปเปอร์แปลง sealed class หนึ่งเป็นอีกอันหนึ่ง ตัวอย่างเช่น DataError จากชั้นข้อมูลถูกแมปเป็น DomainError สำหรับตรรกะทางธุรกิจ จากนั้นเป็น UiState สำหรับชั้นนำเสนอ สิ่งนี้รักษาความปลอดภัยของประเภทในทุกระดับของแอปพลิเคชันและรับประกันว่าไม่มีข้อผิดพลาดใดที่ไม่ได้รับการจัดการ
การทดสอบ sealed class ต้องใช้แนวทางพิเศษ เนื่องจากแต่ละซับคลาสเป็นประเภทแยกต่างหากที่มีสถานะของตัวเอง แนะนำให้เขียนการทดสอบแบบพารามิเตอร์ที่วนซ้ำผ่านซับคลาสทั้งหมดของ sealed class สิ่งนี้รับประกันว่านิพจน์ when ครอบคลุมตัวเลือกทั้งหมด รวมถึงตัวเลือกใหม่ที่เพิ่มเมื่อขยายลำดับชั้น
สำหรับ การทดสอบ UI sealed class ในรูปแบบ UiState ช่วยให้ตรวจสอบการแสดงผลของแต่ละสถานะ: Loading แสดงสปินเนอร์ Content แสดงข้อมูล Error แสดงข้อความข้อผิดพลาด เนื่องจาก sealed class มีจำกัด ความครอบคลุมการทดสอบของทุกรัฐให้ความมั่นใจอย่างสมบูรณ์ในความถูกต้องของตรรกะ UI
แม้ว่าแนวคิดจะเรียบง่าย แต่นักพัฒนามักทำผิดพลาดเมื่อออกแบบลำดับชั้น sealed class มาดูปัญหาหลักและวิธีหลีกเลี่ยง
คำถามที่พบบ่อย
ได้ sealed class สามารถมีเมธอดนามธรรม และแต่ละซับคลาสจำเป็นต้องนำไปใช้ สิ่งนี้สะดวกเมื่อตัวเลือกทั้งหมดต้องให้อินเทอร์เฟสร่วมกันแต่มีตรรกะการดำเนินการที่แตกต่างกัน
ใน Java 17+ มีการแนะนำคลาสและอินเทอร์เฟซซีลด้วยตัวปรับ sealed ปัจจุบัน Android รองรับ Java 17 บางส่วน แต่ในโปรเจกต์ Kotlin sealed class พร้อมใช้งานตั้งแต่ Kotlin 1.0 โดยไม่มีข้อจำกัด
ได้ sealed class หนึ่งสามารถเป็นซับคลาสของอีกอันหนึ่งได้ ลำดับชั้น sealed class ยังคงจำกัด: คอมไพเลอร์รู้จักซับคลาสทั้งหมดในทุกระดับ สิ่งนี้ช่วยให้สร้างการจำแนกข้อผิดพลาดโดยละเอียดได้
Sealed class ไม่สร้างโอเวอร์เฮดในรันไทม์ คอมไพเลอร์ปรับนิพจน์ when กับ sealed class ให้เหมาะสมเป็นตารางกระโดด (tableswitch) ซึ่งเร็วกว่า chain if-else ประสิทธิภาพเหมือนกับ enum
ซับคลาสแต่ละตัวของ sealed class จะถูกทดสอบแยกกัน เนื่องจาก sealed class มีจำกัด คุณสามารถเขียนการทดสอบแบบพารามิเตอร์ที่วนซ้ำผ่านตัวเลือกทั้งหมด สิ่งนี้ให้ความครอบคลุมสมบูรณ์ของสาขาบล็อก when
สรุป
เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร
IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ
อ่านเพิ่มเติม