Companion Object

Anggota mirip static dan fungsi factory pada companion object.

Daftar isi

Companion object adalah object yang diletakkan di dalam badan sebuah kelas untuk menampung anggota yang terikat pada kelasnya, bukan pada instans tertentu. Anggota itu dipanggil langsung lewat nama kelas, sehingga polanya menggantikan anggota static yang dikenal pada bahasa lain. Topik ini membahas deklarasi companion object, konstanta const val, fungsi factory yang membangun instans dari sumber luar, serta kaitan singkatnya dengan anotasi @JvmStatic. Contoh menggunakan titik koordinat yang dibangun dari teks.

Anggota yang terikat pada kelas#

Sebuah kelas hanya boleh memiliki satu companion object, dan penulisannya cukup dengan kata kunci companion object tanpa nama. Anggota di dalamnya dapat berupa fungsi, properti, maupun konstanta, dan seluruhnya diakses lewat nama kelas tanpa membuat instans terlebih dahulu.

kotlin main.kt
data class Titik(val x: Int, val y: Int) {
    companion object {
        const val ASAL = "pusat koordinat"

        fun dariTeks(teks: String): Titik {
            val bagian = teks.split(",")
            return Titik(bagian[0].trim().toInt(), bagian[1].trim().toInt())
        }
    }
}

fun main() {
    val p = Titik.dariTeks("3, 4")
    println("p: $p")

    val q = Titik.dariTeks("-2,7")
    println("q: $q")

    println("ASAL: ${Titik.ASAL}")

    val r = Titik(6, 8)
    println("r: $r")
    println("kuadrat jarak r: ${r.x * r.x + r.y * r.y}")
}
bash
# atur PATH compiler Kotlin dan lokasi kotlin-stdlib
export PATH=/home/kal/my-project/tools/kotlinc/bin:/home/kal/my-project/tools/jdk-merged/bin:$PATH
export STDLIB=/home/kal/my-project/tools/kotlinc/lib/kotlin-stdlib.jar
# kompilasi main.kt menjadi out.jar
kotlinc main.kt -d out.jar
# jalankan program dengan kotlin-stdlib di classpath
java -cp "out.jar:$STDLIB" MainKt
text Keluaran
p: Titik(x=3, y=4)
q: Titik(x=-2, y=7)
ASAL: pusat koordinat
r: Titik(x=6, y=8)
kuadrat jarak r: 100

Program di atas memanggil Titik.dariTeks dua kali untuk membangun titik dari teks, lalu membaca konstanta Titik.ASAL dengan pola yang sama. Tidak ada instans yang diperlukan untuk pemanggilan-pemanggilan tersebut. Instans r dibangun lewat konstruktor biasa karena datanya sudah tersedia sebagai pasangan angka. Ketiga cara itu menghasilkan instans maupun nilai dengan aturan akses yang sama, yaitu lewat nama kelas.

Anggota companion object bukan properti instans, sehingga tidak diikutkan dalam equals maupun destructuring ketika kelasnya berupa data class, dan fungsinya tidak dapat dipanggil lewat variabel instans. Dengan batas itu, kode pembaca dapat langsung membedakan data milik instans dan kemampuan milik kelas. Companion object sesungguhnya adalah object biasa yang ditempatkan di dalam kelas. Nama bawaannya adalah Companion, dan ia boleh diberi nama sendiri bila diperlukan. Karena bentuknya object, ia dapat mengimplementasikan interface, dan anggotanya dapat diberi modifier visibilitas seperti private untuk menyembunyikan rinciannya dari luar kelas.

Konstanta const val dan waktu inisialisasi#

Konstanta yang nilainya diketahui saat kompilasi dinyatakan dengan const val, dan penempatannya hanya sah pada tingkat atas berkas, di dalam object, atau di dalam companion object. Pada program di atas, ASAL berupa konstanta String yang terpasang sebagai bagian dari kelas, sehingga Titik.ASAL dapat dibaca kapan pun tanpa menyiapkan instans. Nilai const val ditanam compiler langsung pada setiap titik pemakaiannya, sehingga pembacaannya tidak menunggu proses apa pun.

