Java memungkinkan sebuah kelas dibangun di atas kelas lain lewat pewarisan (inheritance), yaitu mekanisme mewariskan field dan metode dari kelas induk (superclass) ke kelas turunan (subclass). Topik ini mempraktikkan deklarasi extends, pemanggilan anggota induk lewat super, serta penelusuran urutan kerja konstruktor ketika objek turunan dibuat. Setelah membacanya, hierarki kelas dua tingkat beserta alur konstruksinya dapat ditulis dan dibaca dengan meyakinkan.
Mewariskan kelas dengan extends#
Kata kunci extends menyatakan bahwa satu kelas mewarisi kelas lain. Kelas yang memperluas disebut kelas turunan, sedangkan kelas yang diperluas disebut kelas induk. Turunan otomatis membawa field dan metode induk yang tidak bersifat private, sehingga kode di kelas turunan memakainya langsung tanpa menulis ulang. Pola ini menempatkan bagian yang sama di satu tempat, sedangkan setiap turunan hanya menambah atau menyesuaikan bagian khasnya.
public class DemoPewarisan {
public static void main(String[] args) {
ArsipGambar foto = new ArsipGambar("pantai.png", 1280, 720);
System.out.println("Nama berkas : " + foto.namaBerkas);
System.out.println("Ukuran : " + foto.lebar + " x " + foto.tinggi);
System.out.println("Ringkasan : " + foto.deskripsi());
}
}
class Arsip {
String namaBerkas;
Arsip(String namaBerkas) {
this.namaBerkas = namaBerkas;
System.out.println("Konstruktor Arsip dijalankan untuk " + namaBerkas);
}
String deskripsi() {
return "Berkas " + namaBerkas;
}
}
class ArsipGambar extends Arsip {
int lebar;
int tinggi;
ArsipGambar(String namaBerkas, int lebar, int tinggi) {
super(namaBerkas);
this.lebar = lebar;
this.tinggi = tinggi;
System.out.println("Konstruktor ArsipGambar dijalankan untuk " + namaBerkas);
}
}Konstruktor Arsip dijalankan untuk pantai.png
Konstruktor ArsipGambar dijalankan untuk pantai.png
Nama berkas : pantai.png
Ukuran : 1280 x 720
Ringkasan : Berkas pantai.pngProgram di atas membuat ArsipGambar tanpa mendeklarasikan namaBerkas atau deskripsi() di kelasnya, karena keduanya diwarisi dari Arsip. Dua baris pertama keluaran berasal dari konstruktor yang berjalan berurutan, induk lebih dahulu baru turunan, mekanisme yang dibahas pada bagian rantai konstruktor. Field lebar dan tinggi hanya dimiliki ArsipGambar, sehingga ringkasannya memuat ukuran yang tidak ada di induk. Bila kelak ada ArsipVideo atau ArsipDokumen, masing-masing cukup menulis bagian yang berbeda saja.
Anggota yang diwariskan dan yang tidak#
Hampir semua anggota ikut diwariskan, tetapi pengecualiannya perlu diingat. Konstruktor tidak diwariskan karena bukan anggota kelas, sehingga turunan menyediakan konstruktornya sendiri dan mengisi data induk lewat super(...). Field dan metode private tidak ikut diwariskan, meskipun representasinya tetap tersimpan di dalam objek turunan yang dibangun bersama konstruktor induk, sehingga kode turunan tidak dapat menyentuhnya secara langsung. Cara mengaksesnya adalah metode pengakses yang disediakan induk, atau field yang ditandai protected agar turunan boleh membacanya.
Kelas Object menjadi ujung setiap rantai pewarisan. Kelas apa pun yang tidak menuliskan extends sebenarnya mewarisi Object secara diam-diam, sehingga metode seperti toString() dan equals() tersedia di semua kelas. Aturan ini membuat seluruh objek Java berbagi perilaku dasar yang sama, dan kelas turunan kapan pun boleh menyesuaikan perilaku itu untuk kebutuhannya.
Kata kunci super untuk memanggil versi induk#
Kata kunci super mengacu pada bagian induk dari objek yang sama. Bentuk super.metode() dan super.field memanggil anggota induk, berguna saat turunan mendefinisikan anggota bernama sama dan versi induk tetap dibutuhkan. Bentuk super(...) berbeda maknanya karena memanggil konstruktor induk, dan hanya sah pada baris pertama badan konstruktor. Keduanya sering bekerja bersama, karena konstruktor turunan menyiapkan data milik induk lewat super(...), sementara metode turunan menambah perilaku di atas hasil super.metode().
public class UjiSuper {
public static void main(String[] args) {
LaporanBulanan laporan = new LaporanBulanan("Oktober", 42);
System.out.println(laporan.judulPanjang());
}
}
class Laporan {
String periode;
Laporan(String periode) {
this.periode = periode;
}
String judul() {
return "Laporan " + periode;
}
}
class LaporanBulanan extends Laporan {
int jumlahEntri;
LaporanBulanan(String periode, int jumlahEntri) {
super(periode);
this.jumlahEntri = jumlahEntri;
}
String judulPanjang() {
return super.judul() + " berisi " + jumlahEntri + " entri";
}
}Laporan Oktober berisi 42 entriMetode judulPanjang() tidak pernah membentuk judul dasarnya sendiri, melainkan menitipkannya kepada super.judul(). Bila aturan judul di Laporan berubah, misalnya menambahkan awalan tahun, hasil judulPanjang() ikut berubah tanpa suntingan di kelas turunan. Perubahan satu tempat yang menjalar ke seluruh turunan inilah keuntungan pewarisan yang dirawat dengan baik, sekaligus alasan super lebih disukai daripada menyalin ulang logika induk.
Rantai konstruktor dari anak ke induk#
Membuat objek turunan selalu memicu konstruksi induknya terlebih dahulu. Baris pertama konstruktor turunan yang tidak menuliskan super(...) sebenarnya memanggil super() tanpa argumen, dan pemanggilan itu berlaku berantai hingga mencapai Object. Urutannya terlihat pada keluaran DemoPewarisan di atas, yaitu pesan Konstruktor Arsip muncul sebelum Konstruktor ArsipGambar, karena badan induk selesai lebih dulu sebelum badan turunan mulai berjalan.
Diagram berikut merangkum alur pemanggilan tersebut dari titik new hingga badan turunan selesai.
Urutan yang sama berlaku untuk inisialisasi field. Field induk terisi lebih dulu, baru field turunan, dan semuanya sudah siap ketika badan konstruktor turunan bekerja. Konsekuensinya, metode yang dipanggil dari dalam konstruktor induk melihat objek yang field turunannya mungkin belum terisi, sehingga panggilan metode turunan dari konstruktor induk sebaiknya dihindari bila metode itu bergantung pada field turunan.
Kesalahan umum#
- Mengira konstruktor ikut diwariskan. Turunan tidak mewarisi konstruksi induk, sehingga menulis
new Anak(...)menuntut konstruktorAnakyang dideklarasikan sendiri. Isi datanya lewatsuper(...)yang dipanggil di baris pertama. - Mengakses field
privatemilik induk langsung dari kelas turunan. Kompilator menolak dengan pesanx has private access in Indukmeskipun field itu ikut terbawa di objeknya. Gunakan metode pengakses yang ada di induk, atau naikkan tingkat aksesnya keprotectedbila memang disengaja untuk turunan. - Menulis
super(...)di tengah badan konstruktor. Kompilator menolaknya dengan pesancall to super must be first statement in constructor. Pindahkan pemanggilannya ke baris pertama, lalu susun perhitungan turunan setelahnya.
Ringkasan#
extendsmewariskan field dan metode induk ke kelas turunan, kecuali konstruktor yang tidak diwariskan dan anggotaprivateyang tidak dapat disentuh turunan.super.metode()dansuper.fieldmemanggil versi induk, sedangkansuper(...)memanggil konstruktor induk dan wajib berada di baris pertama konstruktor.- Konstruksi objek turunan selalu menjalankan konstruktor induk lebih dulu, berantai sampai
Object, sehingga urutan keluaran konstruksinya dapat diprediksi.
Referensi#
- The Java Tutorials: Inheritance — Oracle
— Pelajaran resmi tentang pewarisan, termasuk deklarasi
extendsdan anggota yang diwariskan. - The Java Tutorials: Using the Keyword super — Oracle
— Aturan pemanggilan anggota induk dan konstruktor induk lewat
super. - JLS SE 21 §8.1.4: Superclasses and Subclasses — Oracle
— Spesifikasi relasi kelas induk dan turunan beserta syarat
extends.