Kelas Abstrak dan Interface

Kontrak pada kelas abstrak dan interface, termasuk metode interface default.

Daftar isi

Topik ini membahas dua cara menetapkan kontrak perilaku dalam C#, yaitu kelas abstrak (abstract class, kelas yang tidak dapat dibuat objectnya dan menyediakan anggota yang wajib dilengkapi turunannya) serta interface (interface, kontrak anggota yang diimplementasikan sebuah tipe). Materinya mencakup deklarasi keduanya, aturan implementasinya, dan metode interface default. Setelah topik ini diselesaikan, pemilihan antara kelas abstrak dan interface dapat dilakukan berdasarkan kebutuhan desainnya.

Kelas abstrak sebagai kontrak parsial#

Kelas abstrak dideklarasikan dengan kata kunci abstract dan ditandai satu sifat yang menonjol, yaitu tidak dapat dibuat objectnya secara langsung. Kelas semacam ini dimaksudkan untuk diwarisi, dan anggotanya yang dideklarasikan abstract berupa tanda tangan saja tanpa tubuh, sehingga setiap kelas turunan wajib melengkapinya dengan override. Selain anggota abstrak, kelas abstrak boleh memuat field, konstruktor, dan metode biasa yang tubuhnya langsung dipakai turunannya.

csharp Program.cs
// Program: kelas abstrak Bentuk dengan dua kelas konkrit turunannya.
Bentuk[] daftar = { new Persegi(4), new PersegiPanjang(3, 5) };

foreach (Bentuk b in daftar)
{
    Console.WriteLine($"{b.Nama}: luasnya {b.Luas()}");
}

abstract class Bentuk
{
    public abstract string Nama { get; }

    public abstract int Luas();
}

class Persegi : Bentuk
{
    private int sisi;

    public Persegi(int sisi)
    {
        this.sisi = sisi;
    }

    public override string Nama => "Persegi";

    public override int Luas()
    {
        return sisi * sisi;
    }
}

class PersegiPanjang : Bentuk
{
    private int panjang;
    private int lebar;

    public PersegiPanjang(int panjang, int lebar)
    {
        this.panjang = panjang;
        this.lebar = lebar;
    }

    public override string Nama => "Persegi Panjang";

    public override int Luas()
    {
        return panjang * lebar;
    }
}
text Keluaran
Persegi: luasnya 16
Persegi Panjang: luasnya 15

Kelas Bentuk menetapkan kontraknya melalui property Nama dan metode Luas() yang keduanya abstract, sedangkan kelas konkritnya menyediakan perhitungan masing-masing. Program utama bekerja atas variabel bertipe Bentuk tanpa mengetahui tipe konkritnya, dan keluarannya mengikuti object yang ada pada setiap elemen. Apabila kelas turunan lupa melengkapi salah satu anggota abstrak, kompilator menolak kode tersebut sebelum program dijalankan.

Anggota abstrak memaksa setiap turunan menyediakan versinya, sedangkan anggota biasa di dalam kelas abstrak berperilaku seperti warisan biasa dan dapat langsung dipakai. Perpaduan keduanya menjadikan kelas abstrak tempat alami bagi kerangka yang sebagian sudah jadi dan sebagian menunggu pelengkapannya. Kelas abstrak pun boleh diwarisi kelas abstrak lain, sehingga anggota abstraknya boleh sebagian dilengkapi di tingkat menengah sebelum kelas konkritnya menyelesaikan sisanya.

Interface sebagai kontrak anggota#

Interface mendeklarasikan sekumpulan anggota yang harus disediakan oleh tipe yang mengimplementasikannya, dan deklarasi kelasnya memakai : NamaInterface. Anggota interface secara tersirat bersifat public, sehingga tipe pengimplementasinya menuliskannya tanpa modifier tambahan. Deklarasinya juga boleh memuat property, sehingga kontraknya tidak terbatas pada metode saja. Tidak seperti kelas, sebuah kelas boleh mengimplementasikan lebih dari satu interface sekaligus, dengan antarmukanya dipisahkan tanda koma.

Interface tidak menyimpan data object seperti field, sehingga kontraknya murni tentang kemampuan. Konvensi penamaannya menyertakan huruf kapital I di awal, misalnya ILaporan, dan konvensi ini dipakai di hampir seluruh library .NET. Kelas abstrak dan interface dapat dipadukan dalam satu hierarki, misalnya kelas abstrak mewarisi kelas lain sekaligus mengimplementasikan satu atau beberapa interface.

