Banyak class dari library pihak ketiga hampir memenuhi kebutuhan kita, tetapi kekurangan satu method kecil. Membuat subclass hanya untuk menambah satu method terasa berlebihan, terutama bila class tersebut final atau berada di library eksternal. Kotlin menawarkan extension functions — kita dapat menambahkan method ke tipe apa pun tanpa memodifikasi kode sumbernya. Pada chapter ini kita akan melihat cara deklarasi, cara kerja, dan batasan extension function.

Deklarasi Extension Function #

Extension function dideklarasikan dengan menulis tipe penerima sebelum nama function, dipisahkan tanda titik. Di dalam body, this merujuk ke instance penerima sehingga kita dapat mengakses anggotanya.

kotlin
fun String.terbalikManual(): String {
    val builder = StringBuilder()
    for (i in this.length - 1 downTo 0) {
        builder.append(this[i])
    }
    return builder.toString()
}

fun Int.pangkat(n: Int): Int {
    var hasil = 1
    repeat(n) { hasil *= this }
    return hasil
}

fun String.hanyaAlfabet(): Boolean {
    for (ch in this) {
        if (!ch.isLetter()) return false
    }
    return true
}

fun main() {
    val teks = "Kotlin"
    println("Asli: $teks")
    println("Terbalik: ${teks.terbalikManual()}")

    println("2 pangkat 5 = ${2.pangkat(5)}")
    println("3 pangkat 3 = ${3.pangkat(3)}")

    println("'Kotlin' hanya alfabet? ${"Kotlin".hanyaAlfabet()}")
    println("'Kotlin 2026' hanya alfabet? ${"Kotlin 2026".hanyaAlfabet()}")
}

Output yang Dihasilkan:

text
Asli: Kotlin
Terbalik: niltoK
2 pangkat 5 = 32
3 pangkat 3 = 27
'Kotlin' hanya alfabet? true
'Kotlin 2026' hanya alfabet? false

Mari kita bedah baris per baris:

  1. fun String.terbalikManual(): extension function pada tipe String. Tipe String adalah receiver type dan this di dalam body merujuk ke instance String yang dipanggil.
  2. this.length, this[i]: akses ke anggota String. Kata kunci this dapat dihilangkan (cukup length, this[i]), tetapi penulisan eksplisit kadang memperjelas bahwa kita mengakses instance penerima.
  3. fun Int.pangkat(n: Int): Int: extension function pada Int. Pemanggilan 2.pangkat(5) menjadikan 2 sebagai this di dalam body.
  4. repeat(n) { hasil *= this }: lambda di dalam repeat juga memakai this, yang tetap merujuk ke instance Int penerima. Perhatikan bahwa this di lambda secara default merujuk ke receiver terdekat.

Tidak Memodifikasi Class Asli #

Extension function tidak menyuntikkan method baru ke definisi class. Yang terjadi adalah kompilator menerjemahkan 2.pangkat(5) menjadi pemanggilan function statis pangkat(2, 5). Akibatnya, instance class asli tetap utuh dan tidak ada perubahan pada struktur memori objek.

kotlin Contoh: extension pada tipe dari library
fun List<Int>.total(): Int {
    var sum = 0
    for (n in this) sum += n
    return sum
}

fun main() {
    val angka = listOf(1, 2, 3, 4, 5)
    println(angka.total())   // 15
}

Extension function total pada List<Int> bekerja tanpa kita memodifikasi definisi List dari standard library. Pola inilah yang membuat extension sangat berguna untuk meningkatkan keterbacaan kode tanpa subclassing.

Resolusi Statis #

Extension function dipilih berdasarkan tipe statis ekspresi, bukan tipe runtime instance. Perbedaan ini penting saat kita memakai polymorphism. Perhatikan contoh berikut.

kotlin Contoh: resolusi statis vs polymorphism
open class Bentuk
class Lingkaran : Bentuk()

fun Bentuk.nama() = "Bentuk umum"
fun Lingkaran.nama() = "Lingkaran"

fun main() {
    val b: Bentuk = Lingkaran()
    println(b.nama())   // "Bentuk umum" — bukan "Lingkaran"
}

Meskipun instance runtime b adalah Lingkaran, pemanggilan b.nama() memilih extension function Bentuk.nama() karena tipe statis b adalah Bentuk. Polimorfisme tidak berlaku untuk extension function — ini konsekuensi dari penerjemahan ke function statis.

Member Extension vs Top-Level Extension #

Extension function dapat dideklarasikan di level top-level (di luar class) atau sebagai member dari class. Member extension memiliki akses ke anggota class tempat ia dideklarasikan. Pola ini sering dipakai untuk membatasi visibility extension agar hanya dipakai dalam konteks tertentu.

kotlin Contoh: member extension di dalam class
class Pemroses {
    private val log = mutableListOf<String>()

    fun proses(data: String) {
        log.add(data)
    }

    fun String.denganPrefix(): String = "[$data]"

    fun jalankan() {
        proses("alpha".denganPrefix())
        proses("beta".denganPrefix())
        println(log)
    }
}

Member extension denganPrefix hanya dapat dipanggil dari dalam instance Pemroses, sehingga tidak mencemari namespace global.

Referensi Lanjutan #

Ringkasan #

Extension function memungkinkan kita menambahkan method ke class yang sudah ada tanpa memodifikasi kode sumbernya. Resolusi dilakukan secara statis berdasarkan tipe deklarasi, bukan tipe runtime. Extension tidak menyuntikkan method baru ke class — kompilator menerjemahkan pemanggilan menjadi function statis. Pada chapter tailrec Functions berikutnya, kita akan membahas bentuk khusus function rekursif yang dioptimalkan kompilator menjadi loop tanpa pertumbuhan stack.