interface mendeklarasikan kontrak berupa fungsi dan properti yang harus dimiliki sebuah kelas, tanpa menyimpan keadaan apa pun. Berbeda dengan kelas biasa, sebuah kelas boleh mengimplementasikan banyak interface sekaligus, dan anggota interface boleh dibawa tanpa isi maupun dengan implementasi bawaan (default). Topik ini membahas deklarasi interface, implementasi bawaan, deklarasi properti tanpa penyimpanan, serta penyelesaian konflik ketika dua interface menyediakan implementasi bawaan untuk anggota yang sama. Seluruh contoh dijalankan nyata dan keluarannya ditampilkan sebagaimana adanya.
Deklarasi interface dan implementasinya#
Deklarasi interface ditulis dengan kata kunci interface diikuti nama interface dan daftar anggotanya di dalam kurung kurawal. Kelas yang mengimplementasikannya menuliskan nama interface setelah tanda titik dua, sebagaimana pola yang sama dengan pewarisan kelas. Anggota tanpa tubuh berkonotasi abstrak, yaitu wajib diisi oleh kelas implementor yang mengimplementasikan interface tersebut, sedangkan anggota berisi tubuh menjadi implementasi bawaan yang boleh dipakai apa adanya.
Buat berkas main.kt berikut, lalu jalankan dengan dua perintah di bawahnya.
interface Identitas {
val kode: String
fun tugas(): String
fun sapa(): String {
return "Identitas $kode"
}
}
interface Penghitung {
fun sapa(): String {
return "Penghitung siap dipakai"
}
fun hitung(angka: Int): Int {
return angka * 2
}
}
class MesinUjian(override val kode: String) : Identitas, Penghitung {
override fun tugas(): String {
return "Menilai jawaban peserta"
}
override fun sapa(): String {
return "${super<Identitas>.sapa()} dan ${super<Penghitung>.sapa()}"
}
}
fun main() {
val mesin = MesinUjian("M-01")
println(mesin.sapa())
println("Tugas: ${mesin.tugas()}")
println("Hasil hitung untuk 21: ${mesin.hitung(21)}")
}# compiler Kotlin dan pustaka standar yang dipakai jalur ini
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 berkas main.kt menjadi out.jar, lalu jalankan programnya
kotlinc main.kt -d out.jar && java -cp "out.jar:$STDLIB" MainKtIdentitas M-01 dan Penghitung siap dipakai
Tugas: Menilai jawaban peserta
Hasil hitung untuk 21: 42Kelas MesinUjian mengimplementasikan dua interface sekaligus melalui : Identitas, Penghitung. Properti kode dideklarasikan pada Identitas tanpa isi, sehingga MesinUjian wajib mengisinya, dan yang paling ringkas adalah override val kode: String langsung pada parameter constructor utama. Fungsi tugas juga dibawa tanpa isi sehingga wajib diimplementasi, sedangkan sapa dan hitung dibawa lengkap dengan tubuhnya. Keluaran pertama berasal dari sapa yang ditimpa, dan dua keluaran terakhir memperlihatkan implementasi yang diwarisi tanpa penimpaan apa pun.
Implementasi bawaan pada interface#
Implementasi bawaan adalah tubuh fungsi yang ditulis langsung di dalam interface, sehingga kelas implementor tidak wajib menimpanya. Fungsi hitung pada Penghitung dipakai oleh MesinUjian tanpa satu baris kode pun di kelasnya, dan hasilnya terlihat pada baris Hasil hitung untuk 21: 42. Fitur ini membuat interface dapat bertambah kemampuan tanpa memaksa setiap implementor menulis ulang logika yang sama.
Timpa implementasi bawaan hanya ketika perilakunya memang perlu berbeda, sebagaimana sapa pada MesinUjian yang menggabungkan dua sapaan. Perlu diingat bahwa implementasi bawaan tidak mengubah hak akses apa pun, sehingga fungsi bawaan tetap hanya boleh menyentuh anggota interface itu sendiri atau anggota lain yang dideklarasikan di dalamnya.
Properti pada interface#
Interface dapat mendeklarasikan properti, tetapi hanya dalam bentuk deklarasi tanpa penyimpanan, sebagaimana val kode: String pada Identitas. Deklarasi tersebut menjanjikan bahwa setiap implementor menyediakan pembaca kode, dan kelas implementor yang menentukan dari mana nilainya berasal. Pada MesinUjian, nilainya diteruskan dari parameter constructor utama, sedangkan kelas lain bebas mengambilnya dari sumber yang berbeda.
Karena tidak menyimpan apa pun, properti pada interface juga boleh diberi getter bawaan yang menghitung nilai dari anggota lain. Bentuk ini jarang diperlukan, tetapi sah dan tetap mengikuti aturan yang sama, yaitu tidak ada penyimpanan yang lahir dari interface.
Konflik implementasi bawaan#
Konflik terjadi apabila dua interface yang diimplementasikan satu kelas menyediakan implementasi bawaan untuk fungsi dengan nama yang sama. Identitas dan Penghitung keduanya menyediakan sapa, sehingga MesinUjian tidak boleh membiarkannya, dan compiler menolak program tanpa penimpaan eksplisit. Penimpaan itu menentukan sendiri hasilnya, sementara implementasi kedua interface tetap dapat dipanggil melalui bentuk super<Identitas>.sapa() dan super<Penghitung>.sapa().
Bentuk super<NamaInterface> menjadi satu-satunya jalan menyebut implementasi bawaan yang tertutup oleh penimpaan. Tanpa nama interface, pemanggilan super.sapa() tidak bermakna karena ada dua kandidat, sehingga Kotlin menuntut nama yang eksplisit. Pola pada MesinUjian menggabungkan keduanya, dan keluaran Identitas M-01 dan Penghitung siap dipakai membuktikan bahwa kedua sisi kontrak tetap terlayani.
Kesalahan umum#
Kesalahan yang paling sering adalah mengimplementasikan interface tanpa mengisi anggota abstraknya. Berkas berikut sengaja memuat kesalahan tersebut.
interface Identitas {
val kode: String
fun tugas(): String
}
class KartuUjian(override val kode: String) : Identitas {
}# kompilasi berkas salahimplementasi.kt untuk melihat pesan error dari compiler
kotlinc salahimplementasi.kt -d salah.jarsalahimplementasi.kt:6:1: error: class 'KartuUjian' is not abstract and does not implement abstract member:
fun tugas(): String
class KartuUjian(override val kode: String) : Identitas {
^^^^^^^^^^^^^^^^Pesan error menyebut is not abstract and does not implement abstract member, dan compiler bahkan menampilkan bentuk anggota yang belum diisi beserta tempat deklarasinya. Perbaikannya adalah mengimplementasikan tugas, atau menjadikan kelas abstrak apabila pengisiannya memang diserahkan kepada turunan nantinya.
Kesalahan lain adalah memakai super.sapa() tanpa nama interface ketika ada dua kandidat. Compiler melaporkan panggilan itu tidak jelas, dan perbaikannya mengikuti bentuk pada program utama, yaitu menyebut nama interface yang dimaksud di dalam tanda kurung sudut.
Ringkasan#
interfacemendeklarasikan kontrak fungsi dan properti tanpa menyimpan keadaan, dan satu kelas boleh mengimplementasikan banyak interface.- Anggota dengan tubuh menjadi implementasi bawaan yang boleh dipakai tanpa penimpaan, sedangkan anggota tanpa tubuh wajib diisi kelas implementor.
- Properti pada interface dideklarasikan tanpa inisialisator dan tanpa backing field, lalu diisi oleh kelas implementor.
- Konflik dua implementasi bawaan diselesaikan dengan menimpa anggotanya dan memanggil
super<NamaInterface>.metode()untuk masing-masing sisi.
Referensi#
- Interfaces — Kotlin Documentation — Dokumentasi resmi deklarasi interface, implementasi bawaan, dan penyelesaian konflik dengan super.