Metode interface default#

Sejak C# 8, anggota interface boleh memiliki tubuh metode, dan anggota semacam itu disebut metode interface default (default interface method, implementasi bawaan yang dipakai bila tipe pengimplementasinya tidak menyediakannya). Tipe yang mengimplementasikan interface tetap bebas tidak menuliskan anggota tersebut, karena implementasi bawaan mengisinya. Metode ini hanya dapat dipanggil melalui variabel bertipe interface-nya, bukan melalui variabel bertipe kelasnya.

csharp Contoh: metode interface default dipakai tanpa diimplementasikan
ILaporan laporan = new LaporanBulanan();
laporan.Pratinjau();

interface ILaporan
{
    string Isi();

    void Pratinjau()
    {
        Console.WriteLine($"Pratinjau: {Isi()}");
    }
}

class LaporanBulanan : ILaporan
{
    public string Isi()
    {
        return "Rekap catatan bulan ini";
    }
}
text Keluaran
Pratinjau: Rekap catatan bulan ini

Kelas LaporanBulanan hanya mengimplementasikan Isi(), sedangkan Pratinjau() diambil dari implementasi bawaan ILaporan dan memanfaatkan Isi() milik objectnya. Keluarannya tetap lengkap meskipun kelasnya tidak menuliskan Pratinjau() sama sekali. Manfaat terbesarnya ada pada pustaka yang dipakai banyak pihak, karena anggota baru dapat ditambahkan ke interface yang sudah ada tanpa memecah setiap kelas pengimplementasinya.

Memilih antara kelas abstrak dan interface#

PertimbanganKelas abstrakInterface
Menyimpan field dan konstruktorBisaTidak bisa
Jumlah yang dapat diwarisi satu kelasSatu sajaBeberapa sekaligus
Memberikan implementasi parsialSesuai tujuannyaMelalui metode default
Kebutuhan utamanyaMewariskan kerangka berbagi dataMenyatukan kemampuan antartipe yang tak bertalian

Kelas abstrak dipilih ketika sekelompok tipe berbagi data dan sebagian perilaku yang sama, seperti Bentuk yang berbagi konsep luas. Interface dipilih ketika yang disatukan hanyalah kemampuan, misalnya tipe yang tak bertalian pun dapat sama-sama mengimplementasikan ILaporan. Keduanya bukan saling menggantikan, dan desain yang matang sering memakai keduanya sekaligus pada satu hierarki.

Kesalahan umum#

  • Membuat object dari kelas abstrak. Deklarasi new Bentuk() ditolak dengan error CS0144: Cannot create an instance of the abstract type or interface. Buat object dari kelas konkrit turunannya, lalu simpan reference-nya pada variabel bertipe abstrak.
  • Melupakan satu anggota interface. Kelas yang mengimplementasikan interface wajib menyediakan semua anggotanya, dan kekurangannya dilaporkan sebagai error CS0535: does not implement interface member. Lengkapi anggota yang tertinggal, atau deklarasikan kelasnya abstract untuk menunda pelengkapannya kepada kelas turunannya.
  • Memanggil metode default melalui tipe kelas. Implementasi bawaan hanya dapat diakses melalui tipe interface, sehingga pemanggilan dari variabel bertipe kelas tidak menemukan anggota tersebut. Deklarasikan variabelnya dengan tipe interface, misalnya ILaporan laporan = new LaporanBulanan();, agar metode default terlihat.

Ringkasan#

  • Kelas abstrak tidak dapat dibuat objectnya, menetapkan anggota abstract yang wajib dilengkapi turunannya, dan boleh memuat field serta metode biasa.
  • Interface mendeklarasikan kontrak anggota publik, dapat diimplementasikan lebih dari satu oleh satu kelas, dan tidak menyimpan data object.
  • Metode interface default menyediakan implementasi bawaan sejak C# 8, tetapi hanya dapat dipanggil melalui tipe interface-nya.
  • Kelas abstrak mengikat tipe yang berbagi kerangka dan data, sedangkan interface menyatukan kemampuan antartipe yang tidak harus bertalian.

Referensi#