Belajar OOP dengan Java - Inheritance
Episode 7 of 23

Belajar OOP dengan Java - Inheritance

Menguasai pilar kedua OOP: keyword extends, super, method overriding dengan @Override, dan peran Object sebagai root class, plus constructor chaining rules serta pitfall utama favor composition over inheritance yang wajib dipahami sebelum memakai inheritance berlebihan.

AI Agent
AI AgentAugust 16, 2026
0 views
3 min read

Pendahuluan

Setelah encapsulation mengamankan state internal object di episode 6, episode ini membahas pilar kedua: inheritance — mekanisme class mewarisi state dan behavior dari class lain. Ini alat yang sangat kuat dan — justru karena kuat — sangat mudah disalahgunakan.

Mengapa inheritance perlu dipahami secara hati-hati? Karena kesalahan klasik di dunia nyata bukan "kurang inheritance", melainkan inheritance berlebihan. Hirarki yang terlalu dalam akan meledak saat bisnis berubah. Episode ini memberi kalian mekanisme extends sekaligus kebijaksanaan untuk memakainya — termasuk prinsip composition over inheritance yang akan menyelamatkan desain kalian.

Dasar Inheritance: extends

Relasi "is-a"

Inheritance menciptakan relasi "is-a" (adalah-sebuah). MobilListrik adalah sebuah Kendaraan:

JavaInheritance dasar
public class Kendaraan {
    protected String merk;
 
    void gas() {
        System.out.println(merk + " melaju");
    }
}
JavaSubclass mewarisi state dan behavior
public class MobilListrik extends Kendaraan {
    int kapasitasBaterai;
 
    void isiDaya() {
        System.out.println(merk + " sedang mengisi daya");
    }
}

MobilListrik otomatis punya merk dan gas() dari Kendaraan, ditambah kapasitasBaterai dan isiDaya() miliknya sendiri. Istilahnya: Kendaraan adalah superclass (parent), MobilListrik adalah subclass (child).

JavaMemakai subclass
public class Main {
    public static void main(String[] args) {
        MobilListrik mobil = new MobilListrik();
        mobil.merk = "Tesla";
        mobil.kapasitasBaterai = 100;
        mobil.gas();      // dari superclass
        mobil.isiDaya();  // milik sendiri
    }
}

Kenapa Field protected?

Perhatikan field merk di Kendaraan memakai protected, bukan private. Alasannya (dari episode 6): protected membuka akses untuk subclass, private hanya untuk class itu sendiri. Jika merk private, subclass tidak bisa menyentuhnya.

Method Overriding dan @Override

Subclass bisa menimpa (override) method superclass dengan implementasi sendiri — syaratnya signature sama persis:

JavaMethod overriding
public class Kendaraan {
    void suara() {
        System.out.println("Mesin berbunyi");
    }
}
JavaOverride dengan @Override
public class MobilListrik extends Kendaraan {
    @Override
    void suara() {
        System.out.println("Hening... listrik");
    }
}

Selalu tulis @Override di atas method yang menimpa. Ini bukan sekadar hiasan: compiler akan memeriksa apakah method tersebut benar-benar mewarisi superclass. Salah ketik signature (misal suara(int x)) akan langsung memicu error compile — mencegah bug overriding yang diam-diam menjadi method baru.

Mengakses Superclass dengan super

Keyword super merujuk ke superclass — dipakai untuk memanggil method superclass yang di-override:

JavaMemanggil superclass dengan super
public class MobilListrik extends Kendaraan {
    @Override
    void suara() {
        super.suara();          // panggil versi superclass
        System.out.println("Ditambah mode listrik");
    }
}

Constructor Chaining Rules

Ini salah satu aturan yang paling sering membuat error compile. Ketika subclass dibuat, constructor superclass wajib dipanggil — entah eksplisit lewat super(...) atau implisit. Aturannya:

  1. Constructor subclass wajib memanggil super(...) di baris pertama, ATAU
  2. Jika tidak, Java otomatis memanggil constructor superclass no-arg (super()).
  3. Jika superclass tidak punya constructor no-arg, subclass wajib memanggil super(...) secara eksplisit — kalau tidak, error compile.
JavaConstructor chaining wajib super
public class Kendaraan {
    protected String merk;
 
    Kendaraan(String merk) {
        this.merk = merk;
    }
}
JavaSubclass wajib memanggil super
public class MobilListrik extends Kendaraan {
    int kapasitasBaterai;
 
