Subclass tidak hanya mewarisi property dan method — subclass juga dapat menggantinya dengan implementasi sendiri melalui keyword override. Di Kotlin, baik method maupun property dapat di-override, dan keduanya mengikuti aturan polymorphism yang sama. Pada chapter ini kita akan melihat cara override property val dengan nilai berbeda, mengubah val menjadi var (yang diizinkan), serta bagaimana property override tetap bekerja secara dinamis saat diakses dari method superclass.

Meng-override Property val #

Property val pada superclass dapat di-override dengan override val pada subclass. Override ini menggantikan nilai maupun cara property dihitung. Karena property Kotlin pada dasarnya adalah pasangan getter (dan setter untuk var), override property sebenarnya menggantikan accessor yang dipanggil.

kotlin Contoh: override property val
open class Hewan {
    open val nama: String = "Tanpa nama"
}

class Kucing : Hewan() {
    override val nama: String = "Kucing"
}

Saat variabel bertipe Hewan menampung instance Kucing, pembacaan property nama akan memanggil getter yang di-override — mengembalikan "Kucing". Hal ini terjadi karena property override bersifat polymorphism sama seperti method override.

Aturan Mutabilitas: val ke var Diizinkan #

Kotlin mengizinkan subclass mengganti property val pada superclass dengan property var pada subclass. Arah sebaliknya — var menjadi val — dilarang keras karena dapat melanggar kontrak superclass yang sudah menjanjikan property dapat diubah.

Arah OverrideStatus
val → valDiizinkan
val → varDiizinkan (subclass memperluas kemampuan)
var → valDilarang (menyempitkan kontrak superclass)
var → varDiizinkan

Alasan val → var diizinkan adalah bahwa val sebenarnya adalah subset dari var: property read-only adalah property mutable yang kebetulan tidak menyediakan setter. Menambahkan setter pada subclass tidak melanggar kontrak superclass — superclass tetap hanya memanggil getter.

Meng-override Method #

Override method dilakukan dengan override fun pada subclass, menggantikan implementasi superclass yang ditandai open fun. Body subclass dapat memanggil implementasi superclass melalui keyword super bila diperlukan.

kotlin Contoh: override method dengan super
open class Hewan {
    open fun bersuara(): String = "..."
}

class Kucing : Hewan() {
    override fun bersuara(): String = "Meong"
}

Pemanggilan method yang di-override bersifat dinamis — tipe asli object menentukan implementasi yang dipanggil, bukan tipe deklarasi variabel.

Contoh Lengkap Property dan Method Override #

Contoh berikut menggabungkan override property dan method. Superclass Kendaraan memiliki property roda (val) dan method deskripsi() yang membaca property tersebut. Subclass Mobil meng-override kedua anggota, sementara SepedaMotor meng-override property roda menjadi var dan menyediakan method untuk mengubahnya — namun tidak meng-override deskripsi().

kotlin
// overriding.kt
// Mendemonstrasikan override property val/var dan method; val -> var diizinkan.

open class Kendaraan(val merek: String) {
    open val roda: Int = 0

    open fun deskripsi(): String {
        return "Kendaraan $merek dengan $roda roda"
    }
}

class Mobil(merek: String, val tipe: String) : Kendaraan(merek) {
    // Override property val dengan val baru (nilai berbeda).
    override val roda: Int = 4

    override fun deskripsi(): String {
        return "Mobil $merek ($tipe), roda=$roda"
    }
}

class SepedaMotor(merek: String) : Kendaraan(merek) {
    // Override val dengan var — diizinkan karena var lebih longgar dari val.
    override var roda: Int = 2

    fun tambahRodaCadangan() {
        roda = 3
    }
}

fun main() {
    val m = Mobil("Toyota", "Sedan")
    println(m.deskripsi())
    println("Property roda: ${m.roda}")

    val s = SepedaMotor("Honda")
    println(s.deskripsi())
    println("Awal roda motor: ${s.roda}")

    s.tambahRodaCadangan()
    println("Setelah tambahRodaCadangan: ${s.roda}")

    println()
    println("Loop polimorfik:")
    val daftar: List<Kendaraan> = listOf(m, s)
    for (v in daftar) {
        println("- ${v.deskripsi()} (roda=${v.roda})")
    }
}

