Pewarisan

base, virtual, override, dan sealed.

Daftar isi

Topik ini membahas pewarisan (inheritance), yaitu mekanisme yang membuat sebuah kelas mewarisi anggota kelas lain sehingga perilaku bersama cukup ditulis satu kali. Materinya mencakup kata kunci base, pasangan virtual dan override untuk menggantikan perilaku, serta sealed yang menutup rantai penggantian tersebut. Setelah topik ini diselesaikan, hierarki kelas dengan perilaku yang dapat dimodifikasi secara terkendali dapat dirancang.

Sintaks pewarisan dan kata kunci base#

Pewarisan dinyatakan dengan tanda titik dua pada deklarasi kelas, misalnya class Sepeda : Kendaraan, yang menjadikan Sepeda kelas turunan (derived class) dan Kendaraan kelas dasar (base class). Kelas turunan mewarisi field, property, dan metode yang tidak private milik kelas dasarnya, sehingga anggota tersebut dapat dipakai tanpa ditulis ulang. C# hanya mengizinkan satu kelas dasar untuk setiap kelas, tetapi rantai pewarisannya boleh memanjang beberapa tingkat. Property juga dapat dideklarasikan virtual dan digantikan dengan override, dan aturannya sama dengan metode.

Konstruktor kelas dasar tidak diwariskan, tetapi dijalankan lebih dahulu setiap kali object kelas turunan dibuat. Apabila kelas dasar tidak memiliki konstruktor tanpa parameter, kelas turunan wajib meneruskannya secara eksplisit dengan : base(...). Perantai itulah yang meneruskan data dari konstruktor turunan ke konstruktor dasarnya.

csharp Contoh: konstruktor kelas dasar dijalankan lebih dulu
var tabungan = new Tabungan("Dewi", 500000);
Console.WriteLine($"{tabungan.Pemilik}: {tabungan.SaldoAwal}");

class Rekening
{
    public string Pemilik;

    public Rekening(string pemilik)
    {
        Pemilik = pemilik;
        Console.WriteLine("Konstruktor Rekening dijalankan");
    }
}

class Tabungan : Rekening
{
    public int SaldoAwal;

    public Tabungan(string pemilik, int saldoAwal) : base(pemilik)
    {
        SaldoAwal = saldoAwal;
        Console.WriteLine("Konstruktor Tabungan dijalankan");
    }
}
text Keluaran
Konstruktor Rekening dijalankan
Konstruktor Tabungan dijalankan
Dewi: 500000

Keluaran menunjukkan urutannya dengan tegas, yaitu tubuh konstruktor Rekening selesai terlebih dahulu, kemudian barulah tubuh konstruktor Tabungan dijalankan. Pemanggilan base(pemilik) terletak pada baris deklarasi konstruktor turunannya, sehingga urutan eksekusinya terbaca langsung dari deklarasi itu. Kata kunci base juga dipakai di dalam tubuh metode untuk memanggil versi milik kelas dasar, seperti base.Deskripsi() yang muncul pada program di bagian berikutnya.

Kelas turunan mewarisi hampir seluruh anggota kelas dasarnya, tetapi anggota private tetap tidak dapat disentuh langsung dari kode turunannya meskipun benar-benar dibawa oleh objectnya. Akses melalui anggota protected atau public tetap diperlukan untuk data semacam itu, sebagaimana dibahas pada topik access modifier. Sifat pewarisan ini juga membuat satu variabel koleksi bertipe kelas dasar dapat menampung kelas dasar dan seluruh turunannya sekaligus, sebagaimana dipakai program berikutnya.

virtual dan override#

Metode yang dideklarasikan virtual di kelas dasar boleh digantikan oleh kelas turunan dengan deklarasi override. Ketika metode itu dipanggil melalui variabel bertipe kelas dasar, metode yang dijalankan tetap milik object sungguhannya, bukan milik tipe variabelnya. Mekanisme ini disebut dynamic dispatch, yakni pemilihan metode yang dilakukan saat program berjalan berdasarkan tipe object, bukan berdasarkan tipe variabel.

csharp Program.cs
// Program: pewarisan dengan virtual, override, dan pemanggilan base.
Kendaraan[] daftar = { new Kendaraan(), new Sepeda(), new Mobil() };

foreach (Kendaraan k in daftar)
{
    k.Deskripsi();
}

class Kendaraan
{
    public virtual void Deskripsi()
    {
        Console.WriteLine("Kendaraan umum");
    }
}

