Lambda คือฟังก์ชันนิรนามที่แสดงเป็นนิพจน์ขนาดกะทัดรัด { อาร์กิวเมนต์ -> เนื้อหา } ใน Kotlin แลมบ์ดาเป็นวิธีหลักในการส่งผ่านพฤติกรรมไปยังฟังก์ชันลำดับสูง โดยให้ไวยากรณ์ที่กระชับโดยไม่ต้องประกาศฟังก์ชันแยกต่างหาก ตาม เอกสารของ Kotlin (2026) มากกว่า 70% ของฟังก์ชันลำดับสูงในโครงการทั่วไปถูกเรียกใช้ด้วยแลมบ์ดา Lambda ช่วยให้เขียนโค้ดในรูปแบบฟังก์ชันด้วยสัญญาณรบกวนทางไวยากรณ์น้อยที่สุด
ประเด็นสำคัญ
Lambda (นิพจน์ lambda) คือฟังก์ชันนิรนามที่กำหนดเป็นนิพจน์แทนการประกาศ คำนี้มีที่มาจากแคลคูลัสแลมบ์ดาของ Alonzo Church (1936) ในการเขียนโปรแกรม แลมบ์ดาคือบล็อกของโค้ดที่สามารถส่งผ่านเป็นค่า เก็บไว้ในตัวแปร และเรียกใช้ในภายหลัง ใน Kotlin แลมบ์ดาจะอยู่ในวงเล็บปีกกา { } เสมอ โดยอาร์กิวเมนต์จะถูกแยกจากเนื้อหาด้วยลูกศร ->
แลมบ์ดาแตกต่างจากฟังก์ชันปกติตรงที่ไม่มี ชื่อ และคีย์เวิร์ด fun ซึ่งช่วยให้ประกาศพฤติกรรมได้โดยตรงที่จุดใช้งาน โดยไม่ทำให้โค้ดรกด้วยการประกาศเพิ่มเติม คอมไพเลอร์ Kotlin อนุมานชนิดของอาร์กิวเมนต์จากบริบท ดังนั้นในหลายกรณีจึงสามารถละเว้นชนิดได้ แลมบ์ดาคือน้ำตาลทางไวยากรณ์เหนือชนิดฟังก์ชัน (T) -> R: ทุกแลมบ์ดามีชนิดฟังก์ชันที่สอดคล้องกัน
การใช้งานหลักของแลมบ์ดาใน Kotlin คือการส่งต่อไปยัง ฟังก์ชันลำดับสูง ไลบรารีมาตรฐานใช้แลมบ์ดาทุกที่: filter { it > 0 }, map { it.uppercase() }, forEach { println(it) } แลมบ์ดายังใช้สำหรับตัวฟังเหตุการณ์ คอลแบ็ก และตัวกำหนดค่า object ผ่าน apply และ run ทำให้โค้ดเป็นเชิงประกาศมากขึ้นเมื่อเทียบกับคลาสนิรนาม
รูปแบบเต็มของ นิพจน์ lambda ใน Kotlin: { อาร์กิวเมนต์ -> เนื้อหา_lambda } อาร์กิวเมนต์จะถูกเรียงคั่นด้วยเครื่องหมายจุลภาค ตามด้วยลูกศรและเนื้อหา — หนึ่งนิพจน์หรือมากกว่า หากเนื้อหามีหลายบรรทัด นิพจน์สุดท้ายจะกลายเป็นค่าที่ส่งกลับ หากฟังก์ชันรับแลมบ์ดาเป็นอาร์กิวเมนต์สุดท้าย สามารถย้ายออกนอกวงเล็บเรียกใช้ได้
val sum: (Int, Int) -> Int = { a, b -> a + b }
// ชนิดที่อนุมานจากบริบท
val sumShort = { a: Int, b: Int -> a + b }
// การเรียก Lambda
val result = sum(5, 3) // 8
println(result)
ตัวแปร sum ถูกประกาศด้วยชนิดฟังก์ชันที่ชัดเจน (Int, Int) -> Int และเริ่มต้นด้วยแลมบ์ดา { a, b -> a + b } สำหรับ sumShort ชนิดถูกระบุภายในแลมบ์ดาและชนิดตัวแปรถูกอนุมาน ทั้งสองรูปแบบเทียบเท่ากัน การเรียกใช้แลมบ์ดาไม่แตกต่างจากการเรียกใช้ฟังก์ชันปกติ — โดยใช้ไวยากรณ์ sum(5, 3) แลมบ์ดาที่ไม่มีอาร์กิวเมนต์เขียนเป็น { body } มีหนึ่งอาร์กิวเมนต์ — { it -> it * 2 } หรือเพียง { it * 2 }
เมื่อแลมบ์ดารับอาร์กิวเมนต์เพียงหนึ่งตัว Kotlin จะให้ชื่อโดยนัย it ซึ่งช่วยลดความจำเป็นในการประกาศอาร์กิวเมนต์ทางด้านซ้าย นักพัฒนาเขียนเพียงเนื้อหาแลมบ์ดา โดยใช้ it เพื่อเข้าถึงพารามิเตอร์เดียว ไวยากรณ์นี้สะดวกเป็นพิเศษในห่วงโซ่การประมวลผลคอลเล็กชัน where it หมายถึงองค์ประกอบปัจจุบัน
val numbers = listOf(1, 2, 3, 4, 5)
// ชื่อพารามิเตอร์ที่ชัดเจน
val squaredExplicit = numbers.map { n -> n * n }
// it โดยนัย
val squared = numbers.map { it * it }
// การกรองด้วย it
val even = numbers.filter { it % 2 == 0 }
println(squared) // [1, 4, 9, 16, 25]
ในตัวอย่าง map { it * it } ใช้ it แทนการประกาศ n -> Kotlin กำหนด it โดยอัตโนมัติสำหรับพารามิเตอร์แลมบ์ดาตัวเดียว หากแลมบ์ดารับสองอาร์กิวเมนต์ขึ้นไป it จะไม่ถูกสร้าง — ต้องประกาศพารามิเตอร์ทั้งหมดอย่างชัดเจน เพื่อความอ่านง่ายของแลมบ์ดาที่ซ้อนกัน ควรตั้งชื่อพารามิเตอร์ที่มีความหมายและใช้ it เฉพาะในเพรดิเคตสั้นๆ บรรทัดเดียว
Trailing lambda คือคุณสมบัติทางไวยากรณ์ของ Kotlin ที่อนุญาตให้ย้ายอาร์กิวเมนต์แลมบ์ดาตัวสุดท้ายออกนอกวงเล็บเรียกใช้ฟังก์ชัน ทำให้โค้ดดูเหมือนโครงสร้างภาษาที่มีอยู่ในตัว หากแลมบ์ดาเป็นอาร์กิวเมนต์เดียว สามารถละเว้นวงเล็บได้ทั้งหมด วิธีนี้ถูกใช้อย่างแข็งขันในไลบรารีมาตรฐานและ DSL
// การเรียกมาตรฐาน
thread(block = {
println("สวัสดีจากเธรด")
})
// เวอร์ชัน trailing lambda
thread {
println("สวัสดีจากเธรด")
}
// ทำซ้ำกับ trailing lambda
repeat(3) { index ->
println("การวนซ้ำ $index")
}
// เรียกใช้กับแลมบ์ดาหลายบรรทัด
val config = run {
val host = "localhost"
val port = 8080
"$host:$port"
}
ฟังก์ชัน thread รับ Runnable เป็นอาร์กิวเมนต์สุดท้าย ด้วย trailing lambda ทำให้สามารถส่งแลมบ์ดานอกวงเล็บได้ ซึ่งมองเห็นเป็นบล็อกโค้ด ฟังก์ชัน repeat รับจำนวนการวนซ้ำและแลมบ์ดาพร้อมดัชนี run เป็นตัวอย่างของฟังก์ชันลำดับสูงที่แลมบ์ดาหลายบรรทัดคำนวณค่าที่ส่งกลับโดยนิพจน์สุดท้าย วงเล็บรอบ run() สามารถละเว้นได้หากแลมบ์ดาเป็นอาร์กิวเมนต์เดียว
การจับตัวแปร (closure) คือความสามารถของแลมบ์ดาในการเข้าถึงตัวแปรที่ประกาศในขอบเขตภายนอก ใน Kotlin แลมบ์ดาสามารถอ่านและแก้ไขตัวแปร var รวมถึงอ่านตัวแปร val จากบริบทโดยรอบ ตัวแปรที่ถูกจับจะถูกเก็บไว้กับแลมบ์ดาตลอดวงจรชีวิตของมัน ทำให้สามารถสร้างฟังก์ชันที่มีสถานะได้
fun makeCounter(): () -> Int {
var count = 0
return {
count++ // จำนวนการจับ
}
}
fun main() {
val counter = makeCounter()
println(counter()) // 0
println(counter()) // 1
println(counter()) // 2
}
ฟังก์ชัน makeCounter ประกาศตัวแปรเฉพาะที่ count และส่งคืนแลมบ์ดา { count++ } แม้ว่า makeCounter จะทำงานเสร็จสิ้นแล้ว แลมบ์ดาที่ส่งคืนยังคงเก็บการอ้างอิงถึง count ผ่านการปิด แต่ละครั้งที่เรียก counter() จะเพิ่มและส่งคืนค่าปัจจุบัน กลไก closure เป็นพื้นฐานของ currying โรงงานฟังก์ชัน และ memoization สิ่งสำคัญคือต้องจำว่าการจับตัวแปรที่เปลี่ยนแปลงได้อาจนำไปสู่พฤติกรรมที่ไม่คาดคิดในการทำงานพร้อมกัน
คำถามที่พบบ่อย
Lambda ไม่สนับสนุน return แบบไม่มีเงื่อนไข — return ที่ไม่มีป้ายกำกับจะส่งคืนการควบคุมไปยังฟังก์ชันภายนอก หากต้องการออกจากแลมบ์ดา ให้ใช้ return@label โดยที่ label คือชื่อของฟังก์ชันลำดับสูงที่แลมบ์ดาถูกส่งไป
ฟังก์ชันนิรนาม เขียนเป็น fun(a: Int): Int { return a * 2 } และสนับสนุน return โดยไม่มีป้ายกำกับ แลมบ์ดา { a -> a * 2 } สั้นกว่าในไวยากรณ์แต่ต้องใช้ return@label สำหรับการออกก่อนกำหนด
หากแลมบ์ดาไม่รับอาร์กิวเมนต์ ลูกศรจะถูกละเว้น: { body() } หากรับหนึ่งอาร์กิวเมนต์แต่ไม่ได้ใช้ ให้เขียน { _ -> body() } ด้วยขีดล่างเพื่อละเว้นพารามิเตอร์
ใช่ ทุกแลมบ์ดาที่ส่งไปยังฟังก์ชันที่ไม่ใช่ inline จะสร้างอินสแตนซ์ของ คลาสนิรนาม สำหรับฟังก์ชัน inline แลมบ์ดาจะถูกแทรกที่ตำแหน่งเรียกใช้โดยไม่สร้างออบเจกต์ แลมบ์ดาที่ไม่มีการจับตัวแปรจะถูกแคชเป็นออบเจกต์ซิงเกิลตัน
Return ใน Kotlin คือตัวดำเนินการกระโดดที่เป็นของฟังก์ชันที่ใกล้ที่สุดที่ประกาศด้วย fun แลมบ์ดาไม่ใช่ฟังก์ชัน (fun) แต่เป็นนิพจน์ ดังนั้น return ภายในแลมบ์ดาที่ไม่มีป้ายกำกับจะถูกถือว่าเป็นการส่งกลับจากฟังก์ชันภายนอกที่ครอบอยู่
สรุป
เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร
IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ
อ่านเพิ่มเติม