Destructuring declaration คือความสามารถของ Kotlin ในการแยกวัตถุออกเป็นตัวแปรหลายตัวในนิพจน์เดียว โครงสร้าง val (name, age) = user ประกาศตัวแปรสองตัวพร้อมกัน โดยกำหนดค่าให้จากคอมโพเนนต์ของวัตถุ จากข้อมูลใน เอกสารของ Kotlin (2026) การแยกตัวแปรช่วยลดขนาดของโค้ดเมื่อทำงานกับ data class ได้ 30–50% Destructuring declaration ทำให้โค้ดกระชับและอ่านง่ายขึ้น
สาระสำคัญ
Destructuring declaration (การประกาศแบบแยกโครงสร้าง) คือโครงสร้างไวยากรณ์ของ Kotlin ที่ช่วยให้แยกอินสแตนซ์ของคลาสออกเป็นตัวแปรหลายตัวในนิพจน์เดียว ด้านซ้ายมีรายการตัวแปรในวงเล็บ ด้านขวาคือวัตถุที่จะถูกแยก Kotlin จะเรียกฟังก์ชัน componentN() ที่สอดคล้องกันโดยอัตโนมัติสำหรับแต่ละตัวแปร โดยที่ N คือลำดับของตัวแปรในวงเล็บ
destructuring declaration มักถูกใช้กับ data class มากที่สุด คอมไพเลอร์ของ Kotlin จะสร้างฟังก์ชัน component1(), component2() และอื่น ๆ สำหรับ data class โดยอัตโนมัติตามจำนวนคุณสมบัติในคอนสตรัคเตอร์หลัก สำหรับคลาสทั่วไปที่ไม่มีตัวปรับเปลี่ยน data จะไม่มีการสร้าง componentN() แต่สามารถกำหนดได้ด้วยตนเองในรูปแบบฟังก์ชันตัวดำเนินการด้วยคำหลัก operator
Destructuring declaration กลายเป็นแพตเทิร์นยอดนิยมเนื่องจากความสามารถในการ ทำให้โค้ดง่ายขึ้น เมื่อทำงานกับคอลเลกชันของคู่ ผลลัพธ์ของฟังก์ชัน และการวนซ้ำ Map แทนที่จะใช้ person.firstName, person.lastName นักพัฒนาสามารถเขียน val (firstName, lastName) = person ซึ่งช่วยลดจำนวนบรรทัดและเพิ่มความอ่านง่าย โดยเฉพาะในลูปและแลมบ์ดา
เมื่อพบโครงสร้าง val (a, b) = obj คอมไพเลอร์ของ Kotlin จะสร้างโค้ดดังนี้: val a = obj.component1(); val b = obj.component2() ถ้าตัวแปรถูกประกาศด้วย var ก็จะสร้างการกำหนดค่าแบบ var ชนิดของแต่ละตัวแปรจะถูกอนุมานจากชนิดที่ส่งกลับของฟังก์ชัน componentN() ที่เกี่ยวข้อง ซึ่งหมายความว่า destructuring declaration ปลอดภัยต่อชนิดโดยสมบูรณ์และถูกตรวจสอบในขั้นตอนการคอมไพล์
data class Person(val firstName: String, val lastName: String, val age: Int)
fun main() {
val person = Person("Alice", "Smith", 30)
// การแยกโครงสร้าง: component1(), component2(), component3()
val (firstName, lastName, age) = person
println("$firstName $lastName, $age ปี")
}
ในลิสติ้ง Person เป็น data class ที่มีคุณสมบัติสามอย่าง Kotlin สร้าง component1() → firstName, component2() → lastName, component3() → age นิพจน์ val (firstName, lastName, age) = person เรียกฟังก์ชันเหล่านี้และกำหนดผลลัพธ์ สิ่งสำคัญคือชื่อของตัวแปรไม่จำเป็นต้องตรงกับชื่อของคุณสมบัติ — คอมไพเลอร์พิจารณาเฉพาะลำดับของ componentN() และชนิดของมันเท่านั้น คุณสามารถแยกได้เฉพาะคุณสมบัติ N แรกโดยละเว้นส่วนที่เหลือ
Data class เป็นตัวเลือกหลักสำหรับ destructuring declaration ทุก data class จะได้รับ componentN() โดยอัตโนมัติสำหรับคุณสมบัติทั้งหมดของคอนสตรัคเตอร์หลัก ลำดับของ componentN() สอดคล้องกับลำดับการประกาศคุณสมบัติ ซึ่งหมายความว่าเมื่อเปลี่ยนลำดับของฟิลด์ใน data class ลำดับของตัวแปรในการแยกโครงสร้างก็จะเปลี่ยนไปด้วย ซึ่งอาจนำไปสู่บั๊กที่มองไม่เห็น
// การแยกโครงสร้าง Pair
val pair = Pair("key", 42)
val (key, value) = pair
println("$key -> $value")
// การแยกโครงสร้าง Triple
val triple = Triple("A", "B", "C")
val (first, second, third) = triple
// Map.Entry ในลูป
val map = mapOf("a" to 1, "b" to 2)
for ((k, v) in map) {
println("$k = $v")
}
Pair และ Triple เป็นคลาสมาตรฐานที่มีการแยกตัวแปรพร้อมใช้สำหรับสองและสามคอมโพเนนต์ Pair มักถูกใช้เป็นชนิดที่ส่งกลับของฟังก์ชันที่ต้องคืนค่าที่เกี่ยวข้องกันสองค่าโดยไม่ต้องสร้าง data class แยกต่างหาก ในลูป for ที่วนซ้ำ Map.Entry โครงสร้าง (k, v) = entry จะแยกคีย์และค่า ผ่าน component1() และ component2() นี่คือแพตเทิร์นมาตรฐานของการวนซ้ำ Map ใน Kotlin ซึ่งแทนที่การเรียก key และ value อย่างชัดเจนโดยสิ้นเชิง
ถ้าคอมโพเนนต์บางตัวไม่จำเป็นในการแยกโครงสร้าง Kotlin อนุญาตให้แทนที่ด้วย เครื่องหมายขีดล่าง _ ซึ่งช่วยปรับปรุงความอ่านง่าย — ผู้อ่านเห็นทันทีว่าคอมโพเนนต์นั้นถูกละเว้น นอกจากนี้ การใช้ _ ยังป้องกันการเข้าถึงตัวแปรที่ไม่ได้กำหนดค่าเริ่มต้นโดยไม่ได้ตั้งใจ: คอมไพเลอร์ไม่ได้สร้างตัวแปรใด ๆ สำหรับ _ ซึ่งช่วยขจัดคำเตือนเกี่ยวกับตัวแปรที่ไม่ได้ใช้
data class Order(
val id: Long,
val customerName: String,
val amount: Double,
val status: String
)
fun process(orders: List<Order>) {
for ((id, _, amount) in orders) {
println("คำสั่งซื้อ $id: $amount")
}
}
ในลิสติ้ง Order มีคุณสมบัติสี่อย่าง แต่ในลูปต้องการเพียง id และ amount เครื่องหมายขีดล่างที่ตำแหน่งของ customerName บอกคอมไพเลอร์ว่า «ข้าม component2()» Kotlin จะไม่เรียก component2() และไม่สร้างตัวแปรสำหรับคอมโพเนนต์นั้น สำคัญ: เครื่องหมายขีดล่างสามารถใช้ได้เฉพาะใน destructuring declaration แต่ไม่สามารถใช้ในคอนสตรัคเตอร์ของ data class หากข้ามหลายคอมโพเนนต์ติดต่อกัน แต่ละคอมโพเนนต์จะมีเครื่องหมายขีดล่างของตัวเอง
สำหรับคลาสที่ไม่มีตัวปรับเปลี่ยน data คุณสามารถกำหนด componentN() ได้ด้วยตนเองโดยเพิ่มคำหลัก operator ซึ่งช่วยให้ใช้ destructuring declaration กับคลาสใดก็ได้ รวมถึงคลาสของบุคคลที่สามที่ไม่สามารถแก้ไขซอร์สโค้ดได้ — แค่เขียน extension function ที่มี operator ก็เพียงพอ แนวทางนี้มีประโยชน์โดยเฉพาะสำหรับคลาสจากไลบรารีและคลาสจากโค้ด Java ที่ไม่มี componentN() อัตโนมัติ
class Rect(val width: Int, val height: Int)
operator fun Rect.component1(): Int = width
operator fun Rect.component2(): Int = height
// สำหรับคลาส Java ที่มี getters
operator fun Dimension.component1(): Int = width
operator fun Dimension.component2(): Int = height
fun main() {
val rect = Rect(1920, 1080)
val (w, h) = rect
println("ความละเอียด: ${w}x${h}")
}
ในตัวอย่าง Rect เป็นคลาสทั่วไปที่ไม่มี data ฟังก์ชัน extension ที่มี operator fun component1() = width และ component2() = height เพิ่มการรองรับการแยกโครงสร้าง ในทำนองเดียวกันสำหรับ java.awt.Dimension: operator extension บน Dimension ช่วยให้แยกมันออกเป็น width และ height ได้ ข้อจำกัด: จำนวนคอมโพเนนต์สูงสุดสำหรับ destructuring declaration คือ 5 ในทางทฤษฎีสามารถกำหนด component6() และสูงกว่าได้ แต่ Kotlin จะไม่สร้างไวยากรณ์ที่เกี่ยวข้องสำหรับตัวแปร 6+ ตัวในวงเล็บ
คำถามที่พบบ่อย
ใช่ ทุก data class จะได้รับ componentN() โดยอัตโนมัติสำหรับคุณสมบัติทั้งหมดของคอนสตรัคเตอร์หลัก คุณสมบัติที่ประกาศในเนื้อหาของคลาส (ผ่านคอนสตรัคเตอร์ที่ไม่มี val/var) ไม่สร้าง componentN()
ถ้าใน val (a, b, c) = obj วัตถุมีฟังก์ชัน component เพียงสองฟังก์ชัน คอมไพเลอร์จะแสดงข้อผิดพลาด ถ้าตัวแปรน้อยกว่า (val (a) = pair) Kotlin จะเรียกเฉพาะ component1() และละเว้น component2() — จะไม่มีข้อผิดพลาด
ได้ Map.Entry รองรับการแยกโครงสร้าง: val (key, value) = map.entries.first() สำหรับ Map ทั้งหมดในครั้งเดียว การแยกโครงสร้างไม่ทำงาน — ทำได้เพียงทีละรายการ ในลูป for หรือผ่านตัววนซ้ำ
ได้ Kotlin รองรับการแยกโครงสร้างในพารามิเตอร์ของแลมบ์ดา: map.forEach { (k, v) -> ... } สำหรับ data class ในพารามิเตอร์ของแลมบ์ดา ต้องเพิ่มวงเล็บซ้อนรอบพารามิเตอร์
Kotlin รองรับตัวแปรได้สูงสุด 5 ตัว ใน destructuring declaration หากจำเป็นต้องแยกมากกว่าห้าคุณสมบัติ ให้รวมเข้าด้วยกันใน data class ซ้อน หรือใช้ Pair/Triple ภายในตัวแปรเดียว
บทสรุป
เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร
IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