Interface adalah kontrak yang menyatakan method dan property apa yang harus disediakan oleh class yang mengimplementasikannya. Berbeda dengan abstract class yang membatasi satu superclass, sebuah class Kotlin dapat mengimplementasikan banyak interface sekaligus — menjadikan interface alat utama untuk mendefinisikan kontrak yang dapat dipakai ulang lintas hierarki. Pada chapter ini kita akan mendeklarasikan interface dengan method abstrak dan method default, melihat interface dengan property abstrak, dan mengamati class yang mengimplementasikan tiga interface sekaligus.

Apa Itu Interface #

Interface mirip dengan abstract class — keduanya dapat menyimpan method tanpa body yang harus diimplementasikan oleh class lain. Perbedaan utamanya:

AspekInterfaceAbstract class
Multiple inheritanceSatu class dapat implementasikan banyak interfaceHanya boleh warisi satu abstract class
State (field dengan nilai)Tidak bolehBoleleh
ConstructorTidak punyaPunya
Default method bodyBolelehBoleleh
Property abstrakBoleleh (tanpa backing field)Boleleh (dengan backing field)

Karena interface tidak memiliki state, ia lebih ringan dan cocok untuk menyatakan kemampuan (capability) yang dapat dimiliki banyak class dari hierarki berbeda — misalnya Comparable, Drawable, atau Serializable.

Method Abstrak dan Method Default #

Method dalam interface dapat ditulis dengan dua bentuk:

  1. Method abstrak — hanya signature tanpa body. Class implementasi wajib menyediakan body.
  2. Method default — body disediakan oleh interface. Class implementasi dapat memakainya apa adanya atau menggantinya.
kotlin Contoh: interface dengan method default
interface Bergerak {
    fun bergerak(jarak: Int): String {
        return "Bergerak sejauh $jarak meter"
    }
}

Class yang mengimplementasikan Bergerak dapat langsung memanggil bergerak(10) tanpa menulis implementasi sendiri. Method default berguna saat mayoritas class implementasi memerlukan perilaku yang sama — kita menyimpan implementasi sekali di interface dan menghemat duplikasi.

Property pada Interface #

Interface dapat mendeklarasikan property, tetapi property tersebut bersifat abstrak — interface tidak menyediakan backing field. Class implementasi wajib menyediakan property melalui override val atau override var. Interface juga dapat menyediakan getter default untuk property yang nilainya dihitung, namun tidak dapat menyimpan field internal.

kotlin Contoh: interface dengan property abstrak dan getter default
interface Berhitung {
    val faktor: Int
    fun kalikan(x: Int): Int = x * faktor
}

Interface Berhitung menyatakan bahwa setiap class implementasi wajib memiliki property faktor. Method kalikan memiliki body default yang membaca property faktor tersebut — saat dipanggil, runtime membaca faktor dari implementasi class.

Contoh Lengkap dengan Tiga Interface #

Contoh berikut mendefinisikan tiga interface — Bersuara, Bergerak, dan Berhitung — lalu membuat class Robot yang mengimplementasikan ketiganya. Class HewanMamalia hanya mengimplementasikan dua interface (Bersuara dan Bergerak), menunjukkan bahwa interface bersifat opsional dan dapat dikombinasikan sesuai kebutuhan.

kotlin
// interfaces.kt
// Mendemonstrasikan interface, default method, multiple implementation.

interface Bersuara {
    fun bersuara(): String
}

interface Bergerak {
    fun bergerak(jarak: Int): String {
        return "Bergerak sejauh $jarak meter"
    }
}

interface Berhitung {
    val faktor: Int

    fun kalikan(x: Int): Int = x * faktor
}

class Robot(val nama: String) : Bersuara, Bergerak, Berhitung {
    override val faktor: Int = 2

    override fun bersuara(): String {
        return "$nama berbunyi: BEEP BOOP"
    }
}

class HewanMamalia(val nama: String) : Bersuara, Bergerak {
    override fun bersuara(): String {
        return "$nama bersuara umum"
    }
}

fun main() {
    val r = Robot("R2D2")
    println(r.bersuara())
    println(r.bergerak(10))
    println("Hasil kalikan 5 dengan faktor ${r.faktor}: ${r.kalikan(5)}")

    val h = HewanMamalia("Kucing")
    println(h.bersuara())
    println(h.bergerak(3))

    println()
    println("Loop polimorfik terhadap Bersuara:")
    val daftar: List<Bersuara> = listOf(r, h)
    for (x in daftar) {
        println("- ${x.bersuara()}")
    }
}