class Sepeda : Kendaraan
{
    public override void Deskripsi()
    {
        Console.WriteLine("Sepeda: digerakkan tenaga pengendara");
    }
}

class Mobil : Kendaraan
{
    public sealed override void Deskripsi()
    {
        base.Deskripsi();
        Console.WriteLine("Mobil: digerakkan mesin");
    }
}
text Keluaran
Kendaraan umum
Sepeda: digerakkan tenaga pengendara
Kendaraan umum
Mobil: digerakkan mesin

Variabel k bertipe Kendaraan untuk ketiga elemen, tetapi pemanggilan k.Deskripsi() menghasilkan baris yang berbeda-beda sesuai tipe objectnya. Kelas Mobil memanggil base.Deskripsi() sebelum menambahkan barisnya sendiri, sehingga keluarannya menggabungkan versi kelas dasar dengan versinya. Pola seperti ini menjadi dasar kode yang bekerja atas sekumpulan tipe berbeda tanpa memeriksa tipenya satu per satu.

Kemampuan object yang berbeda merespons panggilan yang sama dengan perilakunya masing-masing disebut polimorfisme (polymorphism, sifat banyak bentuk pada satu panggilan yang sama). Pola ini menghapus deretan pemeriksaan tipe yang rapuh, karena penambahan kelas turunan baru tidak mengubah kode yang memakai kelas dasarnya. Cukup deklarasikan kelas turunan baru dengan override-nya, dan kode pemakai langsung mengenalinya tanpa disunting.

sealed menghentikan rantai#

Kata kunci sealed menutup penggantian pada titik yang ditentukan. Apabila diletakkan pada sebuah override, seperti public sealed override void Deskripsi() di kelas Mobil, metode tersebut tidak boleh digantikan lagi oleh kelas turunan dari Mobil. Apabila diletakkan pada deklarasi kelas, misalnya sealed class Mobil, kelas itu sama sekali tidak boleh diwarisi. Penutupan ini berlaku permanen bagi semua turunan yang datang kemudian, sehingga keputusannya perlu disertai alasan yang kuat. Pustaka yang dirancang untuk diperluas umumnya menahan diri terhadap sealed agar jalur penggantian tetap terbuka bagi penggunanya.

csharp Contoh: menggantikan metode yang sudah sealed
// tidak-dijalankan
class MobilBalap : Mobil
{
    public override void Deskripsi()
    {
    }
}

Kompilator menolak deklarasi tersebut dengan error CS0239: cannot override inherited member 'Mobil.Deskripsi()' because it is sealed. Penutupan pada satu metode menyisakan anggota lain yang masih dapat digantikan, sedangkan sealed pada kelas menutup semuanya sekaligus. Penandaan sealed biasanya menyertai keputusan desain bahwa perilakunya sudah final dan tidak boleh disimpangkan oleh turunan apa pun.

Kesalahan umum#

  • Menandai override pada metode yang tidak virtual. Hanya anggota yang dideklarasikan virtual, abstract, atau override yang dapat digantikan, sehingga muncul error CS0506: cannot override inherited member because it is not marked virtual, abstract, or override. Tandai metode kelas dasar dengan virtual bila memang disediakan untuk digantikan.
  • Melupakan : base(...) ketika kelas dasar tidak punya konstruktor tanpa parameter. Konstruktor kelas dasar tetap wajib dijalankan, sehingga kompilator melaporkan parameter yang tidak tersedia. Teruskan data yang diperlukan melalui : base(...) pada deklarasi konstruktor turunannya.
  • Menggantikan metode tanpa kata kunci override. Deklarasi metode baru dengan nama sama tanpa override hanya menyembunyikan versi dasarnya, dan pemanggilan melalui variabel bertipe kelas dasar tetap menjalankan versi dasar. Gunakan override untuk penggantian yang sungguhan dan jadilah hati-hati terhadap peringatan penyembunyiannya.

Ringkasan#

  • Pewarisan dinyatakan dengan class Turunan : Dasar, dan kelas turunannya mewarisi anggota yang tidak private milik kelas dasarnya.
  • Konstruktor kelas dasar selalu dijalankan lebih dahulu, dan : base(...) meneruskan argumen yang dibutuhkannya.
  • Pasangan virtual dan override memungkinkan dynamic dispatch, sehingga metode yang dijalankan mengikuti tipe object, bukan tipe variabelnya.
  • sealed menutup penggantian pada sebuah override atau menutup kelas dari pewarisan sama sekali.

Referensi#