Setiap jenis pegawai di sebuah perusahaan menghitung gaji dengan rumus berbeda: manajer menerima gaji dasar plus tunjangan, sedangkan staf menerima gaji dasar plus upah lembur. Base class Pegawai tidak memiliki rumus yang berlaku untuk semua jenis, dan memberi method kosong di base class berbahaya karena turunan boleh lupa mengisinya. Abstract member menyelesaikan masalah ini dengan mendeklarasikan member tanpa badan, lalu compiler memaksa setiap turunan mengisi implementasinya.

Abstract Class dan Abstract Member #

Member yang ditandai abstract hanya berupa deklarasi: tidak ada badan method, dan baris deklarasi diakhiri titik koma. Class yang memuat abstract member otomatis wajib ditandai abstract juga, dan class abstract tersebut tidak dapat dibuat object-nya. Peran abstract class adalah menjadi cetakan: base class menyimpan data dan kontrak, sedangkan setiap derived class mengisi kontrak tersebut.

Contoh: Perhitungan Gaji #

csharp
public abstract class Pegawai
{
    public string Nama;

    public Pegawai(string nama)
    {
        Nama = nama;
    }

    public abstract int HitungGaji();
}

public class Manajer : Pegawai
{
    public int Tunjangan;

    public Manajer(string nama, int tunjangan) : base(nama)
    {
        Tunjangan = tunjangan;
    }

    public override int HitungGaji()
    {
        return 8000000 + Tunjangan;
    }
}

public class Staf : Pegawai
{
    public int JamLembur;

    public Staf(string nama, int jamLembur) : base(nama)
    {
        JamLembur = jamLembur;
    }

    public override int HitungGaji()
    {
        return 4000000 + JamLembur * 25000;
    }
}

Pegawai pegawaiA = new Manajer("Rina", 2000000);
Pegawai pegawaiB = new Staf("Dedi", 8);

Console.WriteLine($"{pegawaiA.Nama}: Rp{pegawaiA.HitungGaji()}");
Console.WriteLine($"{pegawaiB.Nama}: Rp{pegawaiB.HitungGaji()}");

Output yang Dihasilkan:

text
Rina: Rp10000000
Dedi: Rp4200000

Mari kita bedah baris demi baris:

  1. public abstract int HitungGaji();: Deklarasi ini tidak punya badan; base class hanya menjanjikan bahwa setiap pegawai punya cara menghitung gaji.
  2. public override int HitungGaji() pada Manajer: Derived class pertama mengisi kontrak dengan rumus 8000000 + Tunjangan.
  3. public override int HitungGaji() pada Staf: Kontrak yang sama diisi rumus lembur yang berbeda, membuktikan satu kontrak dapat punya banyak implementasi.
  4. Pegawai pegawaiA = new Manajer(...): Variable bertipe abstract class menampung object turunannya; pembuatan object terjadi pada Manajer, bukan pada Pegawai.
  5. pegawaiA.HitungGaji() dan pegawaiB.HitungGaji(): Pemanggilan yang sama menjalankan rumus berbeda sesuai tipe object masing-masing, menghasilkan dua angka yang berbeda pada output.

Aturan Abstract Member #

Empat aturan berikut mengikat penggunaan abstract member:

  • Deklarasi abstract tidak punya badan dan ditutup titik koma, termasuk pada property dan method.
  • Class yang memuat satu abstract member saja pun wajib ditandai abstract.
  • Turunan non-abstract wajib meng-override seluruh abstract member; jika tidak, program gagal dikompilasi.
  • Abstract class tidak dapat di-instansiasi, sehingga new Pegawai() selalu ditolak compiler.
text
error CS0534: 'Manajer' does not implement inherited abstract member 'Pegawai.HitungGaji()'

Percobaan lain, yaitu membuat object dari abstract class itu sendiri, menghasilkan error CS0144: Cannot create an instance of the abstract type or interface 'Pegawai'. Kedua pesan ini menegaskan peran compiler sebagai penjaga kontrak.

Abstract class sering berdampingan dengan interface. Bab berikutnya membahas sealed, mekanisme kebalikannya: menutup class dan member agar tidak diwarisi maupun diganti lagi.