0Pricing
Kotlin Academy · บทเรียน

กลไก expect/actual สำหรับ API ของแพลตฟอร์ม

ประกาศสิ่งที่คาดว่าจะมีในโค้ดส่วนกลางและจัดเตรียมการใช้งานจริงแยกตามแพลตฟอร์ม

กลไก expect/actual สำหรับ API ของแพลตฟอร์ม เป็นบทเรียน Kotlin Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 2 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Kotlin Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Kotlin Academy มีบทเรียนทั้งหมด 4 บทเรียน

รูปแบบ expect/actual

expect ประกาศสัญญา API ใน commonMain แต่ละแพลตฟอร์มจะจัดเตรียมการใช้งาน actual คอมไพเลอร์จะตรวจสอบว่าทุกเป้าหมายมี actual

// commonMain:
expect fun platformName(): String

// androidMain:
actual fun platformName(): String = "Android ${android.os.Build.VERSION.SDK_INT}"

// iosMain:
actual fun platformName(): String = UIDevice.currentDevice.systemName()

expect class

expect class ประกาศคลาสพร้อม API ของคลาสนั้นใน commonMain แต่ละแพลตฟอร์มจะจัดเตรียมการใช้งานครบถ้วน

// commonMain:
expect class DateTime {
    fun format(pattern: String): String
    val timestamp: Long
}

// androidMain:
actual class DateTime {
    actual val timestamp = System.currentTimeMillis()
    actual fun format(pattern: String): String =
        java.text.SimpleDateFormat(pattern).format(java.util.Date(timestamp))
}

expect object

expect object ประกาศซิงเกิลตันพร้อมสัญญาของมัน เหมาะสำหรับยูทิลิตีเฉพาะแพลตฟอร์ม เช่น การบันทึกข้อมูล การตั้งค่า หรือการวิเคราะห์

// commonMain:
expect object Logger {
    fun debug(msg: String)
    fun error(msg: String, cause: Throwable? = null)
}

// androidMain:
actual object Logger {
    actual fun debug(msg: String) = android.util.Log.d("App", msg)
    actual fun error(msg: String, cause: Throwable?) =
        android.util.Log.e("App", msg, cause)
}

expect val / fun ระดับบนสุด

expect val และ expect fun ระดับบนสุดเรียบง่าย และเหมาะกับยูทิลิตีแพลตฟอร์มที่ไม่มีสถานะ

// commonMain:
expect val isDebugBuild: Boolean
expect fun generateUUID(): String

// androidMain:
actual val isDebugBuild: Boolean = BuildConfig.DEBUG
actual fun generateUUID(): String = java.util.UUID.randomUUID().toString()

// iosMain:
actual val isDebugBuild: Boolean = false // or a native flag
actual fun generateUUID(): String = NSUUID().UUIDString()

expect fun พร้อมการใช้งานเริ่มต้น

ตั้งแต่ Kotlin 1.9 เป็นต้นไป expect fun สามารถมีการใช้งานเริ่มต้นผ่าน expect fun f() { } ได้ แพลตฟอร์มต่าง ๆ จะเขียนทับหรือใช้การใช้งานเริ่มต้นนี้ก็ได้

// commonMain (Kotlin 1.9+):
expect fun log(msg: String) {
    println("[DEFAULT] $msg") // used if no actual overrides
}

// androidMain:
actual fun log(msg: String) = android.util.Log.d("KMP", msg)

// iosMain (uses default):
// no actual needed — default is used

Actual Typealias

หากแพลตฟอร์มมีคลาสที่ตรงกับความต้องการอยู่แล้ว ให้ใช้ actual typealias เพื่อชี้ไปยังคลาสนั้นแทนการสร้างคลาสห่อหุ้ม

// commonMain:
expect class AtomicInt {
    fun get(): Int
    fun incrementAndGet(): Int
}

// androidMain / jvmMain:
actual typealias AtomicInt = java.util.concurrent.atomic.AtomicInteger

// iosMain:
actual class AtomicInt {
    private var value = 0
    actual fun get() = value
    actual fun incrementAndGet() = ++value
}

expect interface (รูปแบบทางอ้อม)

หากต้องการความยืดหยุ่นมากขึ้น ให้ประกาศอินเทอร์เฟซใน commonMain และใช้ expect/actual เฉพาะกับโรงงานที่สร้างอินสแตนซ์ของแพลตฟอร์ม

// commonMain:
interface FileStorage {
    suspend fun readFile(path: String): ByteArray
    suspend fun writeFile(path: String, data: ByteArray)
}
expect fun createFileStorage(): FileStorage

// androidMain:
actual fun createFileStorage(): FileStorage = AndroidFileStorage()

