Abstract class adalah class yang ada hanya untuk diturunkan. Class ini tidak bisa dibuat instance-nya, dan bisa mendeklarasikan member abstract: method dan property tanpa body yang wajib diimplementasikan oleh setiap class turunan yang konkret. Selebihnya, abstract class sama seperti class biasa: bisa punya field, constructor, dan method lengkap yang diwarisi oleh class turunannya.
Mendeklarasikan abstract class
Tandai class dengan abstract, lalu tandai juga setiap member yang wajib disediakan class turunan dengan abstract, dan akhiri dengan titik koma sebagai pengganti body:
Output:
Circle: area 7.07, perimeter 9.42
Rectangle: area 10.00, perimeter 13.00
Describe ditulis sekali saja, di class dasar, dan memanggil Area serta Perimeter, yang sama sekali tidak ada di Shape. Itulah gunanya member abstract: class dasar bisa mengandalkannya, dan compiler menjamin setiap bentuk konkret menyediakannya. Class turunan yang lupa mengimplementasikan salah satunya tidak akan bisa di-compile:
class Triangle : Shape
{
public Triangle() : base("Triangle") { }
public override double Area() => 6;
}
// error CS0534: 'Triangle' does not implement inherited abstract member 'Shape.Perimeter()'
Solusinya adalah mengimplementasikan Perimeter, atau menandai Triangle sendiri sebagai abstract kalau class itu menjadi base perantara untuk class lain.
Aturan member abstract
- Member abstract tidak punya body dan otomatis bersifat virtual: class turunan mengimplementasikannya dengan
override. - Hanya abstract class yang boleh berisi member abstract. Menaruhnya di class biasa menghasilkan error saat compile.
- Member abstract tidak boleh
private(tidak ada yang bisa meng-override-nya) ataustaticdi dalam class. - Method, property, indexer, dan event bisa dibuat abstract. Field dan constructor tidak bisa.
- Abstract class tidak wajib berisi member abstract. Menandai class dengan
abstractsaja sudah mencegah pembuatan instance, berguna untuk class dasar yang hanya masuk akal lewat subclass-nya.
Property abstract menentukan accessor mana yang wajib disediakan class turunan:
abstract class Plan
{
public abstract decimal MonthlyPrice { get; }
public abstract string Name { get; }
}
class ProPlan : Plan
{
public override decimal MonthlyPrice => 12.99m;
public override string Name => "Pro";
}
Constructor di abstract class
Abstract class bisa punya constructor. Constructor ini tidak bisa dipanggil dengan new, tetapi setiap constructor turunan menjalankannya lewat : base(...), jadi di sinilah state bersama diinisialisasi dan divalidasi. Deklarasikan sebagai protected, yang artinya persis itu: hanya class turunan yang bisa memakainya.
Pola template method
Alasan paling umum menulis abstract class adalah untuk menetapkan urutan sebuah algoritma di class dasar, lalu membiarkan class turunan mengisi langkah-langkahnya satu per satu. Method dasar yang menjalankan langkah-langkah itu tidak virtual, jadi tidak ada subclass yang bisa mengubah urutan atau melewati langkahnya.
Output:
item,qty,price
Coffee beans,2,11.50
Filter papers,1,3.20
| Item | Qty | Price |
| --- | --- | --- |
| Coffee beans | 2 | 11.50 |
| Filter papers | 1 | 3.20 |
Total: 26.20
Setiap baris adalah tuple bernama, dan output-nya disusun dengan StringBuilder. Perhatikan campurannya: Header dan Row dibuat abstract karena setiap format harus menentukannya sendiri, sedangkan Footer dibuat virtual dengan default kosong karena kebanyakan format tidak butuh apa-apa di sana. Langkah-langkahnya protected, jadi kode dari luar hanya bisa memanggil Export. Class bawaan framework memakai ide yang sama: Stream dan TextWriter adalah abstract class dasar yang banyak method praktisnya dibangun di atas beberapa member yang disediakan class turunan (Read dan Write untuk stream, Write(char) untuk text writer).
Abstract class vs interface
Keduanya memungkinkan kamu menulis kode untuk sebuah tipe tanpa tahu class konkretnya. Bedanya ada pada apa yang bisa dimuat dan berapa banyak yang bisa dimiliki sebuah class:
| Abstract class | Interface | |
|---|---|---|
| Jumlah yang bisa diwarisi atau diimplementasikan sebuah class | satu | banyak |
| Field instance (state) | ya | tidak |
| Constructor | ya | tidak |
| Method dengan body | ya | ya, sejak C# 8 (default interface method) |
| Member static | ya | ya, sejak C# 8; member static abstract sejak C# 11 |
| Access modifier pada member | apa saja | public secara default; yang lain boleh sejak C# 8 |
| Bisa dipakai struct | tidak | ya |
| Menambah member belakangan | aman kalau tidak abstract | merusak class yang mengimplementasikan, kecuali punya body default |
Default interface method (C# 8) memperkecil jaraknya, tetapi perbedaan intinya tetap ada:
public interface ILogger
{
void Write(string message);
void Error(string message) => Write("ERROR: " + message); // default implementation: C# 8, .NET Core 3.0+
}
Method default tetap tidak bisa menyentuh field instance, karena interface tidak punya field instance; method itu hanya bisa memanggil member lain dari interface tersebut.
Cara praktis untuk memilih:
- Interface saat kamu menggambarkan kemampuan yang bisa dimiliki tipe-tipe yang tidak saling berhubungan (
IComparable<T>,IDisposable,IShippingProvider), saat struct juga harus bisa ikut, atau saat sebuah class butuh beberapa peran seperti itu. Kebanyakan kode dependency injection ditulis dengan interface karena alasan ini. - Abstract class saat tipe-tipe turunannya memang variasi dari satu hal yang sama dan berbagi state atau algoritma yang tetap, seperti exporter di atas.
- Keduanya sering dipakai bersamaan: interface sebagai acuan bagi pemanggil, dan abstract class dasar yang mengimplementasikannya supaya class yang mengimplementasikan tidak perlu menulis ulang bagian yang sama. Halaman interface membahas sisi interface secara rinci.
Kesalahan umum
- Mencoba membuat instance dari class dasar.
new Shape(...)menghasilkan CS0144. Buat instance dari class turunan. - Lupa
overridepada implementasinya. Menulispublic double Area()di class turunan tidak mengimplementasikan member abstract; compiler melaporkan CS0534 karena implementasinya tidak ada, ditambah warning bahwa method baru itu menyembunyikan method warisan. - Constructor public di abstract class. Tidak berbahaya, tetapi menyesatkan. Pakai
protected. - Abstract class tanpa kode bersama. Kalau class dasar hanya berisi member abstract dan tidak punya state, interface menyatakan hal yang sama dan tidak menghabiskan satu-satunya slot class dasar.
- Memanggil member abstract dari constructor dasar. Implementasi turunan berjalan sebelum body constructor turunan, jadi field yang di-assign oleh body itu masih bernilai
0ataunull.
Pertanyaan yang sering diajukan
Apa itu abstract class di C#?
Class yang ditandai abstract hanya dimaksudkan sebagai class dasar: kamu tidak bisa membuat instance-nya dengan new (error CS0144). Class ini boleh berisi field, constructor, dan method biasa, ditambah member abstract yang tidak punya body dan wajib diimplementasikan oleh setiap class turunan yang tidak abstract.
Apa bedanya abstract class dan interface di C#?
Sebuah class hanya bisa mewarisi satu abstract class, tetapi bisa mengimplementasikan banyak interface. Abstract class bisa menyimpan state (field instance), punya constructor dan member dengan tingkat akses apa pun, sehingga cocok menjadi tempat implementasi bersama. Interface menggambarkan sebuah kemampuan tanpa state instance; sejak C# 8 interface boleh punya body method bawaan, tetapi tetap tidak bisa punya field instance atau constructor.
Apa bedanya method abstract dan virtual di C#?
Method virtual punya body yang boleh di-override oleh class turunan. Method abstract tidak punya body dan class turunan wajib meng-override-nya, kalau tidak class turunan itu sendiri menjadi abstract (error CS0534 untuk class konkret). Method abstract hanya boleh ada di abstract class.
Apakah abstract class bisa punya constructor di C#?
Bisa. Constructor itu berjalan saat class turunan dibuat, lewat : base(...), dan biasanya menginisialisasi field bersama. Deklarasikan sebagai protected: constructor public di abstract class memberi kesan bahwa pemanggil bisa memakainya, padahal tidak bisa.
Kenapa saya tidak bisa membuat instance dari abstract class?
Karena abstract class bisa punya member abstract tanpa implementasi: memanggil new Shape().Area() tidak akan punya kode untuk dijalankan. Compiler melaporkan CS0144 ("Cannot create an instance of the abstract type or interface"). Buat instance dari class turunan yang konkret, lalu simpan di variabel bertipe abstract kalau mau: Shape s = new Circle(2);.