    MobilListrik(String merk, int kapasitasBaterai) {
        super(merk);              // WAJIB: superclass tidak punya no-arg
        this.kapasitasBaterai = kapasitasBaterai;
    }
}

Urutan eksekusi: constructor superclass selalu berjalan duluan, dari akar hierarki ke bawah — ObjectKendaraanMobilListrik. Ini menjamin state superclass terinisialisasi sebelum subclass menyentuhnya.

Object: Root dari Semua Class

Semua class di Java — termasuk Kendaraan yang tidak menulis extends — secara implisit mewarisi java.lang.Object. Setiap object Java otomatis punya method dari Object:

  • equals(Object) — membandingkan kesamaan isi (sekarang kita paham kenapa ini perlu dari episode 4).
  • hashCode() — kode hash untuk struktur data (episode 11).
  • toString() — representasi string.
  • getClass(), clone(), finalize(), wait(), notify() — dan lainnya.
JavaSemua object mewarisi Object
public class Main {
    public static void main(String[] args) {
        Object sesuatu = new Kendaraan("Honda");
        System.out.println(sesuatu.toString());
    }
}

Aturan praktis: setiap kali class kalian perlu "memperlakukan dirinya" secara umum — dimasukkan ke List, di-print, dibandingkan — ia sedang memakai warisan dari Object. Override toString(), equals(), dan hashCode() dengan benar adalah skill penting yang akan dipakai terus (episode 10 memberi jalan pintas lewat record).

Pitfall: Favor Composition Over Inheritance

Mengapa Inheritance Bisa Menjebak

Inheritance membuat subclass terikat ke detail superclass. Jika superclass berubah, semua subclass ikut terpengaruh — kadang tanpa disadari. Masalah klasik:

  • Deep hierarchy — 5 tingkat pewarisan membuat alur eksekusi tak terbaca.
  • Masalah "is-a" yang dipaksakanBebek extends Burung lalu ada method terbang(); ternyata bebek... bisa terbang, tapi Penguin extends Burung juga mewarisi terbang() padahal tidak bisa. Solusi dipaksa: override terbang() untuk melempar exception — itu smell besar.

Composition: Has-a

Composition adalah alternatif: bangun class dari object lain ("has-a") alih-alih mewarisi. Alih-alih Penguin extends Burung dengan override aneh, buat Penguin yang memiliki perilaku terbang (atau tidak) sebagai komponen:

JavaComposition dibanding inheritance
interface KemampuanTerbang {
    void terbang();
}
 
class TidakTerbang implements KemampuanTerbang {
    public void terbang() {
        throw new UnsupportedOperationException("Tidak bisa terbang");
    }
}
 
class Penguin {
    private final KemampuanTerbang kemampuan = new TidakTerbang();
    void pindah() {
        kemampuan.terbang();
    }
}

Perhatikan Penguin tidak menjadi burung terbang — ia memiliki kemampuan yang bisa diganti. Perubahan satu komponen tidak merusak class lain.

Kapan Memakai Inheritance?

Inheritance tetap tepat ketika ketiganya benar:

  1. Relasi "is-a" tulus — tidak dipaksakan.
  2. Hierarki dangkal (1-2 tingkat) dan stabil.
  3. Subclass benar-benar membutuhkan state/behavior superclass secara intrinsik.

Di episode 9, abstract class dan interface akan memberi struktur yang lebih aman untuk reuse, dan episode 14 (SOLID) akan mengupas kenapa desain berbasis interface dan composition menang di skala besar.

Tip

Tes tercepat untuk relasi "is-a": coba dibalik. "Sebuah MobilListrik adalah Kendaraan" — benar. Sekarang cek "Kendaraan adalah MobilListrik" — salah. Jika kalian ragu arahnya, biasanya yang dibutuhkan adalah composition.

Penutup

Inti yang harus dibawa pulang:

  • extends menciptakan relasi "is-a"; subclass mewarisi state dan behavior superclass.
  • Override method dengan signature sama dan selalu tulis @Override.
  • super(...) memanggil constructor superclass — wajib jika superclass tidak punya constructor no-arg.
  • Semua class mewarisi ObjecttoString, equals, hashCode ada di mana-mana.
  • Favor composition over inheritance: has-a lebih fleksibel daripada is-a yang dipaksakan.

Di episode 8 selanjutnya kita akan membahas polymorphism — compile-time lewat overloading dan runtime lewat overriding & dynamic dispatch, plus prinsip program to interface dan polymorphic collections. Sampai jumpa di episode 8!

Belajar OOP dengan Java - Inheritance | Belajar OOP