Extension property ใน Kotlin — คืออะไร ไวยากรณ์ และการใช้งาน

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

extension property — กลไกของ Kotlin ที่อนุญาตให้เพิ่มคุณสมบัติใหม่ให้กับคลาสที่มีอยู่โดยไม่ต้องสืบทอดและไม่ต้องแก้ไขซอร์สโค้ด แตกต่างจาก extension functions ตรงที่ extension properties ไม่สามารถเก็บสถานะได้ — ถูกประกาศด้วย getter เท่านั้น และเลือกได้ว่าจะมี setter หรือไม่ เนื่องจากไม่มี backing field ตาม Kotlin Documentation, 2025 extension properties ถูกคอมไพล์เป็น เมธอด getter และ setter แบบสแตติก โดยมี receiver เป็นพารามิเตอร์แรก

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

  • Extension property — คุณสมบัติที่มี receiver type สามารถเข้าถึงได้ผ่านไวยากรณ์คุณสมบัติของ Kotlin
  • ไม่มี backing field — extension property ไม่สามารถเก็บสถานะได้ มีเพียงคำนวณเท่านั้น
  • Getter จำเป็น, setter เป็นทางเลือก — ประกาศเหมือน extension functions ทั่วไป
  • ชนิด nullable รองรับ: receiver สามารถเป็น nullable ได้พร้อมการตรวจสอบภายใน
  • Mutable extension property — เฉพาะเมื่อประกาศเป็น var พร้อม getter และ setter

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

Extension property คือโครงสร้างทางไวยากรณ์ใน Kotlin ที่เพิ่มคุณสมบัติให้กับชนิดที่มีอยู่โดยไม่ต้องเปลี่ยนการประกาศของมัน คุณสมบัติถูกประกาศด้วย receiver type และต้องมี getter ความแตกต่างหลัก จากคุณสมบัติทั่วไปคือการไม่มี backing field: extension property ไม่สามารถเก็บข้อมูลได้ มีเพียงคำนวณตามออบเจกต์ receiver

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

Extension properties ถูกคอมไพล์เป็นคู่ของเมธอด getter และ setter แบบสแตติก ในระดับไบต์โค้ด ไม่มีความแตกต่างระหว่างการเรียก extension property และการเรียก extension function — ทั้งคู่กลายเป็นเมธอดสแตติกที่มีพารามิเตอร์ receiver ตาม JetBrains (Kotlin Docs, 2025) ไม่มีโอเวอร์เฮดใดๆ

ใช้ extension properties สำหรับค่าที่คำนวณสั้นๆ ที่ควรดูเหมือนคุณสมบัติมากกว่าการเรียกเมธอด — ซึ่งช่วยปรับปรุงความสามารถในการอ่านโค้ดและปฏิบัติตามหลักการเข้าถึงอย่างสม่ำเสมอ

ไวยากรณ์ extension property: val และ var

ในการประกาศ extension property ให้ใช้ไวยากรณ์คล้ายคุณสมบัติทั่วไป แต่มีคำนำหน้าเป็น receiver type val ประกาศ extension property แบบอ่านอย่างเดียวที่มี getter จำเป็น var ประกาศแบบ mutable ที่มี getter และ setter เป็นทางเลือก

kotlin
// Extension property แบบอ่านอย่างเดียว
val String.isEmail: Boolean
    get() = this.contains("@") && this.contains(".")

// เรียก
val valid = "test@test.com".isEmail

หมายเหตุ: extension property ถูกเรียกโดยวงเล็บ — str.isEmail ไม่ใช่ str.isEmail() นี่คือความแตกต่างหลักระหว่าง extension property และ extension function: คุณสมบัติดูเหมือนฟิลด์ แม้ว่าจริงๆ แล้วจะคำนวณผ่าน getter

Extension property ทั่วไป (Generic)

Extension properties สามารถเป็น generic ได้ — receiver สามารถใช้พารามิเตอร์ชนิดทั่วไป ซึ่งช่วยให้สร้างคุณสมบัติสากลที่ทำงานกับชนิดคอลเลกชันใดๆ

kotlin
val List<T>.secondOrNull: T?
    get() = if (size >= 2) this[1] else null

val items = listOf("a", "b", "c")
val second = items.secondOrNull // "b"

คุณสมบัติ secondOrNull ทำงานกับชนิด T ใดๆ โดยคืนค่าองค์ประกอบที่สองของรายการหรือ null หากมีน้อยกว่าสององค์ประกอบ นี่คือตัวอย่างทั่วไปที่ extension property เหมาะสมกว่าฟังก์ชัน — การเข้าถึงดูเหมือนการอ่านฟิลด์

