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.
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:
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: 3Mari kita bedah baris per baris:
inline fun proses(a: () -> Unit, noinline b: () -> Unit, crossinline c: () -> Unit): functionprosesdi-inline, tetapi parameterbditandainoinlinedancditandaicrossinline.val fungsiB: () -> Unit = b: parameterbdisimpan ke variabel lokal. Tanpanoinline, kompilator menolak karena parameter inline tidak dapat disimpan sebagai nilai. Dengannoinline,btetap berupa instance class anonim dan dapat disimpan.val runnable = Runnable { c() }: parametercdipanggil dari dalam lambdaRunnable. Tanpacrossinline, kompilator menolak karena ada kemungkinan non-local return dari dalam lambda Runnable yang justru akan dieksekusi nanti — perilaku yang tidak aman.inline fun dalamLoop(crossinline aksi: (Int) -> Unit): parameteraksiditandaicrossinlinekarena dipanggil dari dalamforEach, 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.
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.
| Modifier | Di-inline? | Bisa disimpan sebagai nilai? | Bisa return non-local? |
|---|---|---|---|
| (default, pada function inline) | Ya | Tidak | Ya |
noinline | Tidak | Ya | Tidak (sama seperti lambda biasa) |
crossinline | Ya | Tidak | Tidak |
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.
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 #
- Kotlin inline functions
— panduan resmi
noinlinedancrossinlinemodifier untuk kontrol inlining lambda
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.