Funkcja inline w Kotlin: co to jest, składnia i zastosowanie

Autor: IT Sectr Opublikowano: 2026-06-21 Czas czytania: 9 min

funkcja inline — mechanizm Kotlin, w którym ciało funkcji jest wstawiane bezpośrednio w każde miejsce wywołania na etapie kompilacji. Eliminuje to narzut związany z tworzeniem anonimowych klas i obiektów dla parametrów lambda. Według Kotlin Documentation, 2025, słowo kluczowe inline jest szczególnie efektywne w przypadku funkcji wyższego rzędu, gdzie każda lambda bez wstawiania tworzy osobny obiekt FunctionN, obciążając garbage collector.

Najważniejsze

  • funkcja inline — funkcja, której ciało jest wstawiane w miejsce wywołania na etapie kompilacji
  • Redukcja narzutu — eliminuje tworzenie anonimowych klas i obiektów dla lambd
  • nonlocal return — lambdy wewnątrz funkcji inline mogą wykonać return z funkcji zewnętrznej
  • noinline — zabrania wstawiania poszczególnych parametrów lambda
  • crossinline — zezwala na nonlocal return, ale zabrania return z kontekstu funkcji inline

Co to jest funkcja inline w Kotlin?

Funkcja inline to funkcja oznaczona słowem kluczowym inline. Kompilator Kotlin nie tworzy dla niej osobnego kodu bajtowego z wywołaniem, ale kopiuje ciało funkcji bezpośrednio do każdego miejsca wywołania. Głównym celem jest optymalizacja funkcji wyższego rzędu, które przyjmują wyrażenia lambda, ponieważ każda lambda w normalnej sytuacji tworzy anonimowy obiekt klasy Function.

Według JetBrains Tech Blog (2024), użycie funkcji inline w Kotlin może zmniejszyć liczbę tworzonych obiektów o 40–60% w funkcjach intensywnie wykorzystujących lambdy. W pętlach i operacjach o wysokim obciążeniu (sortowanie, filtrowanie kolekcji) daje to wymierny wzrost wydajności.

Bez inline każda lambda jest kompilowana do anonimowej klasy (lub instancji syntetyzowanego interfejsu funkcyjnego). Dla lambd przechwytujących zmienne tworzone są dodatkowe obiekty-opakowania. Funkcje inline eliminują wszystkie te obiekty na etapie kompilacji, zastępując je bezpośrednim kodem z dostępem do zmiennych lokalnych bez opakowań.

Używaj inline tylko dla funkcji z parametrami lambda — kompilator Kotlin sam ostrzega, jeśli inline nie daje korzyści.

Składnia funkcji inline i mechanizm działania

Wystarczy dodać słowo kluczowe inline przed deklaracją funkcji. Kompilator automatycznie wstawia ciało funkcji w miejsca wywołania. Sama funkcja nadal istnieje w kodzie bajtowym na wypadek, gdyby była wywoływana niebezpośrednio (np. z kodu Java).

kotlin
inline fun Int.repeatAction(action: (Int) -> Unit) {
    for (i in 0 until this) {
        action(i)
    }
}

// Wywołanie — kod lambdy jest wstawiany do ciała funkcji
5.repeatAction { index ->
    println("Index: $index")
}

Po kompilacji powyższy kod będzie równoważny:

kotlin
// Co dzieje się po wstawieniu (schematycznie):
val $this = 5
for (i in 0 until $this) {
    println("Index: $i")
}

Nie tworzy się żadnych obiektów dla lambdy — kod action jest wykonywany bezpośrednio. To jest istota optymalizacji: zamiast wywołania Function.invoke() — bezpośrednie wstawienie kodu z ciałem lambdy.

Sprawdzenie przez dekompilację

Aby sprawdzić wstawianie, otwórz Tools > Kotlin > Show Kotlin Bytecode w IntelliJ IDEA i kliknij Decompile. Zobaczysz, że zamiast wywołania repeatAction z lambdą generowane jest bezpośrednie wstawienie ciała funkcji z pętlą for.

