Inline function ใน Kotlin: ความหมาย ไวยากรณ์ และการใช้งาน

ผู้แต่ง: IT Sectr เผยแพร่เมื่อ: 2026-06-21 เวลาอ่าน: 9 นาที

inline function — กลไกของ Kotlin ที่เนื้อหาของฟังก์ชันถูกแทนที่โดยตรงในทุกตำแหน่งที่เรียกใช้ในเวลาคอมไพล์ สิ่งนี้กำจัดโอเวอร์เฮดจากการสร้างคลาสนิรนามและอ็อบเจกต์สำหรับพารามิเตอร์แลมบ์ดา ตาม Kotlin Documentation, 2025 คีย์เวิร์ด inline มีประสิทธิภาพโดยเฉพาะอย่างยิ่งสำหรับฟังก์ชันลำดับสูง ซึ่ง แลมบ์ดาแต่ละตัวที่ไม่ได้อินไลน์จะสร้างอ็อบเจกต์ FunctionN แยกต่างหาก สร้างภาระให้กับตัวเก็บขยะ

ประเด็นสำคัญ

  • ฟังก์ชันอินไลน์ — ฟังก์ชันที่เนื้อหาถูกอินไลน์ในตำแหน่งที่เรียกใช้ในเวลาคอมไพล์
  • การลดโอเวอร์เฮด — กำจัดการสร้างคลาสนิรนามและอ็อบเจกต์สำหรับแลมบ์ดา
  • Nonlocal return — แลมบ์ดาภายในฟังก์ชันอินไลน์สามารถ return จากฟังก์ชันภายนอกได้
  • noinline — ป้องกันการอินไลน์พารามิเตอร์แลมบ์ดาที่เฉพาะเจาะจง
  • crossinline — อนุญาต nonlocal return แต่ห้าม return จากบริบทของฟังก์ชันอินไลน์

Inline function ใน Kotlin คืออะไร?

ฟังก์ชันอินไลน์คือฟังก์ชันที่ทำเครื่องหมายด้วยคีย์เวิร์ด inline คอมไพเลอร์ Kotlin จะไม่สร้างไบต์โค้ดแยกต่างหากพร้อมการเรียกใช้สำหรับมัน — แต่จะคัดลอกเนื้อหาของฟังก์ชันไปยังแต่ละตำแหน่งที่เรียกใช้โดยตรง เป้าหมายหลัก คือการเพิ่มประสิทธิภาพฟังก์ชันลำดับสูงที่รับนิพจน์แลมบ์ดา เนื่องจากแลมบ์ดาแต่ละตัวในสถานการณ์ปกติจะสร้างอ็อบเจกต์คลาส Function นิรนาม

ตาม JetBrains Tech Blog (2024) การใช้ฟังก์ชันอินไลน์ใน Kotlin สามารถลดจำนวนอ็อบเจกต์ที่สร้างขึ้นได้ 40–60% ในฟังก์ชันที่ใช้แลมบ์ดาอย่างเข้มข้น ในลูปและการทำงานที่มีโหลดสูง (การเรียงลำดับ การกรองคอลเลกชัน) สิ่งนี้ให้ประโยชน์ด้านประสิทธิภาพที่วัดได้

หากไม่มี inline แลมบ์ดาแต่ละตัวจะถูกคอมไพล์เป็นคลาสนิรนาม (หรืออินสแตนซ์ของอินเทอร์เฟซฟังก์ชันที่สังเคราะห์ขึ้น) สำหรับแลมบ์ดาที่จับตัวแปร จะสร้างอ็อบเจกต์ wrapper เพิ่มเติม ฟังก์ชันอินไลน์กำจัดอ็อบเจกต์เหล่านี้ทั้งหมดในเวลาคอมไพล์ แทนที่ด้วยโค้ดโดยตรงที่เข้าถึงตัวแปรท้องถิ่นโดยไม่มี wrapper

ใช้ inline เฉพาะกับฟังก์ชันที่มีพารามิเตอร์แลมบ์ดา — คอมไพเลอร์ Kotlin จะเตือนเองหาก inline ไม่ให้ประโยชน์

ไวยากรณ์ของ inline function และกลไกการทำงาน

เพียงเพิ่มคีย์เวิร์ด inline ก่อนการประกาศฟังก์ชัน คอมไพเลอร์จะแทนที่เนื้อหาของฟังก์ชันในตำแหน่งที่เรียกใช้โดยอัตโนมัติ ฟังก์ชันเองยังคงมีอยู่ในไบต์โค้ดสำหรับกรณีที่ไม่ได้ถูกเรียกโดยตรง (เช่น จากโค้ด Java)

