Функції вищого порядку дуже корисні, і вони дійсно можуть покращити reusability
код. Однак однією з найбільших проблем щодо їх використання є ефективність. Лямбда-вирази компілюються в класи (часто анонімні класи), а створення об'єктів на Java - це важка операція. Ми все ще можемо ефективно використовувати функції вищого порядку, зберігаючи всі переваги, роблячи вбудовані функції.
тут вводиться функція вбудованого зображення
Коли функція позначена як inline
, під час компіляції коду компілятор замінить усі виклики функцій фактичним складом функції. Також лямбда-вирази, подані в якості аргументів, замінюються їх фактичним тілом. Вони будуть розглядатися не як функції, а як фактичний код.
Якщо коротко: - Inline -> замість того, щоб викликати їх, вони замінюються кодом тіла функції під час компіляції ...
У Котліні використання функції в якості параметра іншої функції (так званих функцій вищого порядку) відчуває себе природніше, ніж у Java.
Однак використання лямбдашів має деякі недоліки. Оскільки вони анонімні класи (і, отже, об’єкти), їм потрібна пам'ять (і навіть може додати до загальної кількості методів вашого додатка). Щоб цього уникнути, ми можемо вкласти свої методи.
fun notInlined(getString: () -> String?) = println(getString())
inline fun inlined(getString: () -> String?) = println(getString())
З наведеного вище прикладу : - Ці дві функції роблять точно те саме - друкуючи результат функції getString. Один накреслений, а один - ні.
Якщо ви перевірите декомпільований код Java, ви побачите, що методи абсолютно ідентичні. Це тому, що ключове слово inline - це інструкція компілятору скопіювати код на сайт виклику.
Однак якщо ми передаємо будь-який тип функції іншій функції, як нижче:
//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)
/*...*/}
Тут компілятор скаже нам не використовувати ключове слово inline, коли є лише один лямбда-параметр, і ми передаємо його іншій функції. Отже, ми можемо переписати вище функцію, як показано нижче:
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 (), не пропускаючи рядки коду вимірювання. Ми також виграємо від можливості повторного використання цього коду в інших місцях, де ми хочемо
inline дозволяє викликати функцію з аргументом лямбда в межах закриття ({...}), а не передавати міру, подібну лямбда (myLamda)