Companion object itu sendiri diinisialisasi ketika kelasnya dimuat untuk pertama kali, yaitu sebelum instans pertama dibuat atau sebelum anggotanya pertama kali dipanggil. Properti biasa di dalam companion object yang dihitung saat berjalan mengikuti waktu tersebut. Perbedaannya dengan const val terletak pada waktu penentuannya, karena const val wajib berupa nilai tetap seperti angka, Boolean, atau String tanpa perhitungan apa pun. Bila nilai konstanta perlu dihitung, jadikan properti val biasa di dalam companion object dan biarkan hitungannya berlangsung saat inisialisasi.

Fungsi factory pada companion object#

Fungsi factory adalah fungsi yang mengembalikan instans baru tanpa lewat pemanggilan konstruktor secara langsung. Penempatannya pada companion object memungkinkan pola NamaKelas.dariSesuatu(...) yang terbaca seperti kalimat. Pada program di atas, Titik.dariTeks("3, 4") menerima teks, menguraikannya dengan split, memangkas spasi, lalu mengonversi setiap bagian menjadi Int sebelum konstruktor dipanggil. Pasangan data class dan companion object lazim dipakai bersama, karena data class menyediakan pembawa data yang ringkas, sedangkan companion object menyediakan pintu masuknya.

Kebutuhan yang lebih dari sekadar penguraian juga ditampung dengan baik di tempat yang sama. Fungsi factory dapat memeriksa keabsahan masukan sebelum instans dibuat, mengembalikan instans yang sudah ada untuk nilai yang sering dipakai, atau memilih antara beberapa bentuk instans. Fungsi ini juga menjadi tempat alami untuk memberi nama yang lebih bermakna daripada nama konstruktor, misalnya membedakan dariTeks dengan sumber angka lain. Konstruktor utama tidak dapat melakukan hal-hal tersebut dengan leluasa karena namanya tetap dan tidak memiliki badan. Dengan membiarkan konstruktor sederhana dan menempatkan variasi pembentukan pada fungsi factory, sebuah kelas memberi pemanggilnya pintu masuk yang jelas.

Singkat tentang @JvmStatic#

Dari sisi Java, companion object adalah field bernama Companion pada kelasnya, sehingga anggotanya dipanggil melalui Titik.Companion.dariTeks(...). Anotasi @JvmStatic mengubah hal ini dengan menyuruh compiler menghasilkan metode static pada kelas tersebut, sehingga pemanggilan dari Java menjadi Titik.dariTeks(...). Kondisi ini membuat bentuk pemanggilan atas kelas Kotlin dari Java berbeda dari kebiasaan pemanggilan static yang lazim.

kotlin Contoh: @JvmStatic pada anggota companion object
class KonversiSuhu {
    companion object {
        @JvmStatic
        fun fahrenheitKeCelsius(fahrenheit: Double): Double =
            (fahrenheit - 32) * 5 / 9
    }
}

Anotasi ini tidak mengubah cara pemanggilan dari sisi Kotlin, yang tetap menulis KonversiSuhu.fahrenheitKeCelsius(...) baik dengan maupun tanpa anotasi. Perubahannya hanya terasa ketika basis kode mencampur Kotlin dan Java. Bila anotasi dihilangkan, pemanggilan dari Java tetap dapat dilakukan, hanya lebih panjang karena harus melewati Companion. Anotasi ini pun tidak wajib, sehingga pemilihannya bergantung pada apakah kelas itu akan dipanggil dari Java. Pembahasan interoperabilitas Java secara lengkap, termasuk nama yang dihasilkan compiler, diberikan pada materi interoperabilitas Java.

Ringkasan#

  • companion object menampung anggota yang terikat pada kelas, dan anggotanya dipanggil lewat nama kelas tanpa instans.
  • const val hanya sah pada tingkat atas, di dalam object, atau di dalam companion object, dan wajib berupa nilai tetap saat kompilasi.
  • Fungsi factory pada companion object memberi pintu masuk pembentukan instans yang dapat memeriksa masukan dan menamai sumbernya.
  • @JvmStatic menghasilkan metode static untuk sisi Java, sedangkan pemanggilan dari Kotlin tidak berubah.

Referensi#