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.
// 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;
}
}Persegi: luasnya 16
Persegi Panjang: luasnya 15Kelas 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.
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";
}
}Pratinjau: Rekap catatan bulan iniKelas 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#
| Pertimbangan | Kelas abstrak | Interface |
|---|---|---|
| Menyimpan field dan konstruktor | Bisa | Tidak bisa |
| Jumlah yang dapat diwarisi satu kelas | Satu saja | Beberapa sekaligus |
| Memberikan implementasi parsial | Sesuai tujuannya | Melalui metode default |
| Kebutuhan utamanya | Mewariskan kerangka berbagi data | Menyatukan 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 denganerror 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 kelasnyaabstractuntuk 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
abstractyang 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#
- Interfaces — Microsoft Learn — Menjelaskan interface sebagai kontrak dan aturan implementasinya.
- interface — Microsoft Learn
— Mendokumentasikan kata kunci
interfacebeserta anggota yang boleh dideklarasikannya. - Default interface methods — Microsoft Learn — Membahas metode interface default dan pemanfaatannya untuk evolusi pustaka.