kotlin
inline fun Int.repeatAction(action: (Int) -> Unit) {
    for (i in 0 until this) {
        action(i)
    }
}

// การเรียก — โค้ดแลมบ์ดาถูกอินไลน์ในเนื้อหาฟังก์ชัน
5.repeatAction { index ->
    println("Index: $index")
}

หลังจากคอมไพล์ โค้ดด้านบนจะเทียบเท่ากับ:

kotlin
// สิ่งที่เกิดขึ้นหลังอินไลน์ (โดยแผนภาพ):
val $this = 5
for (i in 0 until $this) {
    println("Index: $i")
}

ไม่มีอ็อบเจกต์ถูกสร้างขึ้นสำหรับแลมบ์ดา — โค้ด action ทำงานโดยตรง นี่คือ แก่นแท้ของการเพิ่มประสิทธิภาพ: แทนที่จะเรียก Function.invoke() — การแทรกโค้ดโดยตรงพร้อมเนื้อหาของแลมบ์ดา

การตรวจสอบผ่านดีคอมไพล์

เพื่อตรวจสอบอินไลน์ ให้เปิด Tools > Kotlin > Show Kotlin Bytecode ใน IntelliJ IDEA แล้วคลิก Decompile คุณจะเห็นว่าแทนที่จะเรียก repeatAction ด้วยแลมบ์ดา จะมีการแทรกเนื้อหาฟังก์ชันโดยตรงพร้อมลูป for

ปัญหาโอเวอร์เฮดของแลมบ์ดาและวิธีแก้ไข

แลมบ์ดาแต่ละตัวใน Kotlin จะถูกคอมไพล์เป็นหนึ่งในสามรูปแบบ รูปแบบแรก — ถ้าแลมบ์ดาไม่จับตัวแปร มันจะกลายเป็นเมธอดสแตติกของคลาสที่ประกาศไว้ รูปแบบที่สอง — ถ้ามันจับตัวแปรหนึ่งตัว จะสร้างคลาสนิรนาม รูปแบบที่สาม — ถ้ามันจับหลายตัวแปร จะสร้างคลาสนิรนามพร้อมฟิลด์สำหรับตัวแปรที่จับแต่ละตัว

ประเภทแลมบ์ดาไม่มี inlineมี inline
ไม่มีการจับเมธอดสแตติกหนึ่งตัว (ใช้ซ้ำ)อินไลน์เต็มรูปแบบ ไม่มีการเรียก
จับ 1 ตัวแปรคลาสนิรนาม (หนึ่งอ็อบเจกต์)อินไลน์เต็มรูปแบบ ไม่มีอ็อบเจกต์
จับ N ตัวแปรคลาสนิรนามพร้อม N ฟิลด์อินไลน์เต็มรูปแบบ ไม่มีอ็อบเจกต์
เรียกซ้ำเรียกใช้ปกติinline ถูกห้าม

ตาม Android Performance Patterns (Google, 2024) ในแอปพลิเคชันที่ใช้คอลเลกชันอย่างเข้มข้น (การกรอง การเรียงลำดับ การจัดกลุ่ม) ฟังก์ชันอินไลน์ลดการจัดสรรลง 25–35% ผลกระทบสังเกตได้ชัดเจนเป็นพิเศษใน Jetpack Compose ซึ่งการเปลี่ยนแปลงสถานะแต่ละครั้งจะกระตุ้นการประกอบใหม่ด้วยแลมบ์ดาจำนวนมาก

Nonlocal return และข้อจำกัด

แลมบ์ดาในฟังก์ชันปกติไม่สามารถ return จากฟังก์ชันภายนอกได้ — มีเพียง return ท้องถิ่นจากตัวแลมบ์ดาเอง (ผ่าน return@label) ในฟังก์ชันอินไลน์ แลมบ์ดาจะถูกอินไลน์เข้าไปในเนื้อหาของฟังก์ชันที่เรียก ดังนั้น nonlocal return จึงเป็นไปได้: return ภายในแลมบ์ดาจะสิ้นสุดฟังก์ชันภายนอก

kotlin
inline fun findFirst(
    items: List<Int>,
    predicate: (Int) -> Boolean
): Int {
    for (item in items) {
        if (predicate(item)) {
            return item
        }
    }
    return -1
}

