Ce face inline, noinline și crossinline
O funcție marcată cu inline nu este apelată normal: compilatorul copiază corpul ei, împreună cu lambda-urile primite, direct în locul apelului. Așa nu se mai creează obiecte pentru lambda, iar codul cu filter, map sau forEach (toate inline) este la fel de rapid ca o buclă scrisă de mână. Avantaj suplimentar: în lambda primită de o funcție inline poți folosi return simplu, care iese din funcția exterioară (non-local return).
noinline se pune pe un parametru lambda care nu trebuie copiat, de obicei pentru că vrei să-l păstrezi într-o variabilă sau să-l trimiți mai departe unei funcții obișnuite. Un parametru inline nu poate fi păstrat ca valoare.
crossinline se pune pe un parametru lambda care va fi apelat din alt context (dintr-un obiect, din altă lambda, dintr-un fir de execuție). Lambda rămâne inline, dar în ea nu mai este permis return simplu, pentru că nu ar avea din ce funcție să iasă.
Exemple
Exemplul 1: Funcție inline și return din lambda
pentruFiecare este inline, deci return din lambda iese direct din cautaPrimulRestant, nu doar din lambda.
inline fun pentruFiecare(elevi: List<Pair<String, Int>>, actiune: (Pair<String, Int>) -> Unit) {
for (e in elevi) actiune(e)
}
fun cautaPrimulRestant(elevi: List<Pair<String, Int>>): String {
pentruFiecare(elevi) { (nume, nota) ->
println("Verific $nume")
if (nota < 5) return nume
}
return "nimeni"
}
fun main() {
val clasa = listOf("Ana" to 9, "Mihai" to 4, "Ioana" to 3)
println("Primul restant: ${cautaPrimulRestant(clasa)}")
}
Rezultat
Verific Ana
Verific Mihai
Primul restant: Mihai
Exemplul 2: noinline: lambda păstrată pentru mai târziu
laFinal este pus într-o listă, deci nu poate fi inline. acum este copiat în locul apelului.
val deRulatLaFinal = mutableListOf<() -> Unit>()
inline fun pas(nume: String, acum: () -> Unit, noinline laFinal: () -> Unit) {
println("Pas: $nume")
acum()
deRulatLaFinal.add(laFinal)
}
fun main() {
pas("descarc fișierul", { println(" descărcat") }, { println("Șterg fișierul temporar") })
pas("trimit email", { println(" trimis") }, { println("Închid conexiunea") })
println("Curățenie:")
deRulatLaFinal.forEach { it() }
}
Rezultat
Pas: descarc fișierul
descărcat
Pas: trimit email
trimis
Curățenie:
Șterg fișierul temporar
Închid conexiunea
Exemplul 3: crossinline: lambda apelată din alt obiect
bloc este apelat din interiorul unui obiect Runnable, deci trebuie marcat crossinline. În el poți folosi doar return@..., nu return simplu.
inline fun sarcina(nume: String, crossinline bloc: () -> String): Runnable =
Runnable { println("[$nume] ${bloc()}") }
fun main() {
val oras = "Brașov"
val sarcini = listOf(
sarcina("vreme") { "În $oras sunt 18 grade" },
sarcina("trafic") { "Drum liber spre $oras" },
)
for (s in sarcini) s.run()
}
Rezultat
[vreme] În Brașov sunt 18 grade
[trafic] Drum liber spre Brașov
Greșeli frecvente
- Marcarea cu
inlinea unei funcții fără parametri lambda: nu câștigi nimic, iar compilatorul avertizează (expected performance impact from inlining is insignificant; cu-Werrorbuild-ul se oprește). - Funcții
inlinefoarte mari, apelate în multe locuri: corpul se copiază la fiecare apel și programul crește. - Folosirea unui parametru inline ca valoare (
lista.add(actiune)): eroareillegal usage of inline parameter, iar compilatorul îți sugerează:Add 'noinline' modifier to the parameter declaration.
Comenzi înrudite
lambda: O lambda este o funcție fără nume, scrisă între acolade, pe care o poți pune într-o variabilă sau trimite altei funcții.reified: reified păstrează tipul generic T la rulare într-o funcție inline, ca să poți scrie x is T sau T::class.Funcții de ordin superior: O funcție de ordin superior primește o funcție ca parametru sau returnează o funcție.return: Încheie funcția curentă și întoarce o valoare apelantului; cu etichetă (return@forEach) iese doar din lambda.value class: value class învelește o singură valoare într-un tip nou, pentru siguranță la compilare, fără costul unui obiect la rulare.