Lambda sangat berguna untuk function sekali pakai, tetapi kadang kita ingin meneruskan function yang sudah dideklarasikan sebelumnya. Menulis ulang sebagai lambda akan menambah duplikasi dan kehilangan nama yang bermakna. Kotlin menyediakan function references dengan operator :: yang mengubah function menjadi nilai bertipe function. Pada chapter ini kita akan melihat bentuk unbound dan bound function reference.
Referensi Function Top-Level #
Function yang dideklarasikan di level top-level (di luar class) dapat dirujuk dengan ::namaFunction. Hasilnya adalah nilai bertipe function yang dapat disimpan ke variabel atau diteruskan sebagai argumen.
fun ganjil(n: Int): Boolean = n % 2 == 1
fun isPanjang(s: String): Boolean = s.length >= 5
fun main() {
val cekGanjil: (Int) -> Boolean = ::ganjil
println("Apakah 7 ganjil? ${cekGanjil(7)}")
println("Apakah 8 ganjil? ${cekGanjil(8)}")
val predikat: (String) -> Boolean = ::isPanjang
val kata = listOf("Budi", "Kotlin", "Siti", "Pemrograman")
val panjang = kata.filter(predikat)
println("Kata panjang: $panjang")
val panjangNama: (String) -> Int = String::length
println("Panjang kata 'Kotlin': ${panjangNama("Kotlin")}")
val nilai = listOf(1, 2, 3, 4, 5, 6)
val hasilGanjil = nilai.filter(::ganjil)
println("Bilangan ganjil: $hasilGanjil")
}Output yang Dihasilkan:
Apakah 7 ganjil? true
Apakah 8 ganjil? false
Kata panjang: [Kotlin, Pemrograman]
Panjang kata 'Kotlin': 6
Bilangan ganjil: [1, 3, 5]Mari kita bedah baris per baris:
val cekGanjil: (Int) -> Boolean = ::ganjil: operator::mengubah functionganjilmenjadi nilai bertipe(Int) -> Boolean. Nilai ini dapat dipanggil seperti function biasa dengancekGanjil(7).kata.filter(predikat): function reference::isPanjangditeruskan sebagai argumenfilter. Lambda tidak perlu ditulis ulang — kode lebih ringkas dan tetap menggunakan nama function yang bermakna.String::length: referensi member functionlengthpada classString. Bentuk ini menghasilkan nilai bertipeString.() -> Intatau(String) -> Inttergantung konteks.nilai.filter(::ganjil): function reference dapat diteruskan langsung tanpa variabel perantara. Bentuk ini paling ringkas untuk function yang dipakai sekali.
Bound vs Unbound #
Function reference ke member function memiliki dua bentuk: unbound (belum terikat instance) dan bound (sudah terikat ke instance tertentu). Bentuk unbound menggunakan Kelas::method, sedangkan bentuk bound menggunakan instance::method.
class Pemohon(val nama: String) {
fun salam() = "Halo, saya $nama"
}
fun main() {
// Unbound: butuh instance saat dipanggil
val refUnbound: Pemohon.() -> String = Pemohon::salam
val p = Pemohon("Budi")
println(p.refUnbound()) // "Halo, saya Budi"
// Bound: instance sudah dibekukan
val refBound: () -> String = p::salam
println(refBound()) // "Halo, saya Budi"
}Pada bentuk unbound, tipe function adalah Pemohon.() -> String — function dengan receiver. Saat dipanggil, instance penerima wajib disediakan. Pada bentuk bound, instance p sudah ditangkap sehingga tipe function menjadi () -> String tanpa parameter tambahan.
Referensi Constructor #
Operator :: juga dapat diterapkan pada constructor. Hasilnya adalah function yang membuat instance baru saat dipanggil. Pola ini sering dipakai sebagai factory function pada pola desain factory atau builder.
data class Pengguna(val nama: String, val umur: Int)
fun main() {
val buat: (String, Int) -> Pengguna = ::Pengguna
val p = buat("Budi", 25)
println(p)
}::Pengguna menghasilkan function (String, Int) -> Pengguna. Pemanggilan buat("Budi", 25) sama dengan Pengguna("Budi", 25). Pola ini berguna saat kita ingin meneruskan constructor sebagai argumen ke higher-order function.
Kapan Memakai Reference vs Lambda #
Pilihan antara function reference dan lambda bergantung pada konteks:
- function sudah dideklarasikan dengan nama bermakna → pakai
::namaagar kode lebih jelas - logika hanya dipakai sekali dan pendek → lambda inline sering kali lebih ringkas
- lambda memerlukan parameter tambahan yang tidak ada di function asli → lambda ekspres
- kode membaca lebih natural dengan trailing lambda → lambda
fun isPositif(n: Int) = n > 0
fun main() {
val data = listOf(-3, -1, 0, 2, 5)
val a = data.filter(::isPositif) // ringkas, jelas
val b = data.filter { it > 0 } // lambda inline, sama hasilnya
println(a)
println(b)
}Kedua pemanggilan di atas menghasilkan output yang sama. Pilihan bergantung pada apakah isPositif sudah ada di codebase atau hanya dibutuhkan sekali di tempat ini.
Referensi Lanjutan #
- Kotlin higher-order functions and lambdas
— panduan resmi callable reference
::functionuntuk mengubah function menjadi nilai - Kotlin functions
— rujukan function sebagai entitas yang dapat dirujuk dengan
::
Ringkasan #
Function references dengan operator :: mengubah function yang sudah dideklarasikan menjadi nilai bertipe function. Bentuk unbound (Kelas::method) menghasilkan function dengan receiver, sedangkan bentuk bound (instance::method) menghasilkan function tanpa parameter tambahan karena instance sudah ditangkap. Operator :: juga dapat diterapkan pada constructor untuk membuat factory function. Pada chapter inline Functions berikutnya, kita akan melihat bagaimana keyword inline mengurangi overhead alokasi lambda pada higher-order function.