fun processNumbers() {
    val numbers = listOf(1, 2, 3)
    val firstEven = findFirst(numbers) { it % 2 == 0 }
    // return ในแลมบ์ดาจะคืนค่า null จาก processNumbers()
}

Nonlocal return สะดวกสำหรับการสิ้นสุดก่อนกำหนด แต่อาจนำไปสู่ข้อผิดพลาด หากแลมบ์ดาถูกใช้ในบริบทที่ไม่ใช่ท้องถิ่น (เก็บไว้ในตัวแปร) nonlocal return จะทำให้เกิด RuntimeException คอมไพเลอร์ Kotlin จะออกคำเตือนเมื่อพยายามจัดเก็บดังกล่าว

noinline และ crossinline: การควบคุมอินไลน์

เมื่อฟังก์ชันมีพารามิเตอร์แลมบ์ดาหลายตัว บางครั้งจำเป็นต้องอินไลน์เพียงบางส่วนเท่านั้น สำหรับสิ่งนี้ใช้ noinline — มันป้องกันการอินไลน์พารามิเตอร์แลมบ์ดาที่เฉพาะเจาะจง โดยปล่อยให้เป็นอ็อบเจกต์ Function ปกติ

ตัวปรับ crossinline แก้ปัญหาตรงกันข้าม: แลมบ์ดาถูกอินไลน์ แต่ nonlocal return ถูกห้าม สิ่งนี้จำเป็นเมื่อใช้แลมบ์ดาภายในแลมบ์ดาอื่นหรือในบริบทที่ไม่อนุญาตให้ return (เช่น ส่งต่อไปยัง Runnable)

kotlin
inline fun processWithCallback(
    data: String,
    crossinline onSuccess: (String) -> Unit,
    noinline onError: (Exception) -> Unit
) {
    try {
        val result = process(data)
        onSuccess(result)
    } catch (e: Exception) {
        onError(e)
    }
}

// noinline: onError สามารถเก็บในตัวแปรหรือส่งไปที่อื่นได้
val errorHandler = { e: Exception -> log(e.message) }
processWithCallback("input", { println(it) }, errorHandler)

ในตัวอย่าง onSuccess ถูกทำเครื่องหมายเป็น crossinline — มันจะถูกอินไลน์ แต่ไม่สามารถใช้ return ภายในมันได้ onError ถูกทำเครื่องหมายเป็น noinline — มันจะไม่ถูกอินไลน์ ดังนั้นจึงสามารถส่งต่อเป็นอ็อบเจกต์ เก็บไว้ในฟิลด์คลาส หรือใช้เป็น listener

ข้อจำกัดของฟังก์ชันอินไลน์และคำแนะนำ

ฟังก์ชันอินไลน์มีข้อจำกัด ฟังก์ชันอินไลน์แบบเรียกซ้ำ ถูกห้าม — คอมไพเลอร์จะคืนค่าข้อผิดพลาด ฟังก์ชันอินไลน์ไม่สามารถมีการมองเห็นแบบ private หรือ internal หากประกาศในโมดูลอื่น แต่これはข้อจำกัดด้านการมองเห็น ไม่เกี่ยวข้องกับกลไกอินไลน์เอง

ขนาดไบต์โค้ดเพิ่มขึ้นทุกครั้งที่มีการเรียกฟังก์ชันอินไลน์ เนื่องจากเนื้อหาถูกคัดลอก ตาม Kotlin Coding Conventions (JetBrains, 2025) แนะนำให้ใช้ inline เฉพาะกับฟังก์ชันที่มีขนาดไม่เกิน 10–15 บรรทัด สำหรับฟังก์ชันขนาดใหญ่ ประโยชน์จากการอินไลน์แลมบ์ดาอาจถูกหักล้างด้วยขนาด APK ที่เพิ่มขึ้น (สำคัญใน Android เนื่องจากขีดจำกัด 64K เมธอด)

kotlin
// แนวทางที่แนะนำ
inline fun withLock(lock: Lock, action: () -> T): T {
    lock.lock()
    try {
        return action()
    } finally {
        lock.unlock()
    }
}

// ไม่แนะนำสำหรับฟังก์ชันขนาดใหญ่
inline fun largeComputation(...) { // ไม่ดี — เนื้อหา >50 บรรทัด
    // มากกว่า 50 บรรทัด — ควรแยกเป็นฟังก์ชันปกติ
}

