Kotlin เป็นภาษาหลักสำหรับการพัฒนา Android ตาม Google Android Docs (2025) กว่า 95% ของโปรเจกต์ Android ใหม่ใช้ Kotlin การทำความเข้าใจลักษณะเฉพาะของ Kotlin — data class, sealed class, coroutines และ scope functions — เป็นข้อกำหนดบังคับสำหรับนักพัฒนา Android
ประเด็นสำคัญ
Kotlin ขยายแนวคิดคลาสจาก Java data class, sealed class, object และ companion object — โครงสร้างที่ไม่มีใน Java หรือถูกนำไปใช้แตกต่างกัน
data class — คลาสที่สร้าง equals(), hashCode(), toString(), copy() และ componentN() อัตโนมัติ เหมาะสำหรับโมเดลข้อมูล (DTO, Entity) ข้อกำหนด: อย่างน้อยหนึ่งพารามิเตอร์ในคอนสตรักเตอร์หลัก val/var ไม่สามารถเป็น open/sealed/inner sealed class — คลาสที่มีลำดับชั้นคลาสย่อยแบบจำกัด คลาสย่อยทั้งหมดทราบในเวลาคอมไพล์และประกาศในไฟล์เดียวกัน นิพจน์ when กับ sealed class ไม่ต้องการสาขา else sealed interface (Kotlin 1.9+) — เหมือน sealed class แต่อนุญาตให้สืบทอดหลายรายการ object — ซิงเกิลตัน ประกาศด้วยคีย์เวิร์ด object (ไม่ใช่ class) การเริ่มต้นแบบขี้เกียจที่ปลอดภัยต่อเธรด companion object — สมาชิกแบบสแตติกของคลาส มีชื่อ (ค่าเริ่มต้นคือ Companion) สามารถ implement อินเทอร์เฟซและมี extension functions
| ชนิด | คำอธิบาย | การใช้งาน |
|---|---|---|
| data class | การสร้าง equals, hashCode, toString, copy อัตโนมัติ | โมเดลข้อมูล, DTO, Entity |
| sealed class | ลำดับชั้นแบบจำกัด, การตรวจสอบ when | สถานะ, ผลลัพธ์, UI State |
| sealed interface | sealed class + การสืบทอดหลายรายการ | เหตุการณ์, การดำเนินการ (Kotlin 1.9+) |
| object | ซิงเกิลตัน (เชิงประกาศ) | Factory, Utility, ค่าคงที่ |
| companion object | สมาชิกสแตติกของคลาส | Factory Method, ค่าคงที่สแตติก |
| enum class | การแจงนับด้วยค่าคงที่ | สถานะที่มีชุดคงที่ |
data class — สำหรับ 90% ของโมเดล sealed class — สำหรับ UI State และผลลัพธ์ object — สำหรับซิงเกิลตัน IT Sectr แนะนำ sealed class สำหรับสถานะหน้าจอทั้งหมด (Loading, Success, Error)
Sealed class — เครื่องมือทรงพลังสำหรับการจำลองสถานะ ตัวอย่าง: UiState<T> พร้อมคลาสย่อย Loading, Success(data: T), Error(message: String) นิพจน์ when รับประกันการจัดการทุกสถานะ Sealed interface — สำหรับเหตุการณ์และการดำเนินการที่ต้องการการนำไปใช้หลายรายการ Value class (Kotlin 1.5+) — ตัวห่อสำหรับค่าเดียวโดยไม่มีโอเวอร์เฮด ใช้สำหรับตัวระบุที่ปลอดภัยชนิด: @JvmInline value class UserId(val id: String)
Extension Function — การเพิ่มเมธอดให้กับคลาสที่มีอยู่โดยไม่ต้องสืบทอด fun String.isEmail(): Boolean Extension Property — เช่นเดียวกันสำหรับคุณสมบัติ (ไม่มี backing field) Inline Function — การแทรกเนื้อหาฟังก์ชันที่จุดเรียก — ไม่มีโอเวอร์เฮดการเรียก ใช้สำหรับ higher-order functions กับ lambda Higher-Order Function — ฟังก์ชันที่รับหรือส่งคืนฟังก์ชันอื่น Scope Functions — ห้าฟังก์ชันสำหรับทำงานกับบริบทของออบเจกต์: let, run, with, apply, also
let — บริบท it, ส่งคืนผลลัพธ์ lambda ใช้สำหรับการเรียก null-safe (?.let {}) apply — บริบท this, ส่งคืนบริบท สำหรับการกำหนดค่าออบเจกต์ run — บริบท this, ส่งคืนผลลัพธ์ lambda สำหรับการคำนวณกับบริบท also — บริบท it, ส่งคืนบริบท สำหรับผลข้างเคียง (การบันทึก) with — ไม่ใช่ extension, บริบท this, ส่งคืนผลลัพธ์ lambda สำหรับกลุ่มการดำเนินการบนออบเจกต์ Lambda — ฟังก์ชันนิรนาม Destructuring Declaration — การแยก data class เป็นตัวแปร: val (name, age) = person
Inline function กับ reified — วิธีเดียวในการรักษาชนิดเจเนอริกใน JVM ใช้สำหรับ: บิลเดอร์ที่ปลอดภัยชนิด (Gson.fromJson<T>()), การตรวจสอบชนิด (is T), การรับคลาส (T::class) Crossinline และ Noinline — ตัวปรับสำหรับพารามิเตอร์ lambda ในฟังก์ชัน inline crossinline — ห้าม non-local return, noinline — ห้ามการอินไลน์ lambda เฉพาะ IT Sectr แนะนำ inline + reified เฉพาะเมื่อทำงานกับชนิด สำหรับ higher-order functions ทั่วไป inline อาจเพิ่มขนาดไบต์โค้ด
Coroutines — เธรดน้ำหนักเบาสำหรับการเขียนโปรแกรมแบบอะซิงโครนัส ไม่บล็อกเธรด — ระงับ (suspend) suspend function — ฟังก์ชันที่สามารถระงับได้โดยไม่บล็อกเธรด สามารถเรียกได้จาก coroutine หรือ suspend function อื่นเท่านั้น
CoroutineScope — ขอบเขตสำหรับ coroutines ประกอบด้วย CoroutineContext (Job + Dispatcher) lifecycleScope — สำหรับ Activity/Fragment (รับรู้วงจรชีวิต) viewModelScope — สำหรับ ViewModel (ยกเลิกอัตโนมัติเมื่อ onCleared()) GlobalScope — ขอบเขตทั่วโลก (ไม่แนะนำในโปรดักชัน — หน่วยความจำรั่ว) Dispatchers: Dispatchers.Main (UI), Dispatchers.IO (เครือข่าย/ดิสก์), Dispatchers.Default (CPU-intensive), Dispatchers.Unconfined (ไม่แนะนำ) launch — เริ่ม coroutine: Job async — เริ่มพร้อมผลลัพธ์: Deferred<T> runBlocking — สะพานแบบบล็อก withContext — เปลี่ยน Dispatcher ภายใน suspend function
// ตัวอย่าง coroutines: การโหลดข้อมูลใน ViewModel
class MainViewModel : ViewModel() {
private val _state = MutableStateFlow<UiState>(UiState.Loading)
val state: StateFlow<UiState> = _state.asStateFlow()
fun loadData() {
viewModelScope.launch {
_state.value = UiState.Loading
try {
val data = withContext(Dispatchers.IO) {
repository.fetchData()
}
_state.value = UiState.Success(data)
} catch (e: Exception) {
_state.value = UiState.Error(e.message)
}
}
}
}
// คำขอแบบขนานกับ async
suspend fun loadCombinedData(): CombinedData = coroutineScope {
val users = async(Dispatchers.IO) { api.getUsers() }
val posts = async(Dispatchers.IO) { api.getPosts() }
CombinedData(users.await(), posts.await())
}viewModelScope.launch — การเริ่ม coroutine มาตรฐานใน ViewModel withContext(Dispatchers.IO) — สลับไปยังเธรด IO สำหรับคำขอเครือข่าย Dispatchers กำหนดว่า coroutine ดำเนินการบนพูลเธรดใด IT Sectr แนะนำ viewModelScope สำหรับ ViewModel และ lifecycleScope สำหรับ Activity/Fragment
reified — ตัวปรับชนิดในฟังก์ชัน inline อนุญาตให้ใช้ชนิด: T::class, is T, as T หากไม่มี reified เจเนอริกใน JVM จะถูกลบ (type erasure) inline fun + reified — การรวมกันที่ทรงพลังสำหรับ API ที่ปลอดภัยชนิด Destructuring Declaration — val (x, y) = point ทำงานสำหรับ data class และชนิดที่มี componentN() typealias — นามแฝงชนิด: typealias Callback = (String) -> Unit lateinit — การเริ่มต้น var แบบเลื่อนเวลา (สำหรับ dependency injection) ไม่ปลอดภัยต่อเธรด lazy — การเริ่มต้น val แบบขี้เกียจ (ปลอดภัยต่อเธรดโดยค่าเริ่มต้น) lazy(LazyThreadSafetyMode.NONE) — ไม่มีการซิงโครไนซ์ Extension function และ Extension property — การเพิ่มฟังก์ชันการทำงานโดยไม่ต้องสืบทอด Kotlin เป็นภาษาที่ extension functions แก้ไขงานยูทิลิตี้ได้ 90%
Delegated Properties — การมอบหมาย getter/setter ให้กับออบเจกต์อื่น ตัวมอบหมายมาตรฐาน: lazy, observable, vetoable, map ตัวมอบหมายแบบกำหนดเองผ่านตัวดำเนินการ getValue/setValue by — คีย์เวิร์ดการมอบหมาย: val name by lazy { loadName() } คุณสมบัติที่มอบหมายเป็นพื้นฐานสำหรับตัวมอบหมาย ViewModel (by viewModels()) IT Sectr แนะนำ lazy สำหรับการเริ่มต้นแบบเลื่อนเวลาและ Delegates.observable สำหรับการสังเกตการเปลี่ยนแปลง
| คุณสมบัติ | คำอธิบาย | ตัวอย่าง |
|---|---|---|
| reified | การรักษาชนิดเจเนอริกใน inline | inline fun <reified T> parse(): T |
| destructuring | การแยก data class | val (name, age) = person |
| typealias | นามแฝงชนิด | typealias Orders = List<Order> |
| lateinit | การเริ่มต้น var แบบเลื่อนเวลา | lateinit var adapter: Adapter |
| lazy | การเริ่มต้น val แบบขี้เกียจ | val config by lazy { load() } |
| delegated | การมอบหมายคุณสมบัติ | var x by Delegates.observable(0) |
reified — สำหรับ API ที่ปลอดภัยชนิด lazy — สำหรับการโหลดแบบเลื่อนเวลา delegated properties — สำหรับการใช้ตรรกะคุณสมบัติซ้ำ IT Sectr แนะนำให้เชี่ยวชาญคุณสมบัติทั้งหมดนี้เพื่อการพัฒนา Kotlin ที่มีประสิทธิภาพ
Flow — สตรีมข้อมูลอะซิงโครนัสแบบเย็นใน Kotlin ปล่อยค่าตามความต้องการของตัวรวบรวม StateFlow — Flow ร้อนที่มีค่าเดียวปัจจุบัน ใช้ใน ViewModel สำหรับ UI State SharedFlow — Flow ร้อนสำหรับเหตุการณ์ (เหตุการณ์ครั้งเดียว) flowOf, asFlow — การสร้าง Flow จากคอลเลกชัน catch, retry, debounce — ตัวดำเนินการ stateIn, shareIn — การแปลง Flow เย็นเป็นร้อน IT Sectr แนะนำ StateFlow สำหรับ UI State และ SharedFlow สำหรับเหตุการณ์ (Snackbar, การนำทาง)
class SearchViewModel : ViewModel() {
private val _query = MutableStateFlow("")
val suggestions: StateFlow<List<String>> = _query
.debounce(300)
.filter { it.length >= 2 }
.flatMapLatest { repository.search(it) }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), emptyList())
fun onQueryChanged(query: String) {
_query.value = query
}
}kotlinx.serialization — ไลบรารี Kotlin สำหรับการทำให้ JSON เป็นอนุกรม @Serializable — คำอธิบายประกอบ @SerialName — ชื่อฟิลด์แบบกำหนดเอง รองรับ: JSON, CBOR, ProtoBuf Parcelize — @Parcelize สำหรับ Android Parcelable (โดยไม่ต้อง implement ด้วยตนเอง) Gson vs Moshi vs kotlinx: kotlinx.serialization — Kotlin-first, ปลอดภัยตอนคอมไพล์, ไม่ต้องใช้ reflection Moshi — คำอธิบายประกอบ + codegen IT Sectr แนะนำ kotlinx.serialization สำหรับโปรเจกต์ใหม่และ Moshi สำหรับโปรเจกต์ที่มีอยู่
@Serializable
data class ApiResponse<T>(
val success: Boolean,
@SerialName("data") val data: T? = null,
val error: String? = null
)
@Serializable
data class User(
val id: Int,
val name: String,
val email: String
)
// การดีซีเรียลไลซ์
val json = Json { ignoreUnknownKeys = true }
val response = json.decodeFromString<ApiResponse<List<User>>>(jsonString)
println("Users: ${response.data?.size ?: 0}")คำถามที่พบบ่อย
data class — สำหรับข้อมูล (สร้าง equals/hashCode/copy อัตโนมัติ) sealed class — สำหรับลำดับชั้นชนิด (ตรวจสอบใน when) sealed interface — เหมือนกันพร้อมการสืบทอดหลายรายการ
let, run, with, apply, also แตกต่างกันตามบริบท (it vs this) และค่าที่ส่งคืน (result vs context) let — null safety, apply — การกำหนดค่า, also — ผลข้างเคียง
CoroutineScope — ขอบเขตของ coroutines lifecycleScope — สำหรับ Activity, viewModelScope — สำหรับ ViewModel GlobalScope — ไม่แนะนำ
reified — การรักษาชนิดในฟังก์ชัน inline อนุญาต T::class และ is T ทำงานเฉพาะกับฟังก์ชัน inline
launch — ไม่มีผลลัพธ์ async — มีผลลัพธ์ (Deferred) runBlocking — สะพานบล็อกสำหรับ main/test withContext — เปลี่ยน Dispatcher
สรุป
เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร
IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