Problem narzutu lambd i jego rozwiązanie

Każda lambda w Kotlin jest kompilowana do jednego z trzech wariantów. Pierwszy — jeśli lambda nie przechwytuje zmiennych, staje się statyczną metodą klasy, w której jest zadeklarowana. Drugi — jeśli przechwytuje jedną zmienną, tworzona jest anonimowa klasa. Trzeci — jeśli przechwytuje wiele zmiennych, tworzona jest anonimowa klasa z polami dla każdej przechwyconej zmiennej.

Typ lambdyBez inlineZ inline
Bez przechwytywaniaJedna metoda statyczna (współdzielona)Pełne wstawienie, bez wywołania
Z przechwyceniem 1 zmiennejAnonimowa klasa (jeden obiekt)Pełne wstawienie, bez obiektu
Z przechwyceniem N zmiennychAnonimowa klasa z N polamiPełne wstawienie, bez obiektu
RekurencyjnaZwykłe wywołanieinline zabroniony

Według Android Performance Patterns (Google, 2024), w aplikacjach intensywnie wykorzystujących kolekcje (filtrowanie, sortowanie, grupowanie) funkcje inline zmniejszają liczbę alokacji o 25–35%. Efekt jest szczególnie widoczny w Jetpack Compose, gdzie każda zmiana stanu powoduje ponowną kompozycję z wieloma lambdami.

Nonlocal return i ograniczenia

Lambda w zwykłej funkcji nie może wykonać return z funkcji zewnętrznej — tylko lokalny return z samej lambdy (przez return@label). W funkcji inline lambda jest wstawiana do ciała funkcji wywołującej, dlatego nonlocal return staje się możliwy: return wewnątrz lambdy kończy funkcję zewnętrzną.

kotlin
inline fun findFirst(
    items: List<Int>,
    predicate: (Int) -> Boolean
): Int {
    for (item in items) {
        if (predicate(item)) {
            return item
        }
    }
    return -1
}

fun processNumbers() {
    val numbers = listOf(1, 2, 3)
    val firstEven = findFirst(numbers) { it % 2 == 0 }
    // return w lambdzie zwróciłby null z processNumbers()
}

Nonlocal return jest wygodny do wczesnego zakończenia, ale może prowadzić do błędów. Jeśli lambda jest używana w kontekście nielokalnym (zapisywana do zmiennej), nonlocal return spowoduje RuntimeException. Kompilator Kotlin wyświetla ostrzeżenie przy próbie takiego zapisu.

noinline i crossinline: zarządzanie wstawianiem

Gdy funkcja ma wiele parametrów lambda, czasami trzeba wstawić tylko część z nich. Służy do tego noinline — zabrania wstawiania konkretnego parametru lambda, pozostawiając go jako zwykły obiekt Function.

Modyfikator crossinline rozwiązuje przeciwne zadanie: lambda jest wstawiana, ale nonlocal return jest zabroniony. Jest to potrzebne, gdy lambda jest używana wewnątrz innej lambdy lub w kontekście, gdzie return jest niedozwolony (np. przekazywana do Runnable).

kotlin
inline fun processWithCallback(
    data: String,
    crossinline onSuccess: (String) -> Unit,
    noinline onError: (Exception) -> Unit
) {
    try {
        val result = process(data)
        onSuccess(result)
    } catch (e: Exception) {
        onError(e)
    }
}

// noinline: onError może być przechowywany w zmiennej lub przekazany gdzie indziej
val errorHandler = { e: Exception -> log(e.message) }
processWithCallback("input", { println(it) }, errorHandler)

W przykładzie onSuccess jest oznaczona jako crossinline — zostanie wstawiona, ale wewnątrz niej nie można użyć return. onError jest oznaczona jako noinline — nie jest wstawiana, można ją przekazać jako obiekt, zapisać w polu klasy lub użyć jako listener.

Ograniczenia funkcji inline i zalecenia

