Abstract Class
Class yang tidak bisa diinstansiasi: kontrak untuk child class.
Kontrak yang wajib dipenuhi
Inheritance mewarisi implementasi. Tapi kadang kamu hanya ingin menetapkan aturan: "setiap hewan WAJIB bisa bersuara, caranya terserah". Abstract class adalah class yang tidak bisa dibuat objeknya, berfungsi sebagai kontrak yang wajib dipenuhi anak-anaknya.
Analogi: abstract class itu seperti formulir kontrak kerja. Formulirnya sendiri tidak bisa "dipekerjakan", tapi setiap karyawan (class konkret) yang menandatanganinya wajib memenuhi klausul-klausulnya.
Kenapa abstract class ada?
Untuk memaksa konsistensi desain. Tanpa kontrak, satu programmer membuat method bunyi(), yang lain suara(), dan kode polimorfisme berantakan. Dengan abstract class, compiler MENJAMIN setiap anak punya method yang sama.
Bentuk dasar
abstract class Hewan {
String nama;
Hewan(this.nama);
void makan() => print('$nama makan.'); // method konkret: boleh ada
// Method abstrak: tanpa badan, WAJIB di-override anak
void bersuara();
}
// class HewanLangsung extends Hewan {} // ERROR jika tidak override bersuara()
class Kucing extends Hewan {
Kucing(String nama) : super(nama);
@override
void bersuara() => print('$nama: Meong!');
}
void main() {
// var h = Hewan('x'); // ERROR: abstract tidak bisa diinstansiasi
Hewan k = Kucing('Kitty'); // boleh: tipe induk, objek anak
k.makan();
k.bersuara();
}Dua jenis anggota:
- Method konkret (
makan()): punya implementasi, diwarisi apa adanya. - Method abstrak (
bersuara();): tanpa badan, anak WAJIB mengimplementasikannya atau ikut menjadi abstract.
Contoh kedua, abstract sebagai kerangka algoritma (template method):
abstract class Pembayaran {
// Kerangka tetap: langkah-langkahnya dikunci
void proses(double jumlah) {
validasi(jumlah);
potongSaldo(jumlah);
kirimNotifikasi();
}
void validasi(double jumlah) {
if (jumlah <= 0) throw ArgumentError('Jumlah harus positif');
}
// Langkah yang disesuaikan tiap metode bayar
void potongSaldo(double jumlah);
void kirimNotifikasi();
}
class BayarQRIS extends Pembayaran {
@override
void potongSaldo(double jumlah) => print('Potong saldo QRIS: $jumlah');
@override
void kirimNotifikasi() => print('Notif: QRIS berhasil');
}
void main() {
Pembayaran p = BayarQRIS();
p.proses(50000);
}Induk mengunci alur, anak mengisi detail. Pola ini menjaga konsistensi proses bisnis.
Abstract vs interface (implements)
Dart tidak punya kata kunci interface terpisah: setiap class BISA menjadi interface lewat implements. Bedanya:
// extends: warisi implementasi makan(), penuhi yang abstrak // implements: TIDAK mewarisi kode, tulis ulang SEMUA (lihat contoh di bawah)
abstract class BisaTerbang {
void terbang(); // murni kontrak, tanpa implementasi
}
class Burung implements BisaTerbang {
@override
void terbang() => print('Mengepakkan sayap');
}
class Pesawat implements BisaTerbang {
@override
void terbang() => print('Mesin menyala');
}extends: mewarisi kode + wajib penuhi yang abstrak (hubungan "adalah").implements: TIDAK mewarisi kode sama sekali, wajib tulis ulang SEMUA method (hubungan "bisa").
Kesalahan umum
1. Membuat objek dari abstract class
// SALAH
abstract class Hewan {
void bersuara();
}
void main() {
// var h = Hewan(); // ERROR: abstract class tidak bisa diinstansiasi
}2. Anak tidak mengimplementasikan method abstrak
// SALAH: Kucing tidak override bersuara()
// class Kucing extends Hewan {
// Kucing(String nama) : super(nama);
// } // ERROR: wajib implementasi atau jadikan abstract// BENAR
class Kucing extends Hewan {
Kucing(String nama) : super(nama);
@override
void bersuara() => print('Meong');
}3. implements tapi berharap mewarisi kode
abstract class Hewan {
void makan() => print('makan');
void bersuara();
}
// SALAH: implements tidak mewarisi makan()
// class Ikan implements Hewan {
// @override
// void bersuara() => print('blub');
// } // ERROR: makan() belum diimplementasikan// BENAR
class Ikan implements Hewan {
@override
void makan() => print('Ikan makan plankton');
@override
void bersuara() => print('blub');
}Kesimpulan
Abstract class = kontrak + boleh ada implementasi bersama. Tidak bisa diinstansiasi, anak wajib memenuhi method abstrak. extends untuk "adalah" dengan warisan kode, implements untuk "bisa" sebagai kontrak murni.
Catatan teknis: Method abstrak juga bisa berupa getter/setter abstrak. Dan class yang memakai
implementsterhadap class biasa tetap wajib mengimplementasikan SEMUA method publiknya, termasuk yang konkret.
Tantangan
Kontrak kendaraan
Buat abstract class Kendaraan dengan method abstract jalan() dan method konkret klakson(). Buat child Motor dan Mobil yang implement jalan().
// TODO
void main() {
// TODO: buat Motor & Mobil, panggil jalan() dan klakson()
}