Overriding Metode

Mengganti perilaku metode induk dengan @Override, memahami kovariansi, dan membedakan hiding dari overriding.

Daftar isi

Overriding adalah mengganti implementasi metode induk di kelas turunan dengan tanda tangan (signature) yang sama, sehingga panggilan yang sama dijawab dengan perilaku turunannya sendiri. Topik ini mempraktikkan aturan overriding beserta anotasi @Override, tipe kembalian kovarian (covariant return type), dan perbedaan hiding pada metode static dari overriding yang sesungguhnya. Ketiganya menentukan metode mana yang benar-benar berjalan ketika program dieksekusi.

Syarat overriding dan pola dasarnya#

Metode turunan mengoverride metode induk bila namanya sama, daftar parameternya sama, dan tipe kembaliannya sama atau lebih spesifik. Tingkat aksesnya tidak boleh menyempit, misalnya dari public menjadi private, dan exception yang diperiksa (checked exception) tidak boleh diperlebar. Bila semua syarat terpenuhi, setiap pemanggilan lewat objek turunan menjalankan versi turunan, meskipun pemanggilnya menulis kode terhadap tipe induk.

java DemoOverriding.java
public class DemoOverriding {
    public static void main(String[] args) {
        Penilaian dasar = new Penilaian(70, 90);
        PenilaianPraktik praktik = new PenilaianPraktik(70, 90, 80);

        System.out.println("Dasar   : " + dasar.rincian());
        System.out.println("Praktik : " + praktik.rincian());
    }
}

class Penilaian {
    double tulis;
    double unggah;

    Penilaian(double tulis, double unggah) {
        this.tulis = tulis;
        this.unggah = unggah;
    }

    double nilaiAkhir() {
        return tulis * 0.5 + unggah * 0.5;
    }

    String rincian() {
        return "nilai akhir " + nilaiAkhir();
    }
}

class PenilaianPraktik extends Penilaian {
    double demonstrasi;

    PenilaianPraktik(double tulis, double unggah, double demonstrasi) {
        super(tulis, unggah);
        this.demonstrasi = demonstrasi;
    }

    @Override
    double nilaiAkhir() {
        return tulis * 0.5 + unggah * 0.25 + demonstrasi * 0.25;
    }
}
text Keluaran
Dasar   : nilai akhir 80.0
Praktik : nilai akhir 77.5

Kelas PenilaianPraktik tidak menyentuh rincian() sama sekali, tetapi hasilnya berbeda karena rincian() di induk memanggil nilaiAkhir(), dan yang berjalan adalah versi PenilaianPraktik. Pola ini terasa di banyak kode nyata, yaitu metode umum di induk memanggil langkah yang dapat diganti oleh turunan. Metode induk yang tidak boleh diganti ditandai final, dan kompiler akan menolak overriding atasnya.

@Override sebagai penjaga tanda tangan#

Anotasi @Override bersifat opsional, tetapi memasangnya pada setiap metode yang memang dimaksudkan mengganti induk adalah kebiasaan yang murah dan menolong. Anotasi itu meminta kompiler memeriksa bahwa tanda tangan metodenya benar-benar cocok dengan metode induk. Bila cocok, kompiler diam. Bila tidak, misalnya karena salah ketik nama atau parameter yang berbeda, kompiler menolak berkasnya dengan pesan method does not override or implement a method from a supertype.

java Contoh: salah ketik tanda tangan tertangkap oleh @Override
// tidak-dijalankan
@Override
double nilaiAkhir(double bobot) { // parameter berbeda, bukan overriding
    return 0;
}

Tanpa @Override, potongan di atas tetap dikompilasi, tetapi maknanya berubah menjadi overloading, yaitu metode baru bernama sama dengan daftar parameter berbeda. Metode induk tidak pernah terganti, dan kesalahan itu baru terasa ketika perilaku lama terus muncul di tempat yang seharusnya sudah berganti. Dengan @Override, kesalahan yang sama berhenti di layar kompilasi sebelum sempat berjalan.

Tipe kembalian kovarian#

Sejak Java 5, tipe kembalian metode overriding boleh menjadi turunan dari tipe kembalian metode induknya, aturan yang disebut kovariansi. Metode induk boleh menjanjikan hasil bertipe umum, sedangkan turunan menajamkan janjinya ke tipe yang lebih spesifik tanpa mengubah tanda tangannya. Aturan ini hanya berlaku untuk tipe referensi, karena tipe primitif tidak punya hierarki turunan sehingga tipe kembaliannya wajib sama persis.

