Extension function ใน Kotlin: คืออะไร ไวยากรณ์และการประยุกต์ใช้

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

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

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

  • Extension function — เมธอดที่มีชนิดตัวรับ ถูกเรียกเหมือนสมาชิกคลาสโดยไม่ต้องสืบทอด
  • ชนิดตัวรับ ถูกระบุผ่านจุดก่อนชื่อฟังก์ชัน: fun Type.method()
  • การจัดส่งแบบสแตติก — extension function ไม่ใช่เสมือน ถูกเรียกตามชนิดของตัวแปร
  • การนำเข้า จำเป็น: extension functions ไม่ใช่สมาชิกของคลาสและต้องการการนำเข้า
  • สมาชิกคลาส มีลำดับความสำคัญ: ถ้าเมธอดตรงกัน สมาชิกคลาสจะถูกเรียก ไม่ใช่ extension

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

Extension function คือฟังก์ชันที่ประกาศภายนอกคลาสแต่ถูกเรียกเหมือนเมธอดของคลาสนั้น ฟังก์ชันรับชนิดตัวรับซึ่งเมธอดใหม่จะถูกเพิ่มเข้าไป ภายในฟังก์ชัน คีย์เวิร์ด this อ้างถึงอินสแตนซ์ของชนิดตัวรับ วิธีการนี้ช่วยให้ขยายคลาส final คลาสไลบรารีของบุคคลที่สาม และแม้แต่ชนิดดั้งเดิมได้โดยไม่ต้องสร้าง wrapper

แตกต่างจาก Java ที่การเพิ่มเมธอดให้กับคลาสที่มีอยู่ต้องใช้การสืบทอดหรือคลาสยูทิลิตีแบบสแตติก Kotlin มอบโซลูชันที่สวยงามโดยไม่มี boilerplate ตาม การศึกษาของ Kotlin Foundation (2024) extension functions อยู่ในห้าคุณสมบัติที่ถูกใช้มากที่สุดของภาษา — นักพัฒนา Kotlin 78% ใช้มันในการทำงานประจำวัน

คอมไพเลอร์ Kotlin แปลง extension function เป็นเมธอดสแตติกซึ่งอินสแตนซ์ตัวรับถูกส่งเป็นอาร์กิวเมนต์แรก ซึ่งหมายความว่า extension functions ไม่ได้แก้ไขไบต์โค้ดของคลาสเป้าหมายและไม่ทำลายการห่อหุ้ม — เฉพาะฟิลด์และเมธอดสาธารณะของตัวรับเท่านั้นที่เข้าถึงได้

ใช้ extension functions เพื่อเขียนฟังก์ชันยูทิลิตีที่สะอาดซึ่งเชื่อมโยงเชิงตรรกะกับชนิดเฉพาะแต่ไม่สามารถเพิ่มลงในซอร์สโค้ดของมันได้

ไวยากรณ์ extension function: การประกาศและการเรียก

ไวยากรณ์พื้นฐานของ extension function: ชื่อชนิดตัวรับ จุด ชื่อฟังก์ชัน จากนั้นพารามิเตอร์และชนิดที่ส่งคืน ภายในฟังก์ชัน ตัวรับสามารถเข้าถึงได้ผ่าน this

kotlin
// Extension function for String
fun String.isEmail(): Boolean {
    return this.contains("@") && this.contains(".")
}

// Called as regular String method
val result = "user@example.com".isEmail() // true

ในตัวอย่างนี้ isEmail() พร้อมใช้งานสำหรับสตริงทั้งหมด ภายในฟังก์ชัน this อ้างถึงสตริงที่เมธอดถูกเรียกใช้ Kotlin อนุญาตให้ละ this ในกรณีส่วนใหญ่ — คุณสามารถเขียนแค่ contains("@") แทน this.contains("@")

Extension function พร้อมพารามิเตอร์

Extension functions สามารถรับพารามิเตอร์เพิ่มเติมและส่งคืนค่าของชนิดใดก็ได้ สิ่งนี้ทำให้พวกมันเป็นฟังก์ชันที่สมบูรณ์ ไม่ใช่แค่น้ำตาลเชิงไวยากรณ์

kotlin
fun List<Int>.defaultIfEmpty(default: Int): List<Int> {
    if (this.isEmpty()) return listOf(default)
    return this
}

