Historia pytania:
Wraz z rosnącą popularnością programowania funkcyjnego i wyrażeń lambda w JVM pojawił się problem narzutów związanych z anonimowymi obiektami i dodatkowymi wywołaniami metod. W Javie podobny problem występuje podczas używania interfejsów funkcyjnych. W Kotlinie wprowadzono słowo kluczowe inline, aby uniknąć zbędnych alokacji podczas przekazywania funkcji lambda.
Problem:
Wywołanie funkcji z parametrami lambda tworzy anonimowe obiekty na stercie i zwiększa głębokość stosu, co spowalnia wykonywanie kodu, szczególnie przy intensywnym użyciu w pętlach i zagnieżdżonych wywołaniach.
Rozwiązanie:
Kotlin pozwala zadeklarować funkcję z modyfikatorem inline, po czym ciało funkcji i przekazane do niej lambdy są wstawiane bezpośrednio w miejsce wywołania podczas kompilacji. Pozwala to kompilatorowi pozbyć się dodatkowych alokacji i zwiększyć wydajność, szczególnie w przypadku krótkich, często wywoływanych funkcji (np. filtrowanie kolekcji).
Przykład kodu:
inline fun <T> Iterable<T>.myFilter(predicate: (T) -> Boolean): List<T> { val result = mutableListOf<T>() for (item in this) if (predicate(item)) result.add(item) return result } val filtered = listOf(1, 2, 3, 4).myFilter { it % 2 == 0 } println(filtered) // [2, 4]
Kluczowe cechy:
noinline i crossinline do kontroli inlining’u poszczególnych lambd.Czy można używać funkcji inline z dowolnym parametrem funkcyjnym lub typu ogólnego T?
Nie, funkcje inline przede wszystkim oszczędzają na narzucie wywołań dla parametrów lambda, ale sam parametr ogólny (np. T) nie jest inlinowany — do tego potrzebny jest modyfikator reified. Dla prostych T bez reified, informacja o typie zostanie usunięta na etapie kompilacji.
Co się stanie, jeśli w funkcji inline zadeklaruję zamknięcie na zmienne z zewnętrznego zasięgu?
Zmienne z zewnętrznego zasięgu są kopiowane do wnętrza inlinowanej wyrażenia. Wszystkie odniesienia do nich będą działać tak, jakby kod został rzeczywiście wstawiony. Może to prowadzić do nieoczekiwanego zachowania, jeśli nie spodziewasz się efektów ubocznych.
Czy można wywoływać funkcję inline z kodu Java?
Tak, ale zostanie skompilowana jako zwykła funkcja i kod Java nie zobaczy żadnych korzyści z inlining’u. Kotlin osiąga optymalizację tylko przy użyciu funkcji inline z kodu Kotlin.
return w inline-lambdach — nieprawidłowa kontrola przepływuProgramista inline’uje długą funkcję z wieloma lambdami filtrującymi i post-processującymi, licząc na przyspieszenie. Kod kompiluje się długo, ostateczny plik APK/RAR/DEX znacznie się zwiększa, a przy tym nie ma przyrostu w szybkości.
Plusy:
Minusy:
Wprowadzono małą funkcję inline dla krótkich filtrów tablicy, wywoływaną setki razy w gorącej pętli — czas wykonania jest krytyczny. Ilość alokacji pamięci jest minimalna, ponieważ lambda nie tworzy anonimowego obiektu.
Plusy:
Minusy: