Kotlin OOP: Mengenal Pewarisan/Inheritance

Pewarisan atau disebut inheritance merupakan fitur yang sangat penting dari pemrograman OOP. OOP tanpa pewarisan bagaikan tubuh tanpa darah. Sebenarnya apa itu pewarisan? Apa guna pewarisan? Lalu, Bagaimana pewarisan di bahasa Kotlin?

Mengapa Kita Membutuhkan Pewarisan?

Pewarisan sangat berguna jika terdapat kelas yang mempunyai kesamaan anggota. Untuk lebih jelasnya kalian bisa lihat kasus di bawah ini.

Terdapat kelas yang bernama Mobil. Kelas ini memiliki properti warna dan produsen. Kelas ini juga memiliki fungsi bernama hidupkan dan matikan. Deklarasi kelasnya seperti ini:

class Mobil {
    val warna = "Apapun"
    val produsen = "Apapun"

    fun hidupkan() {
        println("Mobil dihidupkan")
    }

    fun matikan() {
        println("Mobil dimatikan")
    }
}

Kemudian kita ingin membuat kelas bernama Avanza. Kita ingin kelas Avanza mempunyai anggota yang sama dengan anggota kelas Mobil. Mungkin kita bisa copy paste kode dari kelas Mobil ke Avanza. Jadi kelasnya seperti ini:

class Avanza {
    val warna = "Apapun"
    val produsen = "Apapun"

    fun hidupkan() {
        println("Mobil dihidupkan")
    }

    fun matikan() {
        println("Mobil dimatikan")
    }
}

Tetapi cara ini kurang efisien. Pada permasalahan ini kita bisa menggunakan Pewarisan/Inheritance.

Apa Itu Pewarisan?

Pewarisan atau inheritance adalah fitur pemrograman OOP yang memungkinkan kita mewariskan anggota suatu kelas ke kelas lain. Apa maksud dari mewariskan anggota? Untuk memahaminya kita bisa terapkan pada contoh tadi.

Kelas Avanza bisa mewarisi anggota (properti dan fungsi) dari kelas Mobil. Artinya di dalam kelas Avanza akan terdapat semua anggota kelas Mobil. Kelas Avanza akan memiliki properti yang merupakan properti kelas Mobil yaitu warna dan produsen. Kelas Avanza juga akan memiliki fungsi yang berasal dari kelas Mobil yaitu hidupkan dan matikan.

Pada pewarisan sifat terdapat istilah kelas induk atau super class dan kelas turunan atau sub class. Kelas induk adalah kelas yang anggotanya diwariskan ke kelas lain. Sedangkan kelas turunan adalah kelas yang mewarisi anggota kelas lain. Pada contoh di atas kelas Mobil adalah kelas induk dan kelas Avanza adalah kelas turunan.

Pewarisan di Kotlin

Kita bisa menulis ulang kelas Avanza agar lebih ringkas dengan pewarisan. Kelas Avanza bisa mewarisi kelas Mobil sehingga kita tidak perlu mendeklarasikan ulang anggota-anggotanya. Kodenya menjadi seperti ini:

class Avanza : Mobil() {

}

Sekarang kode terlihat sangat ringkas.

Pada bahasa pemrograman Kotlin pewarisan sifat bisa dilakukan dengan menggunakan titik dua (:) setelah nama kelas. Mobil() menentukan cara memanggil konstruktor kelas Mobil saat objek dari kelas Avanza dibuat.

Terdapat error pada kode di atas yang mempunyai pesan this type is final, so it cannot be inherited from. Error ini terjadi karena kelas Mobil adalah kelas final. Pada Kotin semua kelas secara default adalah kelas final. Kelas final adalah kelas yang tidak dapat diwariskan.

Lalu bagaimana cara untuk mengatasi error ini?. Caranya dengan mengubah kelas Mobil menjadi kelas open. Kelas open merupakan kebalikan kelas final. Kelas open merupakan kelas yang dapat diwariskan.

Untuk mengubah kelas Mobil menjadi kelas open kita hanya perlu menambahkan keywordopen sebelum class di deklarasi kelas tersebut. Beginilah jadinya deklarasi kelasnya:

open class Mobil {
    // ...
}

Error tadi seketika hilang!

Sekarang mari kita coba jalankan kode ini:

val avanza = Mobil()
println(avanza.warna) // Apapun
println(avanza.produsen) // Apapun

Terlihat bahwa saat kita mengakses properti warna pada kelas Avanza maka yang diakses adalah properti warna milik kelas Mobil. Begitu juga dengan properti produsen.

Lalu jalankan kode ini:

avanza.hidupkan() // Mobil dihidupkan
avanza.matikan() // Mobil dimatikan

Terlihat bahwa pada saat kita memanggil fungsi matikan pada kelas Avanza maka yang terpanggil adalah fungsi matikan milik kelas Mobil. Begitu juga dengan fungsi hidupkan.

Override Fungsi dan Properti

Bagaimana caranya agar fungsi matikan milik kelas Mobil tidak dipanggil saat fungsi matikan milik kelas Avanza dipanggil?. Untuk melakukan ini kita bisa men-override fungsi tersebut.

Override memungkinkan kita untuk mengimplementasikan ulang fungsi atau properti pada kelas induk yang diturunkan ke kelas anak.

Misalkan kita ingin mengimplementasikan ulang fungsi hidupkan, fungsi matikan, properti produsen dan properti warna pada kelas Avanza. Kode seperti berikut:

class Avanza : Mobil() {
    override val warna = "Merah"
    override val produsen = "Toyota"

    override fun hidupkan() {
        println("Avanza dihidupkan")
    }

    override fun matikan() {
        println("Avanza dimatikan")
    }
}

Namun kode kita ternyata ada banyak error seperti ini:

error: 'warna' in 'Mobil' is final and cannot be overridden
    override val warna = "Merah"
    ^
error: 'produsen' in 'Mobil' is final and cannot be overridden
    override val produsen = "Toyota"
    ^
error: 'hidupkan' in 'Mobil' is final and cannot be overridden
    override fun hidupkan() {
    ^
error: 'matikan' in 'Mobil' is final and cannot be overridden
    override fun matikan() {
    ^

Apa maksudnya? Ternyata fungsi hidupkan, fungsi matikan, properti produsen dan properti warna berstatus final. Semua anggota kelas secara default berstatus final. Ini mirip dengan kelas final. Anggota kelas yang berstatus final tidak dapat di-override.

Lalu bagaimana cara mengatasinya? Caranya dengan mengubah keempat anggota kelas tadi menjadi berstatus open. Anggota kelas yang berstatus open bisa di-override

Untuk mengubahnya kita tinggal menambah keyword open sebelum deklarasi fungsi (fun) atau variabel (val/var). Deklarasi kelas Mobil akan menjadi begini:

open class Mobil {
    open val warna = "Apapun"
    open val produsen = "Apapun"

    open fun hidupkan() {
        println("Mobil dihidupkan")
    }

    open fun matikan() {
        println("Mobil dimatikan")
    }
}

Seketika semua error di atas menghilang!

Deklarasi fungsi atau properti harus diberi awalan keyword override. Coba kalian jalankan kode seperti ini lagi dan lihat hasilnya:

println(avanza.warna) // Merah
println(avanza.produsen) // Toyota
avanza.hidupkan() // Avanza dihidupkan
avanza.matikan() // Avanza dimatikan

Terlihat setelah kita men-override 2 fungsi di atas, fungsi induk tidak dipanggil saat kedua fungsi dipanggil. Begitu juga dengan 2 properti di atas, properti induk tidak diakses saat kedua fungsi dipanggil.

Memanggil Fungsi Induk dari Kelas Anak

Kadang kita perlu memanggil fungsi milik kelas induk dari kelas Anak khususnya saat men-override fungsi. Untuk memanggil fungsi induk dari kelas anak kita bisa menggunakan super.

Contohnya saat fungsi hidupkan dari kelas Avanza dipanggil maka fungsi hidupkan kelas Mobil dipanggil pada awal fungsi. Sedangkan saat fungsi matikan dari kelas Avanza dipanggil maka fungsi matikan kelas Mobil dipanggil pada akhir fungsi. Kodenya seperti ini:

class Avanza : Mobil {
    // kodenya sama

    override fun hidupkan() {
        super.hidupkan()
        println("Avanza dihidupkan")
    }

    override fun matikan() {
        println("Avanza dimatikan")
        super.matikan()
    }
}

Jalankan kode di bawah dan lihat hasilnya:

avanza.hidupkan()
// Mobil dihidupkan
// Avanza dihidupkan
avanza.matikan()
// Avanza dimatikan
// Mobil dimatikan

Terlihat bahwa fungsi hidupkan dan matikan dari kelas Mobil terpanggil.