ทำไม extension property ไม่สามารถเก็บสถานะได้

Extension property ไม่สามารถมี backing field ได้เพราะไม่ได้ถูกเพิ่มเข้าไปในเมทาดาทาของคลาส — มันมีอยู่เพียงเป็นคู่ของฟังก์ชัน getter/setter แบบสแตติก Backing field (คีย์เวิร์ด field ใน Kotlin) คือฟิลด์ภายในของคลาสที่เก็บค่าคุณสมบัติ Extension property ไม่สามารถเข้าถึงโครงสร้างภายในของคลาส

kotlin
// ❌ ข้อผิดพลาด: extension property ไม่สามารถมี backing field
var String.cachedValue: String
    get() = "computed"
    set(value) {
        field = value // ไม่สามารถเข้าถึงฟิลด์!
    }

// ✅ ถูกต้อง: ใช้พื้นที่จัดเก็บภายนอก
val cache = MutableMap<String, String>()

var String.cachedValue: String
    get() = cache[this] ?: ""
    set(value) { cache[this] = value }

Map ภายนอก ในตัวอย่างแก้ปัญหาการจัดเก็บแต่สร้างปัญหาอื่น — หน่วยความจำรั่วไหล ค่าที่ได้รับผ่าน extension property อยู่ใน Map ตลอดไปหากไม่ถูกล้าง ข้อจำกัดนี้ทำให้ extension properties ไม่เหมาะสมสำหรับการแคชหรือเก็บข้อมูลชั่วคราว

สำหรับการแคช แนะนำให้ใช้ WeakHashMap หรือกลไกที่มีการล้างอัตโนมัติ JetBrains แนะนำให้หลีกเลี่ยงการใช้ var extension properties กับพื้นที่จัดเก็บภายนอกในโค้ดการผลิตโดยไม่มีการจัดการวงจรชีวิตอย่างระมัดระวัง

Extension property เทียบกับ extension function: เมื่อใดควรเลือกอะไร

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

เกณฑ์Extension propertyExtension function
การเรียกไม่มีวงเล็บ: obj.propertyมีวงเล็บ: obj.function()
ความหมายลักษณะเฉพาะ, คุณลักษณะการกระทำ, การดำเนินการ
Backing fieldไม่รองรับไม่เกี่ยวข้อง
พารามิเตอร์เฉพาะ getter/setterพารามิเตอร์ใดๆ
ประสิทธิภาพเหมือนกัน (เมธอดสแตติก)เหมือนกัน (เมธอดสแตติก)
ตัวอย่างtext.lengthtext.isEmail()

กฎง่ายๆ: หากการดำเนินการรับพารามิเตอร์ — ใช้ extension function หากเป็นค่าที่คำนวณง่ายๆ ไม่มีพารามิเตอร์ — ใช้ extension property ตาม Android Architecture Guide (Google, 2025) ควรให้ความสำคัญกับ extension properties สำหรับการเข้าถึงข้อมูลและ extension functions สำหรับการดำเนินการที่มีผลข้างเคียง

Mutable extension property ด้วย var และ setter

Extension property ที่มีคีย์เวิร์ด var รองรับ setter แต่ไม่มีความสามารถในการเก็บค่า — setter มักจะดำเนินการผลข้างเคียงหรือบันทึกข้อมูลในพื้นที่จัดเก็บภายนอก ไวยากรณ์ คล้ายกับคุณสมบัติคลาสแบบ mutable

kotlin
// Mutable extension property พร้อม setter
var StringBuilder.lastChar: Char
    get() = this[length - 1]
    set(value) {
        this.setCharAt(length - 1, value)
    }

val sb = StringBuilder("Kotlin")
println(sb.lastChar) // n
sb.lastChar = '!'
println(sb) // Kotli!

คุณสมบัติ lastChar เป็นตัวอย่างคลาสสิกจากเอกสาร Kotlin Getter คืนค่าอักขระสุดท้ายของ StringBuilder setter แทนที่ด้วยค่าใหม่ หมายเหตุ: สถานะถูกเก็บใน StringBuilder เอง (ผ่าน setCharAt) ไม่ใช่ในฟิลด์แยกต่างหาก — นี่คือการใช้ extension property ที่ถูกต้อง

ตัวอย่างการใช้งาน extension properties

ในโปรเจกต์จริง extension properties มักถูกใช้เพื่อลดความซับซ้อนในการเข้าถึงข้อมูลคอลเลกชัน คำนวณขนาดหรือสถานะขององค์ประกอบ UI และสร้าง API ที่สะดวกเหนือคลาสที่มีอยู่ ไลบรารีมาตรฐานของ Kotlin ใช้กลไกนี้อย่างแข็งขัน: size, indices, lastIndex สำหรับคอลเลกชันคือ extension properties