// iosMain:
actual fun createFileStorage(): FileStorage = IosFileStorage()

การทดสอบด้วย expect/actual

ใน commonTest ให้ทดสอบ API ที่ใช้ร่วมกัน การใช้งาน actual จะทำงานระหว่างงานทดสอบเฉพาะแพลตฟอร์ม (testDebugUnitTest บน Android และ iosX64Test บน iOS)

// commonTest:
import kotlin.test.Test
import kotlin.test.assertNotNull
class PlatformTest {
    @Test
    fun testUUID() {
        val uuid = generateUUID()
        assertNotNull(uuid)
        println("UUID: $uuid")
    }
}

ข้อผิดพลาดที่พบบ่อย

การประกาศ expect ทุกตัวต้องมี actual ใน ALL เป้าหมายที่กำหนดค่าไว้ มิฉะนั้นการสร้างโปรเจกต์จะล้มเหลว ระบบจะตรวจพบ actual ที่ขาดหายไปขณะคอมไพล์

// If you add a new target (e.g., jvmMain), you must add:
// actual fun platformName(): String = "JVM"
// Otherwise:
// error: Expected function platformName has no actual declaration in module :shared for JVM
fun main() { println("Missing actuals = compile error") }

คลาสคำอธิบายประกอบ expect

expect annotation class ช่วยให้แมปคำอธิบายประกอบเฉพาะแพลตฟอร์มได้ เช่น การแมปคำอธิบายประกอบที่ใช้ร่วมกันซึ่งคล้ายกับ @Parcelize ไปยังปลั๊กอิน Parcelize ของ Android

// commonMain:
expect annotation class CommonParcelize()

// androidMain:
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.BINARY)
actual annotation class CommonParcelize

// iosMain:
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.BINARY)
actual annotation class CommonParcelize

กรณีใช้งานจริง: Keychain/Preferences

ตัวอย่าง expect/actual ในโลกจริงคืออินเทอร์เฟซพื้นที่จัดเก็บข้อมูลปลอดภัย โดย Android ใช้ EncryptedSharedPreferences และ iOS ใช้ Keychain

// commonMain:
expect class SecureStorage {
    fun save(key: String, value: String)
    fun get(key: String): String?
}

// androidMain:
actual class SecureStorage {
    private val prefs = /* EncryptedSharedPreferences */ mapOf<String,String>()
    actual fun save(key: String, value: String) { /* ... */ }
    actual fun get(key: String): String? = null // simplified
}

ตรวจสอบความเข้าใจ

หากการประกาศ expect ไม่มี actual สำหรับเป้าหมายหนึ่ง จะเกิดอะไรขึ้นขณะคอมไพล์

สรุปทบทวน

expect ประกาศ API ใน commonMain ส่วน actual จัดเตรียมการใช้งานบนแพลตฟอร์ม ใช้ actual typealias เมื่อแพลตฟอร์มมีชนิดข้อมูลที่ต้องการอยู่แล้ว ระบบจะตรวจพบ actual ที่ขาดหายไปขณะคอมไพล์ จึงไม่มีปัญหาที่คาดไม่ถึงขณะรันไทม์

คำถามที่พบบ่อย

บทเรียน “กลไก expect/actual สำหรับ API ของแพลตฟอร์ม” ฟรีหรือไม่

ใช่ — ข้อความเต็มของ “กลไก expect/actual สำหรับ API ของแพลตฟอร์ม” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Kotlin Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Kotlin Academy มีบทเรียนทั้งหมด 4 บทเรียน

คุณจะเรียนรู้อะไรในบทเรียน “กลไก expect/actual สำหรับ API ของแพลตฟอร์ม”

ประกาศสิ่งที่คาดว่าจะมีในโค้ดส่วนกลางและจัดเตรียมการใช้งานจริงแยกตามแพลตฟอร์ม คุณปฏิบัติ Kotlin Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน

คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Kotlin Academy หรือไม่

ไม่จำเป็นต้องมีประสบการณ์มาก่อน Kotlin Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 2 จากทั้งหมด 4 บทเรียน

บทเรียน “กลไก expect/actual สำหรับ API ของแพลตฟอร์ม” ใช้เวลานานแค่ไหน

บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย

ฉันเขียนและรันโค้ดในบทเรียน Kotlin Academy นี้ได้ไหม

ได้ บทเรียน Kotlin Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. โครงสร้างโปรเจกต์ KMP: commonMain, androidMain, iosMain
  2. กลไก expect/actual สำหรับ API ของแพลตฟอร์ม
  3. การแชร์ชั้น Repository และ Use Case
  4. การฉีดการพึ่งพาใน KMP ด้วย Koin
← กลับไปที่ Kotlin Academy