Funkcje inline mają ograniczenia. Rekurencyjne funkcje inline są zabronione — kompilator zgłosi błąd. Funkcje inline nie mogą mieć widoczności private lub internal, jeśli są zadeklarowane w innym module, ale to ograniczenie dotyczy dostępu, a nie samego mechanizmu wstawiania.

Rozmiar kodu bajtowego rośnie z każdym wywołaniem funkcji inline, ponieważ ciało jest kopiowane. Według Kotlin Coding Conventions (JetBrains, 2025), zaleca się używanie inline tylko dla funkcji o rozmiarze do 10–15 linii. Dla większych funkcji korzyść z wstawiania lambd może być zniwelowana przez zwiększenie rozmiaru APK (w Android jest to krytyczne — istnieje limit 64K metod).

kotlin
// Zalecana praktyka
inline fun withLock(lock: Lock, action: () -> T): T {
    lock.lock()
    try {
        return action()
    } finally {
        lock.unlock()
    }
}

// Nie zalecane dla dużych funkcji
inline fun largeComputation(...) { // źle — ciało >50 linii
    // ponad 50 linii — lepiej wydzielić do zwykłej funkcji
}

Publiczne funkcje inline w bibliotekach wymagają ostrożności: jeśli ciało funkcji inline ulegnie zmianie, wszyscy klienci muszą skompilować się ponownie. JetBrains zaleca używanie @PublishedApi internal dla elementów wywoływanych z funkcji inline, aby zachować kompatybilność w obrębie modułu.

Często zadawane pytania

Czy można zrobić inline extension function?

Tak, inline extension function działa bez ograniczeń. Na przykład: inline fun String.transform(block: (Char) -> Char): String. Rozszerzenie nie wpływa na możliwość wstawiania — kompilator traktuje ją tak samo jak zwykłą funkcję inline.

Kiedy inline NIE daje korzyści?

Jeśli funkcja nie przyjmuje parametrów lambda — inline nie daje przewagi. Kompilator Kotlin wyświetla ostrzeżenie: "Expected performance impact from inlining is insignificant. Inlining works best for functions with parameters of functional types." Ponadto inline jest szkodliwy dla dużych funkcji ze względu na wzrost kodu bajtowego.

Czym inline różni się od @JvmInline (value class)?

inline — modyfikator funkcji wstawiający jej ciało w miejsce wywołania. @JvmInline (value class) — mechanizm dla klas-opakowań, które są zastępowane wartością podczas kompilacji. Różne koncepcje: inline optymalizuje wywołania, value class optymalizuje reprezentację danych.

Czy można używać inline z suspend-funkcjami?

Nie, suspend-funkcje nie mogą być inline, ponieważ są kompilowane do maszyny stanów z Continuation. Jednak funkcja inline może przyjmować suspend-lambdę jako parametr z crossinline. Jest to często używane w korutynach: inline fun launch(block: suspend CoroutineScope.() -> Unit).

Czy inline wpływa na debugowanie?

Tak, funkcje inline utrudniają debugowanie, ponieważ ciało funkcji nie jest wywoływane, ale wstawiane w miejsce wywołania. Stacktrace staje się dłuższy, punkty przerwania działają, ale mogą pokazywać nieoczekiwane pozycje. JetBrains zaleca debugowanie bez inline, a włączanie go tylko w wersjach release.

Podsumowanie

  • funkcja inline — wstawia ciało funkcji w miejsce wywołania, eliminując narzut lambd
  • Nonlocal return — return z lambdy kończy funkcję zewnętrzną (możliwe tylko w inline)
  • noinline — zabrania wstawiania konkretnego parametru lambda
  • crossinline — zezwala na wstawianie, ale zabrania nonlocal return
  • Funkcje rekurencyjne nie mogą być inline — kompilator zgłasza błąd
  • Rozmiar kodu bajtowego rośnie — używaj inline dla funkcji do 10–15 linii
  • Optymalne zastosowanie — funkcje wyższego rzędu z parametrami lambda, bloki synchronizacji, scope functions (let, apply, also, run)

Opracujemy aplikację mobilną pod klucz

IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.

Omów projekt

Przeczytaj również