Higher-order function คือฟังก์ชันที่รับฟังก์ชันอื่นเป็นพารามิเตอร์หรือคืนฟังก์ชันเป็นผลลัพธ์ใน Kotlin ฟังก์ชันเป็นพลเมิงของคลาสแรก: สามารถเก็บในตัวแปร ส่งเป็นอาร์กิวเมนต์ และคืนได้ ตาม เอกสาร Kotlin (2026) higher-order functions ช่วยลดการทำซ้ำโค้ดโดยเฉลี่ย 30% เมื่อเทียบกับวิธีการแบบิมเพราทีฟ Higher-order function เป็นแนวคิดพื้นฐานของรูปแบบฟังก์ชันในการพัฒนาสมัยใหม่
ประเด็นสำคัญ
Higher-order function คือฟังก์ชันที่มีอย่างน้อยหนึ่งในสองคุณลักษณะ: การรับฟังก์ชันอื่นเป็นอาร์กิวเมนต์หรือการคืนฟังก์ชันเป็นผลลัพธ์ ในคณิตศาสตร์ ฟังก์ชันเหล่านี้เรียกว่าฟังก์ชันนัลหรือตัวดำเนินการ ในการเขียนโปรแกรม ฟังก์ชันเหล่านี้ปรากฎตั้งแต่ปี 1958 ใน Lisp และกลายเป็นส่วนบังคับของภาษาสมัยใหม่ทั้งหมด — JavaScript, Python, Swift, Kotlin, Scala และ Haskell Higher-order function ช่วยให้สามารถทำนามธรรมจากการดำเนินการเฉพาะและ ส่งพฤติกรรมเป็นค่า
ความแตกต่างหลักระหว่าง higher-order function กับฟังก์ชันทั่วไปคือการมีพารามิเตอร์ที่มี ชนิดฟังก์ชัน หรือค่าฟังก์ชันที่คืนมา ใน Kotlin ชนิดฟังก์ชันเขียนเป็น (ParamType) -> ReturnType ตัวอย่าง ชนิด (Int) -> String หมายถึงฟังก์ชันที่รับ Int และคืน String ชนิด () -> Unit หมายถึงฟังก์ชันที่ไม่มีพารามิเตอร์และไม่คืนค่าที่มีประโยชน์ ระบบชนิดนี้เองที่ทำให้ higher-order functions มั่นความปลอดภัยด้านชนิด
สิ่งที่ตรงกันข้ามของ higher-order function คือ first-class function (ฟังก์ชันลำดับหนึ่ง) First-class function หมายความว่าสามารถใช้ฟังก์ชันเหมือนค่าอื่น ๆ: กำหนดค่าให้ตัวแปร เก็บในคอลเล็กชั่น ส่งเป็นอาร์กิวเมนต์ Higher-order function คือฟังก์ชันที่ใช้ first-class functions ในการรับหรือคืน Kotlin รองรับทั้งสองแนวคิดในระดับภาษาโดยไม่ต้องใช้ไลบรารีเพิ่มเติม
ใน Kotlin ฟังก์ชันใด ๆ ที่มีอย่างน้อยหนึ่งพารามิเตอร์ที่ประกาศด้วยชนิดฟังก์ชันหรือชนิดที่คืนเป็นชนิดฟังก์ชันจะถูกถือว่าเป็น higher-order function โดยอัตโนมัติ คอมไพเลอร์ไม่ต้องการอะไรพิเศษ — แค่ระบุ (T) -> R ในลายเซ็นก็พอ เมื่อเรียกฟังก์ชันดังกล่าว อาร์กิวเมนต์จะเป็นนิพจน์ lambda การอ้างอิงไปยังฟังก์ชันที่มีอยู่แล้วผ่าน :: หรือค่าฟังก์ชันที่เก็บไว้ในตัวแปร
fun operate(a: Int, b: Int, op: (Int, Int) -> Int): Int {
return op(a, b)
}
fun main() {
val sum = operate(10, 20) { x, y -> x + y }
println(sum) // 30
}
ในรายการ ฟังก์ชัน operate รับจำนวนเต็มสองตัวและพารามิเตอร์ op ชนิด (Int, Int) -> Int ตัวฟังก์ชันเรียกการดำเนินการที่ส่งมาในบรรทัดเดียว ใน main การเรียก operate ส่ง lambda { x, y -> x + y } — Kotlin วางไว้หลังวงเล็บโดยใช้ไวยากรณ์ trailing lambda หาก lambda เป็นอาร์กิวเมนต์สุดท้าย จะสามารถย้ายไปนอกวงเล็บได้อย่างสมบูรณ์ ซึ่งช่วยให้อ่านโซ่โฟร์เชนของสายโส้การเรียกง่ายขึ้น
ภายใต้ Kotlin คอมไพล์ higher-order functions ผ่าน อินเทอร์เฟส Function (Function2 สำหรับสองพารามิเตอร์) แต่ละ lambda กลายเป็นคลาสนิรนามที่ใช้งานอินเทอร์เฟส FunctionN ที่สอดคล้องกัน ซึ่งหมายความว่าการสร้าง lambda ต้องจัดสรรอบเจกส์สำหรับแต่ละการดำเนินการ เพื่อลดโอเวอร์เฮ็ด Kotlin รองรัฟังก์ชัน inline ซึ่งแทนที่ตัวของ higher-order function ที่ตำแหน่งที่เรียก ซึ่งช่วยขจัดการสร้างคลาสนิรนาม
การส่งฟังก์ชันเป็น อาร์กิวเมนต์ เป็นรูปแบบที่พบบ่อยที่สุดของการใช้ higher-order functions แทนที่จะสร้างลำดับชั้นคลาสโดยใช้เมธอดพอลิมอร์ฟิสม์ นักพัฒนาสามารถส่งพฤติกรรมที่ต้องการไปยังจุดที่ใช้งานโดยตรง ซึ่งเป็นการใช้หลักเปิดปิด (Open/Closed Principle) โดยไม่ต้องใช้การสืบทอด: การดำเนินการใหม่จะถูกเพิ่มเป็น lambda ใหม่ ไม่ใช่คลาสย่อยใหม่
สำหรับฟังก์ชันที่สามารถเป็น null ได้ Kotlin ใช้ ชนิด ((T) -> R)? โดยมีเครื่องหมายคำถามหลังวงเล็บ ฟังก์ชันดังกล่าวสามารถเรียกได้หลังจากการตรวจสอบ null หรือผ่านตัวดำเนินการ ?.invoke() ในกรณนี้ ลายเซ็นของ higher-order function ระบุอย่างชัดเจนว่าการส่งฟังก์ชันเป็นตัวเลือก — โค้ดที่เรียกสามารถลดอาร์กิวเมนต์ได้ ซึ่งมีประโยชน์สำหรับคอล์แบ็กและตัวจัดการอีเวนต์ที่มีพฤติกรรมเป็นตัวเลือก
fun <T> List<T>.customFilter(
predicate: (T) -> Boolean
): List<T> {
val result = mutableListOf<T>()
for (item in this) {
if (predicate(item)) result.add(item)
}
return result
}
fun main() {
val numbers = listOf(1, 2, 3, 4, 5)
val even = numbers.customFilter { it % 2 == 0 }
println(even) // [2, 4]
}
ฟังก์ชันส่วนขยาย customFilter รับพารามิเตอร์ predicate ของชนิดฟังก์ชัน (T) -> Boolean ภายในลูป มันเรียก predicate สำหรับแต่ละองค์ประกอบและรวบรวมองค์ประกอบที่ตรงกัน การเรียกใน main ส่ง lambda { it % 2 == 0 } โดยที่ it คือชื่อแฝงนิมนิพจน์ของพารามิเตอร์แต่งเดียวของ lambda ด้วย higher-order functions ทรรกฟิลเตอร์จึงแยกออกจากกลไการเดินข้ามคอลเล็กชั่นอย่างสมบูรณ์
Higher-order function สามารถ คืนฟังก์ชัน — รูปแบบนี้เรียกว่าโรงงานฟังก์ชันหรือตัวสร้างพฤติกรรม ฟังก์ชันที่คืนมาสามารถจับ (closure) ตัวแปรจากขอบเขตภายนอก โดยรักษาไว้ระหว่างการเรียกแต่ละครั้ง ซึ่งช่วยให้สามารถสร้างตัวจัดการที่กำหนดค่าได้และการดำเนินการเฉพาะทางโดยอิงตามแม่แบบทั่วไป
เมื่อคืนฟังก์ชัน Kotlin อนุมาน ชนิดฟังก์ชัน ((T) -> R)? ที่คืนจากลายเซ็น คอมไพเลอร์จะตรวจสอบว่านิพจน์ return ทั้งหมดในตัวฟังก์ชันคืนค่าฟังก์ชันที่เข้ากันได้ ตัวแปรที่ถูกจับจะถูกเก็บไว้ในอ็อบเจกต์ lambda และสามารถเข้าถึงได้ตราบใดที่มีการอ้างอิงถึงมัน นี่เป็นกลไกที่มีพลัง แต่ต้องใส่ใจการจัดการหน่วยความจำ
fun makeMultiplier(factor: Int): (Int) -> Int {
return { x -> x * factor }
}
fun main() {
val double = makeMultiplier(2)
val triple = makeMultiplier(3)
println(double(5)) // 10
println(triple(5)) // 15
}
ฟังก์ชัน makeMultiplier รับตัวประกอบ factor และคืน lambda { x -> x * factor } โดยที่ factor ถูกจับจากขอบเขตภายนอก (closure) แต่ละการเรียก makeMultiplier สร้างฟังก์ชันใหม่โดยมีค่า factor ของตนเอง ตัวแปร double และ triple เก็บฟังก์ชันที่คืนมาและสามารถเรียกได้หลายครั้ง รูปแบบนี้ใช้อย่างแพร่หลายในการกำหนดค่าไคลเอนต์ HTTP ตัวตกแต่ง แล้ะมิดเดิลแวร์
การใช้ higher-order functions ร่วมกับ lambda ช่วยให้สามารถสร้างสายโส้การดำเนินการที่สามารถแสดงความหมายได้โดยไม่ต้องใช้ตัวแปรกลาง ไลบรารีมาตรฐานของ Kotlin มี higher-order functions อยู่หลายสิบตัว: let, run, apply, also, filter, map, flatMap, fold, reduce, forEach, groupBy และอื่น ๆ แต่ละตัวรับ lambda และดำเนินการแปลงข้อมูลโดยใช้มัน
data class User(val name: String, val age: Int)
fun main() {
val users = listOf(
User("Alice", 25),
User("Bob", 17),
User("Charlie", 30)
)
val result = users
.filter { it.age >= 18 }
.map { it.name.uppercase() }
.sorted()
println(result) // [ALICE, CHARLIE]
}
ในตัวอย่าง สายโส้ของ higher-order functions สามตัวประมวลผลรายชื่อผู้ใช้ filter รับประโยจน์ เลือกเฉพาะผู้ใหญ่แล้ว map แปลงผู้ใช้แต่ละคนเป็นชื่อตัวพิมพ์ใหญ่ sorted เรียงเรียงผลลัพธ์จากน้อยไปมาก แต่ละการดำเนินการรับ lambda และ Kotlin รับประกันความปลอดภัยด้านชนิดในทุกขั้นตอน หากไม่มี higher-order functions จะต้องเขียนลูปที่มี if รายชื่อชั่วคราว และการเรียงลำดับด้วยตนเอง
คำถามที่พบบ่อย
Higher-order function รับฟังก์ชันอื่นเป็นพารามิเตอร์หรือคืนหนึ่งฟังก์ชัน ฟังก์ชันทั่วไปทำงานกับข้อมูลเท่านั้น — ตัวเลข, สตริง, อ็อบเจกต์ Higher-order function ทำงานกับพฤติกรรม โดยส่งตรรกะเป็นอาร์กิวเมนต์
ได้ ตัวขยาย inline ช่วยขจัดโอเวอร์เฮ็ดของการสร้างคลาสนิรนามสำหรับ lambda โดยใช้ crossinline หรือ noinline คุณสามารถควบคุมได้ว่า lambda ใดบ้างที่ถูก inline และ lambda ใดที่ยังเป็นอ็อบเจกต์
ฟังก์ชันที่ไม่มีพารามิเตอร์และไม่มีค่าที่คืนมีชนิด () -> Unit ฟังก์ชันที่มีหนึ่งพารามิเตอร์ชนิด T และคืน R เขียนเป็น (T) -> R สำหรับสองพารามิเตอร์ — (T, U) -> R และเช่นนี้ไปจนถึง 22 อาร์กิวเมนต์
Lambda คือสัยลักษณ์ย่อ { args -> body } ส่วนฟังก์ชันนิรนามคือ fun(args): ReturnType { body } Lambda ไม่สามารถใช้ return โดยไม่มีฉลาก แต่ฟังก์ชันนิรนามสามารถใช้ได้ ทั้งสองสามารถส่งไปยัง higher-order function ได้
หลีกเลี่ยง higher-order functions ใน ลูปร้อน โดยไม่มี inline — แต่ละ lambda สร้างอ็อบเจกต์ สำหรับโค้ดที่ต้องการประสิทธิภาพสูง ใช้ inline fun อย่าใช้ lambda ที่ซ้อนกันลึกๆ มากเกินไป — สิ่งนี้จะลดความสามารถในการอ่าน
สรุป
เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร
IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ
อ่านเพิ่มเติม