Pewarisan (inheritance) membangun kelas baru di atas kelas yang sudah ada, sedangkan kelas abstract mendefinisikan kontrak yang wajib dipenuhi kelas turunannya. Topik ini membahas pewarisan dengan extends, penggantian perilaku dengan @override beserta panggilan super, dan kelas abstract dengan metode tanpa isi, ditutup dengan error analyzer yang muncul apabila anggota hanya ada pada subkelas. Setiap contoh berupa berkas utuh yang dijalankan dengan Dart SDK 3.13 dan disertai keluaran aslinya.
Pewarisan dengan extends#
Kata kunci extends mendeklarasikan kelas turunan (subclass) dari sebuah kelas induk (superclass). Kelas turunan mewarisi seluruh anggota kelas induknya, baik bidang maupun metode, dan tetap boleh menambahkan anggota baru miliknya sendiri. Dart mengizinkan satu kelas induk langsung untuk setiap kelas, sehingga rantai pewarisan selalu berbentuk garis tunggal dari kelas paling atas.
class Hewan {
void makan() {
print('Hewan makan');
}
}
class Burung extends Hewan {
void terbang() {
print('Burung terbang');
}
}
void main() {
final hewan = Hewan();
hewan.makan();
final burung = Burung();
burung.makan();
burung.terbang();
}Hewan makan
Hewan makan
Burung terbangObjek Burung menjawab panggilan makan() karena metode tersebut diwarisi dari Hewan, sementara terbang() hanya tersedia pada Burung. Konstruktor subkelas secara implisit memanggil konstruktor kelas induk tanpa argumen sebelum badannya sendiri berjalan, dan panggilan eksplisit super(...) pada initializer list meneruskan argumen apabila kelas induk memiliki konstruktor berparameter. Rantai pembentukan ini berlaku untuk setiap tingkat pewarisan, dari kelas paling atas hingga kelas yang dibuat.
Konstruktor tidak diwariskan bersama anggota lain, sehingga kelas turunan selalu mendeklarasikan konstruktornya sendiri dan mengarahkan pembentukan ke kelas induk melalui super. Anggota yang ditandai statis juga tidak mengikuti pewarisan, karena anggota semacam itu melekat pada kelasnya dan hanya dijangkau melalui nama kelas yang mendeklarasikannya. Sementara itu, anggota instance biasa diwariskan utuh dan dapat dipanggil dari objek subkelas tanpa penulisan ulang apa pun.
Mengganti perilaku dengan @override#
Anotasi @override menandai anggota yang menggantikan anggota kelas induknya. Penggantian dilakukan dengan mendeklarasikan ulang anggota bernama sama beserta tanda tangan (signature) yang cocok, dan badan baru menggantikan badan warisan pada setiap panggilan melalui objek subkelas. Di dalam badan penggantinya, super.namaMetode() tetap menjalankan versi milik kelas induk.
class Hewan {
void perkenalan() {
print('Saya hewan umum');
}
}
class Burung extends Hewan {
final String nama;
Burung(this.nama);
@override
void perkenalan() {
super.perkenalan();
print('Saya $nama, saya bisa terbang');
}
}
void main() {
final burung = Burung('Cici');
burung.perkenalan();
}Saya hewan umum
Saya Cici, saya bisa terbangKeluaran menunjukkan kedua baris, karena super.perkenalan() menjalankan versi Hewan terlebih dahulu sebelum baris tambahan milik Burung. Anotasi @override sendiri tidak mengubah perilaku program, tetapi meminta analyzer memeriksa apakah anggota tersebut benar-benar menggantikan anggota induk. Apabila nama salah tulis atau tanda tangannya berbeda, analyzer menampilkan peringatan bahwa anggota tersebut tidak menggantikan apa pun, sehingga kekeliruan semacam itu, yang tanpa anotasi akan berlalu diam-diam sebagai anggota baru, dapat tertangkap sejak awal.
Anggota yang dapat diganti tidak terbatas pada metode, karena getter, setter, dan bidang instance juga dapat dideklarasikan ulang dengan anotasi yang sama. Konstruktor tidak termasuk di dalamnya karena memang tidak diwariskan sejak awal. Tanda tangan penggantian harus cocok dengan yang ada di kelas induk, dan perbedaan tipe kembalian yang menyimpang ditolak analyzer.
Kelas abstract dan kontraknya#
Kelas abstract (abstract class) adalah kelas yang tidak dapat dibuat menjadi objek dan boleh memuat metode tanpa isi. Metode tanpa isi itu berperan sebagai kontrak, karena setiap kelas turunan wajib menyediakan implementasinya dengan tanda tangan yang sama. Kelas abstract juga tetap boleh memuat anggota yang sudah berisi implementasi, sehingga kontrak dan perilaku bawaan dapat digabung dalam satu kelas.
import 'dart:math';
abstract class BangunDatar {
double luas();
double keliling();
}
class PersegiPanjang extends BangunDatar {
final double panjang;
final double lebar;
PersegiPanjang(this.panjang, this.lebar);
@override
double luas() => panjang * lebar;
@override
double keliling() => 2 * (panjang + lebar);
}
class Lingkaran extends BangunDatar {
final double jariJari;
Lingkaran(this.jariJari);
@override
double luas() => pi * jariJari * jariJari;
@override
double keliling() => 2 * pi * jariJari;
}
void main() {
final daftarBangun = <BangunDatar>[
PersegiPanjang(10, 4),
Lingkaran(7),
];
for (final bangun in daftarBangun) {
print('luas: ${bangun.luas().toStringAsFixed(2)}');
}
}luas: 40.00
luas: 153.94Daftar daftarBangun bertipe List<BangunDatar>, sehingga PersegiPanjang dan Lingkaran dapat ditampung bersama di dalamnya. Pemanggilan bangun.luas() pada setiap elemen menjalankan implementasi milik kelas aslinya, dan perilaku ini disebut polimorfisme (polymorphism). Kontrak dari BangunDatar menjamin setiap anggota daftar memiliki luas() dan keliling(), tanpa perlu mengetahui kelas konkretnya. Percobaan membuat objek langsung dengan BangunDatar() ditolak analyzer dengan error bahwa kelas abstract tidak dapat diinstansiasi.
Kontrak kelas abstract menjamin setiap subkelas konkret menyediakan implementasi penuh. Apabila satu pun metode tanpa isi belum diimplementasikan, analyzer menolak subkelas tersebut dan menyarankan agar kelasnya dijadikan abstract. Kebijakan ini mencegah objek setengah jadi, karena kelas yang belum memenuhi kontraknya tidak dapat diinstansiasi sama sekali.
Kesalahan umum: anggota subkelas lewat tipe induk#
Referensi yang bertipe kelas induk hanya mengekspos anggota yang dideklarasikan oleh kelas induk, meskipun objek di baliknya adalah subkelas dengan anggota tambahan. Analyzer menilai kode berdasarkan tipe statis (static type) referensinya, sehingga pemanggilan anggota milik subkelas melalui referensi induk ditolak pada waktu kompilasi. Blok berikut menuliskan berkas bermasalah ke folder sementara, lalu menjalankan dart analyze atas berkas itu.
rm -rf /tmp/uji/pewarisan
mkdir -p /tmp/uji/pewarisan
cat > /tmp/uji/pewarisan/anggota_subkelas.dart <<'EOF'
class Hewan {
void makan() {}
}
class Burung extends Hewan {
void terbang() {}
}
void main() {
final hewan = Hewan();
hewan.terbang();
}
EOF
cd /tmp/uji/pewarisan
export PATH=/home/kal/dart-sdk/bin:$PATH
dart analyze anggota_subkelas.dartAnalyzing anggota_subkelas.dart...
error - anggota_subkelas.dart:11:9 - The method 'terbang' isn't defined for the type 'Hewan'. Try correcting the name to the name of an existing method, or defining a method named 'terbang'. - undefined_method
1 issue found.Pesan undefined_method menyatakan bahwa tipe Hewan tidak memiliki metode terbang, dan pesan itu muncul bahkan ketika objek yang dibuat jelas bertipe Hewan biasa tanpa anggota tambahan. Perbaikannya menyesuaikan maksud program, yaitu menyimpan objek dengan referensi bertipe Burung apabila terbang() memang akan dipanggil, atau menaikkan metode itu ke Hewan apabila seluruh hewan membutuhkannya. Anggota yang boleh dipanggil ditentukan oleh tipe referensi saat deklarasi, bukan oleh tipe objek yang terbentuk saat runtime. Kesalahan serupa juga terjadi pada bidang, karena pemanggilan bidang milik subkelas melalui referensi induk ditolak dengan alasan yang sama.
Ringkasan#
extendsmewariskan seluruh anggota kelas induk ke subkelas, dengan satu kelas induk langsung untuk setiap kelas.@overridememinta analyzer memeriksa keabsahan penggantian anggota, sedangkansuper.namaMetode()menjalankan versi milik kelas induk.- Kelas abstract tidak dapat diinstansiasi dan boleh memuat metode tanpa isi yang wajib diimplementasikan setiap subkelasnya.
- Anggota yang boleh dipanggil ditentukan oleh tipe statis referensi, sehingga anggota milik subkelas tidak terjangkau melalui referensi bertipe kelas induk.
Referensi#
- Classes — dart.dev — Dokumentasi resmi kelas Dart, mencakup extends, override, dan metode abstract.
- Effective Dart — dart.dev — Pedoman perancangan kontrak kelas dan penggunaan anotasi override.