kotlin
// Extension properties สำหรับคอลเลกชัน
val List<Int>.sumFast: Int
    get() = fold(0) { acc, i -> acc + i }

val String.half: String
    get() = this.substring(0, length / 2)

// Extension property สำหรับ Android View
val View.isVisible: Boolean
    get() = visibility == View.VISIBLE

// ตรวจสอบ null ผ่าน receiver ที่ปลอดภัย
val String?.isNullOrBlank: Boolean
    get() = this == null || this.isBlank()

Extension property isVisible สำหรับ View เป็นตัวอย่างที่นักพัฒนา Android ทุกคนควรรู้ แทนที่จะใช้ view.visibility == View.VISIBLE คุณสามารถเขียน view.isVisible ซึ่งไม่เพียงสั้นกว่า แต่ยังอ่านได้เหมือนภาษาธรรมชาติ: “หาก view มองเห็นได้” แม้จะเรียบง่าย แต่คุณสมบัติเหล่านี้ช่วยปรับปรุงความสามารถในการอ่านโค้ดอย่างมีนัยสำคัญ

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

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

ไม่ ไม่สามารถประกาศ extension properties สำหรับ companion object หรือ object declaration ได้ กลไก extension ใช้ได้กับคลาส อินเทอร์เฟซ และชนิด nullable เท่านั้น สำหรับ object ให้ใช้ฟังก์ชันระดับบนสุดปกติ

Extension property แตกต่างจาก inline property อย่างไร?

Inline property (กับตัวปรับแต่ง inline) เป็นกลไกของ Kotlin สำหรับเรียก getter/setter โดยไม่สร้างออบเจกต์คุณสมบัติ Extension property ถูกคอมไพล์เป็นเมธอดสแตติกเสมอ ในขณะที่ inline property ถูกคอมไพล์เป็นการเรียกโดยไม่มีตัวห่อหุ้ม ทั้งสองแก้ปัญหาที่แตกต่างกัน: extension property เพิ่มคุณสมบัติให้กับชนิดที่มีอยู่ inline ปรับการเรียกคุณสมบัติของตัวเองให้เหมาะสม

Extension properties รองรับคำอธิบายประกอบ (annotations) หรือไม่?

ใช่ extension property สามารถมีคำอธิบายประกอบได้ แต่เฉพาะในระดับการประกาศเท่านั้น ไม่สามารถใส่คำอธิบายประกอบแยกสำหรับ getter หรือ setter ของ extension property — ต่างจากคุณสมบัติคลาสทั่วไป ตัวอย่าง: @JvmName("getIsValid") val String.isValid get() = true.

สามารถใช้ extension property กับ companion object ของคลาสได้หรือไม่?

ไม่ ไม่สามารถประกาศ extension properties กับ companion object เป็น receiver ได้ นี่คือข้อจำกัดของภาษา — extension property ทำงานกับอินสแตนซ์ของชนิดเท่านั้น ในขณะที่ companion object เป็นบริบทแบบสแตติก ใช้ฟังก์ชัน extension ระดับบนสุดหรือค่าคงที่

Extension property มีผลต่อขนาด APK หรือไม่?

น้อยที่สุด แต่ละ extension property เพิ่มเมธอด getter แบบสแตติกหนึ่งเมธอด (และเลือกได้ว่าจะมี setter) ลงในไบต์โค้ดที่คอมไพล์แล้ว สำหรับการเปรียบเทียบ การสร้างคลาสตัวห่อหุ้มที่มีคุณสมบัติเดียวกันเพิ่มทั้งคลาส Extension properties เป็นแนวทางที่เบากว่าในการขยายฟังก์ชันการทำงาน

สรุป

  • Extension property — คุณสมบัติที่คำนวณสำหรับชนิดที่มีอยู่โดยไม่ต้องสืบทอด
  • ไม่มี backing field — สถานะไม่ถูกเก็บ มีเพียงคำนวณผ่าน getter
  • var พร้อม setter — เป็นไปได้ แต่ต้องมีพื้นที่จัดเก็บภายนอกสำหรับเขียนข้อมูล
  • ไวยากรณ์ — val/var พร้อม receiver type และ getter ที่จำเป็น
  • ประสิทธิภาพ — ไม่มีโอเวอร์เฮด คอมไพล์เป็นเมธอดสแตติก
  • การใช้งาน — ลักษณะที่คำนวณได้: ความยาว สถานะ ขนาด การตรวจสอบ
  • ข้อจำกัด — ไม่เหมาะสมสำหรับการเก็บสถานะ การแคชโดยไม่มีการจัดการหน่วยความจำ

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

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

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

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