Function di Kotlin dapat diperlakukan sebagai nilai — kita dapat menyimpannya di variabel, meneruskannya ke function lain, atau mengembalikannya dari function. Mekanisme ini disebut callable reference. Pada chapter ini kita akan membahas tiga bentuk callable reference — top-level function, class member (unbound), dan instance member (bound) — serta bagaimana memanfaatkan keduanya untuk membangun higher-order function yang ringkas. Pemahaman ini melengkapi pengetahuan reflection dan menjembatani ke bab tentang lambda dan higher-order function pada section lain.
Tiga Bentuk Callable Reference #
| Bentuk | Sintaks | Tipe |
|---|---|---|
| Top-level function | ::namaFungsi | (T) -> R |
| Class member (unbound) | Class::member | (Class) -> R |
| Instance member (bound) | instance::member | () -> R |
| Constructor | ::ClassName | (T) -> ClassName |
| Property | Class::prop atau instance::prop | KProperty<R> atau () -> R |
Contoh Kode #
Kode berikut mendemonstrasikan ketujuh bentuk sekaligus:
// File: callable_references.kt
// Mendemonstrasikan callable reference: ::funcName, Class::func, obj::method (bound).
package com.example.callable
import kotlin.reflect.KFunction
import kotlin.reflect.KProperty
fun sapa(nama: String): String = "Halo, $nama!"
fun gandakan(x: Int): Int = x * 2
class Pengguna(val nama: String) {
fun halo(): String = "Halo, saya $nama"
}
fun terapkan(x: Int, op: (Int) -> Int): Int = op(x)
fun main() {
// 1. Top-level function reference.
val fnSapa: (String) -> String = ::sapa
println(fnSapa("Budi"))
val fnGanda: (Int) -> Int = ::gandakan
println("gandakan(21) = ${terapkan(21, fnGanda)}")
// 2. Reference ke method pada class (unbound): butuh instance sebagai receiver.
val haloUnbound: (Pengguna) -> String = Pengguna::halo
val p = Pengguna("Siti")
println(haloUnbound(p))
// 3. Bound reference: instance sudah terikat, signature tidak butuh receiver.
val haloBound: () -> String = p::halo
println(haloBound())
// 4. Reference ke property (getter).
val namaProp: KProperty<String> = Pengguna::nama
println("namaProp.getter.call(p) = ${namaProp.getter.call(p)}")
val namaBound: () -> String = p::nama
println("namaBound() = ${namaBound()}")
// 5. Reference ke constructor.
val buatPengguna: (String) -> Pengguna = ::Pengguna
val baru = buatPengguna("Dewi")
println("Pengguna baru: ${baru.nama}")
// 6. KFunction reflection — nama dan parameter.
val kfn: KFunction<*> = ::sapa
println("KFunction nama=${kfn.name}, param count=${kfn.parameters.size}")
// 7. Mem-pass callable reference ke higher-order function.
val list = listOf(1, 2, 3, 4, 5)
val kaliDua = list.map(::gandakan)
println("map(gandakan) = $kaliDua")
}Output yang Dihasilkan:
Halo, Budi!
gandakan(21) = 42
Halo, saya Siti
Halo, saya Siti
namaProp.getter.call(p) = Siti
namaBound() = Siti
Pengguna baru: Dewi
KFunction nama=sapa, param count=1
map(gandakan) = [2, 4, 6, 8, 10]Mari kita bedah baris-baris kunci di atas:
- Baris 26 (
val fnSapa: (String) -> String = ::sapa): reference ke function top-level. Tipe(String) -> Stringsesuai signaturesapa(nama: String): String. Setelah ini,fnSapa("Budi")setara dengansapa("Budi"). - Baris 30 (
val haloUnbound: (Pengguna) -> String = Pengguna::halo): reference ke member functionhalo. Karena unbound, signature adalah(Pengguna) -> String— instance wajib disertakan saat pemanggilan. - Baris 34 (
val haloBound: () -> String = p::halo): bound reference. Instancepsudah terikat ke reference; signature hanya() -> String. PemanggilanhaloBound()langsung mengembalikan hasil. - Baris 38 (
val namaProp: KProperty<String> = Pengguna::nama): reference ke property class. TipeKProperty<String>menyimpan metadata tambahan dibanding function type biasa. - Baris 42 (
val namaBound: () -> String = p::nama): bound reference ke property. Signature() -> String— pemanggilan seperti function tanpa argumen. - Baris 46 (
val buatPengguna: (String) -> Pengguna = ::Pengguna): reference ke constructor. Memungkinkan kita membuat instance secara dinamis, berguna untuk factory pattern. - Baris 50 (
val kfn: KFunction<*> = ::sapa): reference sebagaiKFunctionuntuk inspeksi reflection.kfn.namemengembalikan"sapa",kfn.parametersmengembalikan daftar parameter. - Baris 54 (
list.map(::gandakan)): callable reference dipakai sebagai argumen higher-order function. Lebih ringkas dibanding lambda{ x -> gandakan(x) }.
Bound Reference dan Capturing #
Bound reference menangkap instance ke dalam closure. Setelah reference dibuat, instance tidak dapat diubah — bahkan bila variabel yang menampung instance di-assign ulang. Hal ini penting untuk diingat:
var p1 = Pengguna("Budi")
val halo = p1::halo
p1 = Pengguna("Siti")
println(halo()) // tetap mencetak "Halo, saya Budi"Constructor Reference #
Reference ::ClassName menghasilkan function type yang sesuai dengan constructor utama. Untuk class dengan secondary constructor, kita dapat memilih constructor lain dengan cast eksplisit atau dengan deklarasi tipe parameter:
class Produk(val nama: String) {
constructor(id: Long) : this("Produk#$id")
}
val buatDariNama: (String) -> Produk = ::Produk
// val buatDariId: (Long) -> Produk = ::Produk // ambiguousKompilator memilih constructor yang paling cocok berdasarkan tipe yang dideklarasikan.
Property Reference dan KProperty #
Reference property menghasilkan KProperty (untuk val) atau KMutableProperty (untuk var). Selain sebagai function value, KProperty menyimpan metadata seperti name, isLateinit, dan returnType — berguna untuk reflection lanjutan.
Referensi Lanjutan #
- Reflection
— bagian callable reference (
::) pada panduan reflection resmi - KFunction API reference
— API reference
KFunctionuntuk reference function - KProperty API reference
— API reference
KPropertyuntuk reference property
Selanjutnya #
Reflection sangat berguna, tetapi membawa overhead performa. Chapter Reflection Performance akan mengukur biaya reflection dan menunjukkan cara meminimalkannya.