val populated = listOf(1, 2).defaultIfEmpty(0)
val empty = listOf<Int>().defaultIfEmpty(0)

พารามิเตอร์ default กำหนดค่าที่ส่งคืนสำหรับลิสต์ว่าง Extension function สามารถเป็น generic — ชนิด List เป็น generic แม้ว่าตัวอย่างจะใช้ List เฉพาะก็ตาม

การจัดส่งแบบสแตติกและตัวรับ

Extension functions ใน Kotlin ใช้ การจัดส่งแบบสแตติก ไม่ใช่การจัดส่งแบบเสมือน นี่คือความแตกต่างสำคัญจากเมธอดคลาสทั่วไป ว่า extension function ใดจะถูกเรียกนั้นถูกกำหนดในเวลาคอมไพล์โดยชนิดสแตติกของตัวแปร ไม่ใช่โดยชนิดจริงในรันไทม์

kotlin
open class Animal
class Dog : Animal()

fun Animal.speak() = "Animal sound"
fun Dog.speak() = "Woof"

fun test() {
    val animal: Animal = Dog()
    println(animal.speak()) // "Animal sound" — static type Animal

    val dog: Dog = Dog()
    println(dog.speak()) // "Woof" — static type Dog
}

แม้ว่าตัวแปร animal จะชี้ไปยังอินสแตนซ์ของ Dog แต่ extension function สำหรับ Animal ถูกเรียกเพราะชนิดสแตติกของตัวแปรคือ Animal ถ้า speak() เป็นเมธอดเสมือนของคลาส Dog.speak() จะถูกเรียก พฤติกรรมนี้สำคัญที่ต้องพิจารณาเมื่อออกแบบ API ด้วย extension functions

การนำเข้าและขอบเขตของ extension functions

Extension functions ไม่ใช่สมาชิกของคลาส — พวกมันเป็นฟังก์ชันปกติที่ต้องการการนำเข้าเพื่อใช้ในไฟล์อื่น Kotlin มีสองตัวเลือกการนำเข้า: ตามชื่อฟังก์ชันหรือพร้อมกับการเปลี่ยนชื่อ

kotlin
// Import by name
import com.example.extensions.isEmail

// Import with alias (to resolve conflicts)
import com.example.extensions.isEmail as isValidEmail

// Call after import
val valid = "test@test.com".isEmail()

การเปลี่ยนชื่อ ผ่าน as มีประโยชน์สำหรับการขัดแย้งของชื่อ — ตัวอย่างเช่น ถ้าไลบรารีสองแห่งมี extension function ชื่อเดียวกันสำหรับชนิดเดียวกัน ในกรณีนี้ คุณสามารถนำเข้าหนึ่งในนั้นด้วยชื่ออื่นและเรียกใช้โดยใช้ชื่อใหม่

ขอบเขต

Extension functions สามารถประกาศได้ในระดับต่างๆ:

ระดับการมองเห็นตัวอย่าง
Top-levelทั้งโปรเจกต์หลังนำเข้าfun String.isEmail()
Member extensionภายในคลาสเจ้าของclass A { fun B.ext() }
Localภายในฟังก์ชันfun test() { fun String.ext() }

Member extension functions เป็นกรณีพิเศษที่ extension function ถูกประกาศภายในคลาสอื่น ในกรณีนี้ ทั้งตัวรับ (this ของฟังก์ชัน) และสมาชิกของคลาสภายนอกสามารถเข้าถึงได้ภายใน extension function

ลำดับความสำคัญของสมาชิกคลาสเหนือส่วนขยาย

ถ้าคลาสมีเมธอดที่มีลายเซ็นเดียวกับ extension function สมาชิกคลาสจะถูกเรียกเสมอ Extension function ไม่เคยแทนที่เมธอดคลาส — นี่คือการตัดสินใจเชิงสถาปัตยกรรมเพื่อป้องกันการแทนที่โดยไม่ตั้งใจ

kotlin
class User {
    fun greet() = "Hello from class"
}

fun User.greet() = "Hello from extension"

fun main() {
    val user = User()
    println(user.greet()) // "Hello from class"
}