Output yang Dihasilkan:

text
R2D2 berbunyi: BEEP BOOP
Bergerak sejauh 10 meter
Hasil kalikan 5 dengan faktor 2: 10
Kucing bersuara umum
Bergerak sejauh 3 meter

Loop polimorfik terhadap Bersuara:
- R2D2 berbunyi: BEEP BOOP
- Kucing bersuara umum

Mari kita amati interaksi antar interface:

  1. interface Bersuara { fun bersuara(): String }: interface dengan satu method abstrak. Class implementasi wajib menyediakan body untuk bersuara().
  2. interface Bergerak { fun bergerak(jarak: Int): String { ... } }: interface dengan method default. Class implementasi dapat memakai implementasi default atau menggantinya.
  3. interface Berhitung { val faktor: Int; fun kalikan(x: Int): Int = x * faktor }: interface dengan property abstrak dan method default yang membaca property tersebut. Interface tidak menyimpan nilai faktor — class implementasi yang harus menyediakannya.
  4. class Robot(val nama: String) : Bersuara, Bergerak, Berhitung: tanda titik dua diikuti tiga interface dipisah koma. Class mengimplementasikan ketiganya sekaligus — multiple interface implementation.
  5. override val faktor: Int = 2: Robot menyediakan property faktor yang dijanjikan interface Berhitung. Method default kalikan di interface akan membaca nilai ini saat dipanggil melalui instance Robot — bukti bahwa method default dapat bergantung pada property implementasi.
  6. r.bergerak(10): Robot tidak meng-override bergerak(), sehingga method default Bergerak yang dipanggil. Pemanggilan mengembalikan teks default "Bergerak sejauh 10 meter".
  7. r.kalikan(5): pemanggilan method default dari interface Berhitung. Body method membaca property faktor (yang disediakan Robot sebagai 2), sehingga hasilnya 5 * 2 = 10.
  8. val daftar: List<Bersuara> = listOf(r, h): variabel bertipe List<Bersuara> dapat menampung instance Robot dan HewanMamalia karena keduanya mengimplementasikan Bersuara. Loop memanggil x.bersuara() yang dipolimorfik sesuai implementasi masing-masing class.

Multiple Interface Implementation #

Karena sebuah class dapat mengimplementasikan banyak interface, kita dapat menyusun kemampuan secara fleksibel. Class Robot yang mengimplementasikan Bersuara, Bergerak, dan Berhitung dapat diperlakukan sebagai ketiga tipe tersebut bergantung pada konteks. Function yang menerima parameter bertipe Bergerak dapat memproses Robot maupun HewanMamalia tanpa peduli class konkret yang dimasukkan.

kotlin Contoh: function yang menerima interface
fun jarakTotal(bergerak: Bergerak, daftarJarak: List<Int>): Int {
    return daftarJarak.sumOf { it }
}

Function jarakTotal menerima argumen bertipe Bergerak — apa pun class konkret yang dimasukkan, selama mengimplementasikan Bergerak, function dapat memprosesnya. Tambah class baru yang mengimplementasikan Bergerak, dan function tetap bekerja tanpa perubahan.

Konflik Method Default #

Bila dua interface yang diimplementasikan memiliki method default dengan signature sama, class implementasi harus menyelesaikan konflik dengan meng-override method tersebut secara eksplisit. Override dapat memanggil implementasi salah satu interface melalui super<InterfaceName>.method().

kotlin Contoh: menyelesaikan konflik method default
interface A { fun foo() = "A" }
interface B { fun foo() = "B" }

class C : A, B {
    override fun foo(): String {
        return super<A>.foo() + " dan " + super<B>.foo()
    }
}

Tanpa override eksplisit, kompilator akan menolak deklarasi C : A, B karena ambigu.

Referensi Lanjutan #

  • Interfaces — panduan resmi deklarasi interface dan method default

Ringkasan #

Interface adalah kontrak yang menyatakan method dan property apa yang harus disediakan class implementasi. Interface dapat memiliki method default dan property abstrak, namun tidak boleh memiliki state atau constructor. Sebuah class Kotlin dapat mengimplementasikan banyak interface sekaligus, memberikan fleksibilitas yang tidak dimiliki abstract class. Pada chapter Interface Delegation berikutnya, kita akan melihat bagaimana class dapat mendelegasikan implementasi interface ke object lain.