ฟังก์ชันลำดับที่สูงขึ้นมีประโยชน์มากและสามารถปรับปรุงreusability
โค้ดได้อย่างแท้จริง อย่างไรก็ตามหนึ่งในข้อกังวลที่ใหญ่ที่สุดเกี่ยวกับการใช้งานคือประสิทธิภาพ นิพจน์แลมบ์ดาถูกคอมไพล์ไปยังคลาส (มักไม่ระบุชื่อคลาส) และการสร้างอ็อบเจ็กต์ใน Java เป็นการดำเนินการที่หนักหน่วง เรายังคงสามารถใช้ฟังก์ชันลำดับที่สูงกว่าได้อย่างมีประสิทธิภาพในขณะที่ยังคงรักษาประโยชน์ทั้งหมดไว้ด้วยการสร้างฟังก์ชันแบบอินไลน์
นี่คือฟังก์ชันอินไลน์ในรูปภาพ
เมื่อฟังก์ชันถูกทำเครื่องหมายเป็นinline
ในระหว่างการคอมไพล์โค้ดคอมไพลเลอร์จะแทนที่การเรียกฟังก์ชันทั้งหมดด้วยเนื้อหาจริงของฟังก์ชัน นอกจากนี้นิพจน์แลมบ์ดาที่ระบุเป็นอาร์กิวเมนต์จะถูกแทนที่ด้วยเนื้อความจริง จะไม่ถือว่าเป็นฟังก์ชัน แต่เป็นรหัสจริง
ในระยะสั้น: -อินไลน์ -> แทนที่จะถูกเรียกพวกเขาจะถูกแทนที่ด้วยรหัสเนื้อหาของฟังก์ชันในเวลาคอมไพล์ ...
ใน Kotlin การใช้ฟังก์ชันเป็นพารามิเตอร์ของฟังก์ชันอื่น (เรียกว่าฟังก์ชันลำดับที่สูงกว่า) ให้ความรู้สึกเป็นธรรมชาติมากกว่าใน Java
แม้ว่าการใช้ lambdas จะมีข้อเสียอยู่บ้าง เนื่องจากเป็นคลาสที่ไม่ระบุตัวตน (ดังนั้นจึงเป็นวัตถุ) พวกเขาจึงต้องการหน่วยความจำ (และอาจเพิ่มจำนวนวิธีการโดยรวมของแอปของคุณด้วย) เพื่อหลีกเลี่ยงปัญหานี้เราสามารถสอดแทรกวิธีการของเราได้
fun notInlined(getString: () -> String?) = println(getString())
inline fun inlined(getString: () -> String?) = println(getString())
จากตัวอย่างข้างต้น : - ฟังก์ชันทั้งสองนี้ทำหน้าที่เหมือนกันทุกประการ - การพิมพ์ผลลัพธ์ของฟังก์ชัน getString หนึ่งเป็นแบบอินไลน์และอีกอันไม่ใช่
หากคุณตรวจสอบโค้ด java ที่ถอดรหัสแล้วคุณจะเห็นว่าวิธีการนั้นเหมือนกันทั้งหมด นั่นเป็นเพราะคำหลักแบบอินไลน์เป็นคำสั่งให้คอมไพเลอร์คัดลอกโค้ดไปยังไซต์การโทร
อย่างไรก็ตามหากเรากำลังส่งประเภทฟังก์ชันใด ๆ ไปยังฟังก์ชันอื่นดังต่อไปนี้:
//Compile time error… Illegal usage of inline function type ftOne...
inline fun Int.doSomething(y: Int, ftOne: Int.(Int) -> Int, ftTwo: (Int) -> Int) {
//passing a function type to another function
val funOne = someFunction(ftOne)
/*...*/
}
เพื่อแก้ปัญหานั้นเราสามารถเขียนฟังก์ชันของเราใหม่ได้ดังนี้:
inline fun Int.doSomething(y: Int, noinline ftOne: Int.(Int) -> Int, ftTwo: (Int) -> Int) {
//passing a function type to another function
val funOne = someFunction(ftOne)
/*...*/}
สมมติว่าเรามีฟังก์ชันลำดับที่สูงกว่าดังต่อไปนี้:
inline fun Int.doSomething(y: Int, noinline ftOne: Int.(Int) -> Int) {
//passing a function type to another function
val funOne = someFunction(ftOne)
/*...*/}
ที่นี่คอมไพเลอร์จะบอกให้เราไม่ใช้คีย์เวิร์ดแบบอินไลน์เมื่อมีพารามิเตอร์แลมบ์ดาเพียงตัวเดียวและเรากำลังส่งต่อไปยังฟังก์ชันอื่น ดังนั้นเราสามารถเขียนฟังก์ชันด้านบนใหม่ได้ดังนี้:
fun Int.doSomething(y: Int, ftOne: Int.(Int) -> Int) {
//passing a function type to another function
val funOne = someFunction(ftOne)
/*...*/
}
หมายเหตุ : - เราต้องลบคีย์เวิร์ด noinline ด้วยเพราะสามารถใช้ได้กับฟังก์ชันอินไลน์เท่านั้น!
สมมติว่าเรามีฟังก์ชันดังนี้ ->
fun intercept() {
// ...
val start = SystemClock.elapsedRealtime()
val result = doSomethingWeWantToMeasure()
val duration = SystemClock.elapsedRealtime() - start
log(duration)
// ...}
วิธีนี้ใช้งานได้ดี แต่เนื้อของตรรกะของฟังก์ชันนั้นปนเปื้อนด้วยรหัสการวัดทำให้เพื่อนร่วมงานของคุณทำงานได้ยากขึ้น :)
นี่คือวิธีที่ฟังก์ชันอินไลน์สามารถช่วยโค้ดนี้ได้:
fun intercept() {
// ...
val result = measure { doSomethingWeWantToMeasure() }
// ...
}
inline fun <T> measure(action: () -> T) {
val start = SystemClock.elapsedRealtime()
val result = action()
val duration = SystemClock.elapsedRealtime() - start
log(duration)
return result
}
ตอนนี้ฉันสามารถมีสมาธิในการอ่านสิ่งที่เจตนาหลักของฟังก์ชัน intercept () คือโดยไม่ต้องข้ามบรรทัดของโค้ดการวัด นอกจากนี้เรายังได้รับประโยชน์จากตัวเลือกในการนำรหัสนั้นไปใช้ซ้ำในที่อื่น ๆ ที่เราต้องการ
อินไลน์ช่วยให้คุณสามารถเรียกใช้ฟังก์ชันที่มีอาร์กิวเมนต์แลมบ์ดาภายในการปิด ({... }) แทนที่จะส่งแลมด้าเหมือนการวัด (myLamda)