Saat sebuah function ditandai inline, semua parameter bertipe function secara default ikut di-inline. Kadang kita ingin mengecualikan salah satu parameter — misalnya karena ingin menyimpan lambda tersebut sebagai objek. Kadang juga kita ingin melarang lambda melakukan return non-local dari function induk. Kotlin menyediakan dua modifier untuk kebutuhan ini: noinline dan crossinline. Pada chapter ini kita akan membedah keduanya.

noinline: Parameter yang Tidak Di-inline #

Modifier noinline diletakkan sebelum nama parameter function. Lambda yang diteruskan ke parameter ini tetap menjadi instance class anonim — tidak disalin ke call site. Modifier ini berguna bila kita ingin menyimpan lambda ke variabel, mengembalikannya, atau meneruskannya ke function lain yang tidak inline.

kotlin
inline fun proses(
    a: () -> Unit,
    noinline b: () -> Unit,
    crossinline c: () -> Unit
) {
    a()

    val fungsiB: () -> Unit = b
    fungsiB()

    val runnable = Runnable { c() }
    runnable.run()
}

inline fun dalamLoop(crossinline aksi: (Int) -> Unit) {
    val daftar = listOf(1, 2, 3)
    daftar.forEach { elemen ->
        aksi(elemen)
    }
}

fun main() {
    proses(
        a = { println("Bagian A (inline penuh)") },
        b = { println("Bagian B (noinline, dipakai sebagai nilai)") },
        c = { println("Bagian C (crossinline, dipanggil dari Runnable)") }
    )

    println("-----")

    var hitung = 0
    dalamLoop { elemen ->
        hitung += 1
        println("Elemen $elemen, panggilan ke-$hitung")
    }
    println("Total pemanggilan: $hitung")
}

Output yang Dihasilkan:

text
Bagian A (inline penuh)
Bagian B (noinline, dipakai sebagai nilai)
Bagian C (crossinline, dipanggil dari Runnable)
-----
Elemen 1, panggilan ke-1
Elemen 2, panggilan ke-2
Elemen 3, panggilan ke-3
Total pemanggilan: 3

Mari kita bedah baris per baris:

  1. inline fun proses(a: () -> Unit, noinline b: () -> Unit, crossinline c: () -> Unit): function proses di-inline, tetapi parameter b ditandai noinline dan c ditandai crossinline.
  2. val fungsiB: () -> Unit = b: parameter b disimpan ke variabel lokal. Tanpa noinline, kompilator menolak karena parameter inline tidak dapat disimpan sebagai nilai. Dengan noinline, b tetap berupa instance class anonim dan dapat disimpan.
  3. val runnable = Runnable { c() }: parameter c dipanggil dari dalam lambda Runnable. Tanpa crossinline, kompilator menolak karena ada kemungkinan non-local return dari dalam lambda Runnable yang justru akan dieksekusi nanti — perilaku yang tidak aman.
  4. inline fun dalamLoop(crossinline aksi: (Int) -> Unit): parameter aksi ditandai crossinline karena dipanggil dari dalam forEach, yang merupakan inline function dengan lambda sendiri.

crossinline: Melarang Non-local Return #

Modifier crossinline mengizinkan inlining tetapi melarang lambda melakukan return non-local dari function induk. Modifier ini diperlukan bila lambda akan dipanggil dari konteks yang berbeda — misalnya dari dalam lambda lain, dari dalam objek anonim, atau dari eksekutor asinkron.

kotlin Contoh: crossinline dipakai karena lambda dipanggil dari Runnable
inline fun jalankanAsync(crossinline tugas: () -> Unit) {
    val thread = Thread { tugas() }
    thread.start()
    thread.join()
}

Tanpa crossinline, pemanggil jalankanAsync { return } akan mencoba keluar dari function induk padahal tugas dijalankan di thread lain — perilaku yang ambigu. crossinline melarang return demikian dan tetap mengizinkan inlining.

Perbandingan Tiga Modifier #

Tabel berikut merangkum perbedaan perilaku antara parameter inline biasa, noinline, dan crossinline.

ModifierDi-inline?Bisa disimpan sebagai nilai?Bisa return non-local?
(default, pada function inline)YaTidakYa
noinlineTidakYaTidak (sama seperti lambda biasa)
crossinlineYaTidakTidak

Pemilihan modifier bergantung pada cara parameter function dipakai di dalam body:

  • bila parameter hanya dipanggil langsung dan boleh return → tidak perlu modifier
  • bila parameter disimpan ke variabel, dikembalikan, atau diteruskan ke function non-inline → noinline
  • bila parameter dipanggil dari konteks lain (lambda bersarang, Runnable, executor) → crossinline

Kapan Memakai Kombinasi #

Banyak function praktis memakai kombinasi ketiganya. Sebagai contoh, function yang menerima dua lambda — satu untuk transformasi data, satu untuk callback completion — seringkali menandai callback sebagai noinline karena callback disimpan untuk dipanggil nanti, sementara transformasi tetap inline untuk kinerja.

kotlin Contoh: kombinasi inline, noinline, crossinline
inline fun <T> prosesDenganCallback(
    data: List<T>,
    crossinline transformasi: (T) -> T,
    noinline selesai: () -> Unit
): List<T> {
    val hasil = mutableListOf<T>()
    data.forEach { elemen ->
        hasil.add(transformasi(elemen))   // dipanggil dari forEach → crossinline
    }
    selesai()                              // disimpan/dipanggil → noinline
    return hasil
}

Pola di atas umum di library yang memisahkan transformasi inline dari callback yang disimpan untuk dipakai nanti.

Referensi Lanjutan #

Ringkasan #

noinline mengecualikan parameter tertentu dari inlining sehingga lambda dapat disimpan atau diteruskan. crossinline mengizinkan inlining tetapi melarang non-local return — berguna saat lambda dipanggil dari konteks lain seperti lambda bersarang atau Runnable. Pilihan modifier bergantung pada cara parameter dipakai di dalam body function. Pada chapter Pure Functions dan Side Effects berikutnya, kita akan menutup section Functions dengan pembahasan konsep pure function dan dampak side effect terhadap keterbacaan serta uji unit.