Output yang Dihasilkan:

text
Mobil Toyota (Sedan), roda=4
Property roda: 4
Kendaraan Honda dengan 2 roda
Awal roda motor: 2
Setelah tambahRodaCadangan: 3

Loop polimorfik:
- Mobil Toyota (Sedan), roda=4 (roda=4)
- Kendaraan Honda dengan 3 roda (roda=3)

Mari kita amati interaksi override property dan method:

  1. override val roda: Int = 4 pada Mobil: property roda di-override dengan nilai tetap 4. Pemanggilan m.roda mengembalikan 4 karena getter override yang dipanggil.
  2. override var roda: Int = 2 pada SepedaMotor: property val superclass di-override menjadi var. Sekarang property dapat diubah melalui tambahRodaCadangan(). Pola ini sah karena subclass memperluas kontrak — superclass tidak kehilangan kemampuan apa pun.
  3. Method deskripsi() pada superclass membaca roda: implementasi superclass Kendaraan.deskripsi() memakai referensi roda. Saat dipanggil pada instance SepedaMotor, runtime memilih getter property roda yang di-override — sehingga setelah tambahRodaCadangan(), pesan menampilkan 3 roda meskipun method deskripsi() sendiri tidak di-override.
  4. Mobil meng-override deskripsi(): subclass menyediakan format teks sendiri. Saat dipanggil, runtime memilih implementasi Mobil karena method bersifat polymorphism.
  5. SepedaMotor tidak meng-override deskripsi(): method deskripsi() mengikuti implementasi superclass. Tapi karena property roda di-override, hasil teksnya tetap mencerminkan nilai terbaru property subclass.
  6. Loop polimorfik: variabel v bertipe Kendaraan, namun v.deskripsi() memilih implementasi sesuai tipe asli object (Mobil atau SepedaMotor). Inilah bukti bahwa property override bekerja sama dengan method override secara konsisten.

Memanggil Implementasi Superclass dengan super #

Subclass dapat memanggil implementasi superclass melalui super.namaMethod() atau super.namaProperty. Pola ini berguna saat subclass ingin memperluas perilaku superclass alih-alih menggantinya sepenuhnya.

kotlin Contoh: memanggil super dalam override
open class Hewan {
    open fun bersuara(): String = "Suara umum"
}

class Kucing : Hewan() {
    override fun bersuara(): String {
        val asli = super.bersuara()
        return "$asli, lalu Meong!"
    }
}

Pemanggilan super.bersuara() mengembalikan implementasi superclass tanpa memicu override subclass lagi. Hasilnya kemudian digabung dengan teks tambahan dari subclass.

Aturan Penting Override #

Beberapa aturan yang perlu diingat:

  • Property yang di-override juga harus open di superclass.
  • Tipe property subclass harus kompatibel dengan tipe superclass (mis. Int → Int, bukan Int → String).
  • Override property val menjadi var mengharuskan subclass menyediakan setter. Kompilator otomatis menyediakan setter default bila tidak ditulis manual.
  • Override method dengan signature yang berbeda (mis. parameter berbeda) bukan override — itu adalah method baru yang menutup (shadow) method superclass.

Referensi Lanjutan #

  • Inheritance — aturan resmi override method dan property pada subclass

Ringkasan #

Property dan method sama-sama dapat di-override dengan keyword override. Property val dapat diubah menjadi var pada subclass karena var lebih longgar; arah sebaliknya dilarang. Property override bersifat polymorphism sehingga method superclass yang membaca property akan mendapatkan nilai dari implementasi subclass. Pada chapter Abstract Classes berikutnya, kita akan melihat bagaimana class abstrak memaksa subclass untuk meng-override method dan property tertentu — sebuah bentuk kontrak yang lebih ketat daripada open.