Pewarisan

Mewariskan kelas dengan extends, memanggil super, dan menelusuri rantai konstruktor.

Daftar isi

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.

java DemoPewarisan.java
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);
    }
}
text Keluaran
Konstruktor Arsip dijalankan untuk pantai.png
Konstruktor ArsipGambar dijalankan untuk pantai.png
Nama berkas : pantai.png
Ukuran      : 1280 x 720
Ringkasan   : Berkas pantai.png

Program 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().

java UjiSuper.java
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";
    }
}
text Keluaran
Laporan Oktober berisi 42 entri

Metode 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 pemanggilan konstruktor saat objek turunan dibuatnew ArsipGambar("pantai.png", 1280, 720)super("pantai.png") dijalankanBadan konstruktorArsip (induk)Badan konstruktorArsipGambar (turunan)123Badan induk selesai terlebih dahulu, barulah badan turunan berjalan
Urutan pemanggilan konstruktor saat objek turunan dibuat

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 konstruktor Anak yang dideklarasikan sendiri. Isi datanya lewat super(...) yang dipanggil di baris pertama.
  • Mengakses field private milik induk langsung dari kelas turunan. Kompilator menolak dengan pesan x has private access in Induk meskipun field itu ikut terbawa di objeknya. Gunakan metode pengakses yang ada di induk, atau naikkan tingkat aksesnya ke protected bila memang disengaja untuk turunan.
  • Menulis super(...) di tengah badan konstruktor. Kompilator menolaknya dengan pesan call to super must be first statement in constructor. Pindahkan pemanggilannya ke baris pertama, lalu susun perhitungan turunan setelahnya.

Ringkasan#

  • extends mewariskan field dan metode induk ke kelas turunan, kecuali konstruktor yang tidak diwariskan dan anggota private yang tidak dapat disentuh turunan.
  • super.metode() dan super.field memanggil versi induk, sedangkan super(...) 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#