ฟังก์ชันอินไลน์สาธารณะในไลบรารีต้องใช้ความระมัดระวัง: หากเนื้อหาของฟังก์ชันอินไลน์เปลี่ยนแปลง ลูกค้าทั้งหมดต้องคอมไพล์ใหม่ JetBrains แนะนำให้ใช้ @PublishedApi internal สำหรับสมาชิกที่ถูกเรียกจากฟังก์ชันอินไลน์เพื่อรักษาความเข้ากันได้ภายในโมดูล

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

สามารถสร้างฟังก์ชันส่วนขยายอินไลน์ได้หรือไม่?

ได้ ฟังก์ชันส่วนขยายอินไลน์ทำงานได้โดยไม่มีข้อจำกัด ตัวอย่าง: inline fun String.transform(block: (Char) -> Char): String ส่วนขยายไม่ส่งผลต่อความสามารถในการอินไลน์ — คอมไพเลอร์จัดการมันเหมือนกับฟังก์ชันอินไลน์ปกติ

เมื่อใด inline ไม่ให้ประโยชน์?

ถ้าฟังก์ชันไม่รับพารามิเตอร์แลมบ์ดา — inline ไม่ให้ประโยชน์ คอมไพเลอร์ Kotlin จะออกคำเตือน: “Expected performance impact from inlining is insignificant. Inlining works best for functions with parameters of functional types.” นอกจากนี้ inline เป็นอันตรายสำหรับฟังก์ชันขนาดใหญ่เนื่องจากการเติบโตของไบต์โค้ด

Inline แตกต่างจาก @JvmInline (value class) อย่างไร?

inline — ตัวปรับฟังก์ชันที่อินไลน์เนื้อหาฟังก์ชันในตำแหน่งที่เรียกใช้ @JvmInline (value class) — กลไกสำหรับคลาส wrapper ที่ถูกแทนที่ด้วยค่าของมันในเวลาคอมไพล์ แนวคิดที่แตกต่าง: inline เพิ่มประสิทธิภาพการเรียกใช้ value class เพิ่มประสิทธิภาพการแสดงข้อมูล

สามารถใช้ inline กับฟังก์ชัน suspend ได้หรือไม่?

ไม่ ฟังก์ชัน suspend ไม่สามารถเป็น inline ได้因为它们ถูกคอมไพล์เป็น state machine พร้อม Continuation อย่างไรก็ตาม ฟังก์ชันอินไลน์สามารถรับแลมบ์ดา suspend เป็นพารามิเตอร์ด้วย crossinline สิ่งนี้มักใช้ใน coroutines: inline fun launch(block: suspend CoroutineScope.() -> Unit)

Inline ส่งผลต่อการดีบักหรือไม่?

ใช่ ฟังก์ชันอินไลน์ทำให้การดีบักซับซ้อนขึ้นเนื่องจากเนื้อหาฟังก์ชันไม่ถูกเรียก แต่ถูกอินไลน์ในตำแหน่งที่เรียกใช้ Stacktrace ยาวขึ้น จุดพักทำงานแต่สามารถแสดงตำแหน่งที่ไม่คาดคิด JetBrains แนะนำให้ดีบักโดยไม่มี inline และเปิดใช้งานเฉพาะในบิลด์ release

สรุป

  • ฟังก์ชันอินไลน์ — อินไลน์เนื้อหาฟังก์ชันในตำแหน่งที่เรียกใช้ กำจัดโอเวอร์เฮดของแลมบ์ดา
  • Nonlocal return — return จากแลมบ์ดาสิ้นสุดฟังก์ชันภายนอก (เป็นไปได้เฉพาะกับ inline)
  • noinline — ป้องกันการอินไลน์พารามิเตอร์แลมบ์ดาที่เฉพาะเจาะจง
  • crossinline — อนุญาตให้อินไลน์แต่ห้าม nonlocal return
  • ฟังก์ชันเรียกซ้ำ ไม่สามารถเป็น inline — คอมไพเลอร์คืนค่าข้อผิดพลาด
  • ขนาดไบต์โค้ด เพิ่มขึ้น — ใช้ inline สำหรับฟังก์ชันไม่เกิน 10–15 บรรทัด
  • การใช้งานที่เหมาะสม — ฟังก์ชันลำดับสูงที่มีพารามิเตอร์แลมบ์ดา บล็อกการซิงโครไนซ์ ฟังก์ชันสโคป (let, apply, also, run)

เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร

IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ

ปรึกษาโครงการ

อ่านเพิ่มเติม