java UjiKovariansi.java
public class UjiKovariansi {
    public static void main(String[] args) {
        Rencana dasar = new Rencana(7);
        RencanaMingguan mingguan = new RencanaMingguan(7);
        RencanaMingguan hasil = mingguan.perbarui(3);

        System.out.println(dasar.deskripsi());
        System.out.println(hasil.deskripsi());
    }
}

class Rencana {
    int hari;

    Rencana(int hari) {
        this.hari = hari;
    }

    String deskripsi() {
        return "rencana selama " + hari + " hari";
    }

    Rencana perbarui(int tambahan) {
        return new Rencana(hari + tambahan);
    }
}

class RencanaMingguan extends Rencana {
    RencanaMingguan(int hari) {
        super(hari);
    }

    @Override
    String deskripsi() {
        return "rencana mingguan selama " + hari + " hari";
    }

    @Override
    RencanaMingguan perbarui(int tambahan) {
        return new RencanaMingguan(hari + tambahan);
    }
}
text Keluaran
rencana selama 7 hari
rencana mingguan selama 10 hari

Induk Rencana menjanjikan hasil bertipe Rencana, sedangkan RencanaMingguan menajamkannya menjadi RencanaMingguan. Berkat kovariansi, hasil mingguan.perbarui(3) dapat disimpan langsung ke variabel RencanaMingguan tanpa casting, dan anggota khas turunan tetap terjangkau dari pemanggil. Bila tipe kembaliannya dipaksa sama persis, pemanggil yang membutuhkan tipe spesifik harus menulis cast sendiri setiap kali.

Hiding pada metode static#

Metode static tidak mengikuti aturan overriding. Pemilihannya dilakukan kompiler berdasarkan tipe referensi saat kompilasi, bukan tipe objek saat program berjalan. Mendefinisikan metode static bernama sama di turunan karenanya disebut hiding, yaitu menyembunyikan versi induk dari sudut pandang nama turunan, bukan menggantinya secara perilaku.

java DemoHiding.java
public class DemoHiding {
    public static void main(String[] args) {
        Pengaturan.cetakSumber();
        PengaturanLanjut.cetakSumber();
    }
}

class Pengaturan {
    static String sumber() {
        return "berkas konfigurasi utama";
    }

    static void cetakSumber() {
        System.out.println("Sumber: " + sumber());
    }
}

class PengaturanLanjut extends Pengaturan {
    static String sumber() {
        return "berkas konfigurasi lokal";
    }
}
text Keluaran
Sumber: berkas konfigurasi utama
Sumber: berkas konfigurasi utama

Pemanggilan kedua tetap mencetak versi induk, dan inilah bukti bedanya dengan overriding. Metode cetakSumber() diwarisi PengaturanLanjut tanpa ditulis ulang, sehingga isi badannya tetap memanggil Pengaturan.sumber() yang terikat saat kompilasi. Versi sumber() milik PengaturanLanjut hanya berlaku ketika dipanggil lewat nama PengaturanLanjut atau referensi bertipe itu. Perilaku yang mengikuti objek, bukan tipe referensinya, sebaiknya dinyatakan sebagai metode instance dan dioverride secara sah.

AspekOverriding metode instanceHiding metode static
Metode dipilih berdasarkantipe objek saat program berjalantipe referensi saat kompilasi
Panggilan lewat referensi indukversi turunan berjalanversi induk berjalan
Wajar dipakai untukperilaku milik objekutilitas kelas yang tidak memakai objek

Kesalahan umum#

  • Mengubah daftar parameter ketika maksudnya mengganti metode. Hasilnya metode baru (overloading) yang hidup di samping versi induk, sementara pemanggil lama tetap memakai versi induk. Pasang @Override agar selisih tanda tangan tertangkap kompiler.
  • Mengira metode static ikut dioverride. Yang terjadi adalah hiding, sehingga panggilan lewat referensi induk tetap menjalankan versi induk. Bila perilakunya milik objek, jadikan metode instance.
  • Menyempitkan tingkat akses saat mengoverride. Kompiler menolak dengan pesan attempting to assign weaker access privileges; was public. Pertahankan tingkat akses yang sama atau lebarkan bila memang diperlukan.

Ringkasan#

  • Overriding mengganti implementasi metode induk dengan tanda tangan sama, tingkat akses sama atau lebih luas, dan tipe kembalian sama atau kovarian.
  • Anotasi @Override meminta kompiler memeriksa kecocokan tanda tangan, sehingga salah ketik berhenti sebelum program berjalan.
  • Metode static tidak dioverride melainkan dihiding, dan pemilihannya mengikuti tipe referensi pada saat kompilasi.

Referensi#