แม้ว่า extension function จะถูกกำหนดทีหลังและมีลายเซ็นเดียวกัน คอมไพเลอร์ จะเลือกเมธอดของคลาส คอมไพเลอร์ Kotlin จะแสดงคำเตือนเมื่อตรวจพบสถานการณ์นี้ วิธีเดียวที่จะเรียก extension function เมื่อมีเมธอดคลาสอยู่คือเรียกมันเป็นฟังก์ชันปกติ: greet(user)

ตัวอย่างเชิงปฏิบัติของ extension functions

ในการพัฒนา Android extension functions กลายเป็นเครื่องมือมาตรฐานสำหรับการทำงานกับ View Context และ fragments ไลบรารี Android KTX สร้างขึ้นบน extension functions โดยให้ wrapper ที่สะดวกเหนือ Android API

kotlin
// Extension function for working with View
fun View.show() {
    visibility = View.VISIBLE
}

fun View.hide() {
    visibility = View.GONE
}

// Extension function for Context — toast
fun Context.toast(message: String) {
    Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
}

// Extension function as DSL builder
fun String.colorFormat(color: String): String =
    "$color$this\u001b[0m"

Extension function toast() ทำให้การเรียกที่ขึ้นกับ Context กระชับ: แทนที่จะเขียน Toast.makeText(context, message, length).show() ก็แค่เขียน context.toast("ข้อความ") ซึ่งช่วยลด boilerplate และทำให้โค้ดอ่านง่ายขึ้น

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

สามารถแทนที่ extension function ในคลาสย่อยได้หรือไม่?

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

Extension functions ทำงานกับชนิด nullable อย่างไร?

คุณสามารถประกาศ extension functions สำหรับชนิดตัวรับ nullable: fun String?.isNullOrEmail() ภายในฟังก์ชันดังกล่าว this อาจเป็น null ดังนั้นคุณต้องใช้ตัวดำเนินการเรียกที่ปลอดภัยหรือการตรวจสอบ null อย่างชัดแจ้ง

Extension functions มี overhead ด้านประสิทธิภาพหรือไม่?

ไม่ extension functions ถูกคอมไพล์เป็นเมธอดสแตติก ในระดับไบต์โค้ด การเรียก extension function เหมือนกับการเรียกเมธอดสแตติกโดยมีตัวรับเป็นพารามิเตอร์แรก ไม่มีการสะท้อนหรือการจัดส่งแบบไดนามิกใดๆ

สามารถประกาศ extension property สำหรับคลาสได้หรือไม่?

ได้ extension properties ทำงานคล้ายกับ extension functions แต่ไม่สามารถเก็บสถานะได้ — แค่ getter และ setter ตัวอย่าง: val List.sum get() = reduce(Int::plus) พวกมันยังถูกคอมไพล์เป็นเมธอด getter/setter แบบสแตติก

Extension functions แตกต่างจากเมธอดในคลาสยูทิลิตีของ Java อย่างไร?

ใน Java คุณเขียน StringUtils.isEmail(str) ในขณะที่ Kotlin คุณเขียน str.isEmail() ความแตกต่างไม่ใช่แค่ไวยากรณ์: extension functions รองรับการเติมข้อความอัตโนมัติใน IDE ปรับปรุงความสามารถในการอ่านของลูกโซ่การเรียก และอนุญาตให้ IDE แนะนำฟังก์ชันที่เกี่ยวข้องสำหรับชนิดเฉพาะ Java ไม่มีความสามารถเหล่านี้

สรุป

  • Extension function — เมธอดที่มีชนิดตัวรับ ถูกเรียกเหมือนสมาชิกคลาสโดยไม่ต้องสืบทอด
  • การจัดส่งแบบสแตติก — การเลือกฟังก์ชันตามชนิดสแตติก ไม่ใช่ชนิดรันไทม์
  • Overhead เป็นศูนย์ — คอมไพล์เป็นเมธอดสแตติกโดยมีตัวรับเป็นพารามิเตอร์แรก
  • การนำเข้า จำเป็นสำหรับใช้ในไฟล์อื่น รองรับนามแฝงผ่าน as
  • สมาชิกคลาส มีลำดับความสำคัญเหนือ extension functions ที่มีลายเซ็นเดียวกัน
  • Member extensions — ส่วนขยายที่ประกาศภายในคลาสสามารถเข้าถึงสมาชิกของมันได้
  • การใช้งานจริง — ยูทิลิตีสำหรับ String, View, Context, คอลเลกชันและการสร้าง DSL

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

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

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

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