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.

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.
extendsInheritance menciptakan relasi "is-a" (adalah-sebuah). MobilListrik adalah sebuah Kendaraan:
public class Kendaraan {
protected String merk;
void gas() {
System.out.println(merk + " melaju");
}
}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).
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
}
}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.
@OverrideSubclass bisa menimpa (override) method superclass dengan implementasi sendiri — syaratnya signature sama persis:
public class Kendaraan {
void suara() {
System.out.println("Mesin berbunyi");
}
}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.
superKeyword super merujuk ke superclass — dipakai untuk memanggil method superclass yang di-override:
public class MobilListrik extends Kendaraan {
@Override
void suara() {
super.suara(); // panggil versi superclass
System.out.println("Ditambah mode listrik");
}
}Ini salah satu aturan yang paling sering membuat error compile. Ketika subclass dibuat, constructor superclass wajib dipanggil — entah eksplisit lewat super(...) atau implisit. Aturannya:
super(...) di baris pertama, ATAUsuper()).super(...) secara eksplisit — kalau tidak, error compile.public class Kendaraan {
protected String merk;
Kendaraan(String merk) {
this.merk = merk;
}
}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 — Object → Kendaraan → MobilListrik. Ini menjamin state superclass terinisialisasi sebelum subclass menyentuhnya.
Object: Root dari Semua ClassSemua 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.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).
Inheritance membuat subclass terikat ke detail superclass. Jika superclass berubah, semua subclass ikut terpengaruh — kadang tanpa disadari. Masalah klasik:
Bebek 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 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:
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.
Inheritance tetap tepat ketika ketiganya benar:
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.
Inti yang harus dibawa pulang:
extends menciptakan relasi "is-a"; subclass mewarisi state dan behavior superclass.@Override.super(...) memanggil constructor superclass — wajib jika superclass tidak punya constructor no-arg.Object — toString, equals, hashCode ada di mana-mana.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!