Pemrograman Dart
Inheritance (Pewarisan): extends, super, dan Hirarki Kelas
Pelajari pilar kedua OOP di Dart 3, yaitu Inheritance (pewarisan kelas) menggunakan kata kunci extends, super parameters, serta cara merancang hirarki kelas yang bersih.

Ketika membangun sistem yang memiliki beberapa jenis entitas serupa—misalnya PegawaiTetap, PegawaiKontrak, dan Magang, atau BukuCetak dan BukuDigital—kita akan mendapati bahwa banyak properti dan method yang sama persis di antara semuanya (seperti id, nama, dan method cetakInfoDasar()). Menyalin-tempel (copy-paste) properti dan method tersebut ke setiap class akan menyulitkan pemeliharaan aplikasi. Pilar kedua Object-Oriented Programming, yaitu Inheritance (Pewarisan), memungkinkan kita menempatkan seluruh atribut dan perilaku umum di satu kelas induk (superclass), lalu menurunkan kelas-kelas anak (subclass) yang mewarisi kemampuan tersebut sekaligus menambahkan ciri khasnya masing-masing menggunakan kata kunci extends.
Yang akan dipelajari
- Memahami hubungan is-a (adalah sebuah) dalam merancang hirarki kelas induk (superclass) dan kelas anak (subclass).
- Menggunakan kata kunci
extendsuntuk mewarisi properti dan method dari kelas induk. - Meneruskan parameter constructor ke kelas induk secara ringkas menggunakan fitur super parameters (
super.namaField) di Dart 3. - Memanggil method atau constructor milik kelas induk menggunakan kata kunci
superdan mengenali kapan pewarisan sebaiknya tidak dipaksakan.
Konsep pewarisan (extends) dan hubungan is-a
Di Dart, sebuah class dapat mewarisi satu kelas induk (single inheritance) menggunakan sintaks class KelasAnak extends KelasInduk. Sebelum menggunakan extends, selalu uji apakah kedua kelas memiliki hubungan is-a (adalah sebuah) yang masuk akal:
KendaraanListrikadalah sebuahKendaraan(tepat menggunakanextends).Dosenadalah seorangCivitasAkademika(tepat menggunakanextends).Mobilmemiliki sebuahMesin(ini adalah hubungan has-a / komposisi, bukanextends—letakkan objekMesinsebagai field di dalamclass Mobil).
| Istilah | Sebutan Lain | Peran dalam Hirarki |
|---|---|---|
| Superclass | Parent class / Kelas Induk | Menyimpan properti dan method umum yang berlaku bagi seluruh turunannya. |
| Subclass | Child class / Kelas Anak | Mewarisi seluruh anggota publik dari superclass dan dapat menambah field/method khusus. |
extends |
Kata kunci pewarisan | Menghubungkan subclass dengan superclass-nya. |
super |
Referensi ke kelas induk | Digunakan oleh subclass untuk memanggil constructor atau method milik superclass. |
Meneruskan constructor dengan super. (Super Parameters)
Saat sebuah objek subclass dibuat, constructor milik superclass-nya wajib ikut dijalankan agar seluruh field yang diwariskan terinisialisasi dengan benar. Sebelum Dart 2.17/Dart 3, kita harus menulis ulang tipe dan nama parameter di kelas anak lalu meneruskannya di akhir dengan : super(id: id, nama: nama).
Di Dart 3, kita cukup menggunakan sintaks super-initializer parameters (super.namaField) langsung di daftar parameter constructor kelas anak—sama ringkasnya seperti saat kita menulis this.namaField!
Mari kita buat contoh hirarki produk di dalam folder lib/item_perpustakaan.dart:
// File: lib/item_perpustakaan.dart
// SUPERCLASS (Kelas Induk)
class ItemPerpustakaan {
final String kode;
final String judul;
final int tahunTerbit;
bool _sedangDipinjam = false;
ItemPerpustakaan({
required this.kode,
required this.judul,
required this.tahunTerbit,
});
bool get sedangDipinjam => _sedangDipinjam;
void pinjam() {
if (_sedangDipinjam) {
print('Maaf, "$judul" sedang dipinjam orang lain.');
return;
}
_sedangDipinjam = true;
print('Berhasil meminjam "$judul" [$kode].');
}
void kembalikan() {
_sedangDipinjam = false;
print('Terima kasih telah mengembalikan "$judul".');
}
String infoDasar() => '[$kode] $judul ($tahunTerbit)';
}
// SUBCLASS 1: BukuCetak mewarisi ItemPerpustakaan
class BukuCetak extends ItemPerpustakaan {
final String penulis;
final int jumlahHalaman;
final String nomorRak;
// Menggunakan super.kode, super.judul, dan super.tahunTerbit (Dart 3)
BukuCetak({
required super.kode,
required super.judul,
required super.tahunTerbit,
required this.penulis,
required this.jumlahHalaman,
required this.nomorRak,
});
void cetakDetailBuku() {
// Memanggil method infoDasar() yang diwarisi dari superclass
print('${infoDasar()} | Penulis: $penulis | $jumlahHalaman hlm | Rak: $nomorRak');
}
}
// SUBCLASS 2: EBook juga mewarisi ItemPerpustakaan
class EBook extends ItemPerpustakaan {
final double ukuranFileMb;
final String formatFile;
EBook({
required super.kode,
required super.judul,
required super.tahunTerbit,
required this.ukuranFileMb,
this.formatFile = 'PDF',
});
void unduhPratinjau() {
print('Mengunduh sampel "${super.judul}" ($ukuranFileMb MB, format $formatFile)...');
}
}
Sekarang kita uji kedua subclass tersebut dari bin/belajar_dart_oop.dart:
// File: bin/belajar_dart_oop.dart
import 'package:belajar_dart_oop/item_perpustakaan.dart';
void main() {
var buku = BukuCetak(
kode: 'BK-101',
judul: 'Pemrograman Dart 3 Modern',
tahunTerbit: 2026,
penulis: 'Widia Rohman',
jumlahHalaman: 320,
nomorRak: 'A-12',
);
var ebook = EBook(
kode: 'EB-205',
judul: 'Panduan Arsitektur Flutter',
tahunTerbit: 2026,
ukuranFileMb: 14.5,
formatFile: 'EPUB',
);
// BukuCetak dan EBook otomatis memiliki method pinjam() & kembalikan() dari ItemPerpustakaan
buku.cetakDetailBuku();
buku.pinjam();
buku.pinjam(); // Gagal karena sudah berstatus dipinjam
print('');
ebook.unduhPratinjau();
ebook.pinjam();
}
Keluaran program:
[BK-101] Pemrograman Dart 3 Modern (2026) | Penulis: Widia Rohman | 320 hlm | Rak: A-12
Berhasil meminjam "Pemrograman Dart 3 Modern" [BK-101].
Maaf, "Pemrograman Dart 3 Modern" sedang dipinjam orang lain.
Mengunduh sampel "Panduan Arsitektur Flutter" (14.5 MB, format EPUB)...
Berhasil meminjam "Panduan Arsitektur Flutter" [EB-205].
Perhatikan bagaimana kita sama sekali tidak perlu menulis ulang logika _sedangDipinjam, pinjam(), maupun kembalikan() di dalam BukuCetak dan EBook.
Memanggil named constructor induk dengan : super.nama(...)
Bagaimana jika kelas induk memiliki named constructor, atau jika kelas anak perlu mengolah nilai terlebih dahulu sebelum mengirimkannya ke constructor induk? Kita dapat memanggil : super(...) atau : super.namaConstructor(...) di bagian initializer list:
class Pegawai {
final String nama;
final int gajiPokok;
Pegawai({required this.nama, required this.gajiPokok});
Pegawai.magang(this.nama) : gajiPokok = 2500000;
}
class Programmer extends Pegawai {
final String bahasaUtama;
final int tunjanganProyek;
// 1. Meneruskan ke constructor utama Pegawai menggunakan super.
Programmer({
required super.nama,
required super.gajiPokok,
required this.bahasaUtama,
this.tunjanganProyek = 1500000,
});
// 2. Memanggil named constructor milik kelas induk (: super.magang)
Programmer.intern({
required String nama,
required this.bahasaUtama,
}) : tunjanganProyek = 500000,
super.magang(nama);
int get totalPenghasilan => gajiPokok + tunjanganProyek;
}
void main() {
var senior = Programmer(
nama: 'Dimas',
gajiPokok: 9000000,
bahasaUtama: 'Dart',
);
var anakMagang = Programmer.intern(
nama: 'Fajar',
bahasaUtama: 'Dart & Flutter',
);
print('${senior.nama} (${senior.bahasaUtama}): Rp${senior.totalPenghasilan}');
print('${anakMagang.nama} (${anakMagang.bahasaUtama}): Rp${anakMagang.totalPenghasilan}');
}
Keluaran program:
Dimas (Dart): Rp10500000
Fajar (Dart & Flutter): Rp3000000
Aturan visibilitas dan operator is pada pewarisan
- Pemeriksaan tipe (
is): Karena setiapBukuCetakadalahItemPerpustakaan, ekspresibuku is BukuCetakdanbuku is ItemPerpustakaankeduanya menghasilkantrue. - Anggota privat (
_) lintas file: Ingat kembali aturan dari Materi 11! JikaItemPerpustakaanberada dilib/item.dartdanBukuCetakberada di file terpisahlib/buku_cetak.dart, makaBukuCetaktidak bisa mengakses_sedangDipinjamsecara langsung, melainkan harus melalui getter publiksedangDipinjamatau methodpinjam().
Kaitannya dengan Flutter / Dunia Nyata: Setiap kali Anda membuat layar atau komponen baru di Flutter, baris pertama yang Anda tulis adalah
class BerandaPage extends StatelessWidgetdan di dalam constructor-nya terdapatconst BerandaPage({super.key});. Sintakssuper.keytersebut persis merupakan fitur super parameter Dart 3 yang baru saja kita pelajari untuk meneruskan identitas widget (Key) ke kelas indukWidget!
Coba sendiri
Di dalam proyek VS Code Anda:
- Buat satu subclass baru di dalam
lib/item_perpustakaan.dartbernamaclass JurnalIlmiah extends ItemPerpustakaanyang memiliki field tambahanfinal int volumedanfinal String bidangStudi. - Tambahkan method
cetakSitasi()padaJurnalIlmiah. - Di dalam
bin/belajar_dart_oop.dart, buat objek dariJurnalIlmiah, lalu uji hasil pemeriksaan tipeprint(jurnal is ItemPerpustakaan);danprint(jurnal is BukuCetak);.
Kesalahan yang sering terjadi
| Kesalahan Umum | Penyebab | Solusi yang Benar |
|---|---|---|
| Error: The superclass 'X' doesn't have a zero argument constructor | Membuat subclass tanpa memanggil constructor induk, padahal superclass memiliki parameter constructor yang wajib diisi. | Gunakan super.namaField pada parameter constructor subclass, atau panggil : super(...) di initializer list. |
Mencoba mewarisi dua kelas sekaligus (class A extends B, C) |
Dart hanya mendukung single inheritance (satu kelas induk langsung per class) untuk menghindari konflik diamond problem. | Warisi satu kelas induk utama dengan extends, dan gunakan mixin (with) untuk berbagi perilaku tambahan (dibahas di Materi 15). |
Menulis : super(...) sebelum inisialisasi field anak di initializer list (: super(x), y = 10;) |
Aturan urutan eksekusi Dart mewajibkan panggilan : super(...) diletakkan paling akhir di dalam initializer list. |
Inisialisasi seluruh field milik subclass terlebih dahulu, baru tutup dengan , super(...);. |
Memaksakan extends hanya demi menghemat 2 baris kode padahal tidak ada hubungan is-a (mis. class Faktur extends Pelanggan) |
Menyebabkan objek Faktur memiliki method milik Pelanggan yang tidak masuk akal secara domain bisnis. |
Gunakan komposisi (has-a): simpan final Pelanggan pelanggan; sebagai field di dalam class Faktur. |
Latihan
Kerjakan tiga soal latihan berikut untuk menguji pemahaman Anda tentang hirarki kelas dan super:
- Hirarki Kendaraan (
extends&super.): Buatclass Kendaraandengan fieldfinal String merekdanfinal int tahun, serta methodString infoKendaraan(). Turunkan menjadiclass MobilListrik extends Kendaraanyang menambahkanfinal int kapasitasBateraiKwhdan methoddouble estimasiJarakTempuh()(asumsikan 1 kWh dapat menempuh 6.5 km). - Hirarki Akun Bank (
Berbunga&Bisnis): Buat superclassRekeningDasardengannomorRekening,pemilik, dansaldobeserta methodsetor(int nominal). Buat dua subclass:RekeningTabungan(memilikidouble bungaTahunanPersendan methodtambahBungaBulanan()) sertaRekeningGiro(memilikiint biayaAdminBulanandan methodpotongAdmin()). - Hirarki Bertingkat Tiga Level (
Multi-level Inheritance): Buat hirarki 3 tingkat:PerangkatElektronik(nama,dayaWatt) →Komputer(menambahramGb,penyimpananGb) →LaptopGaming(menambahnamaGpu,ukuranLayarInci, serta named constructorLaptopGaming.standar(String nama)yang mengisi nilai spesifikasi default dan meneruskannya ke kelas induk).
Kunci jawaban dan pembahasan
Latihan 1
class Kendaraan {
final String merek;
final int tahun;
const Kendaraan({
required this.merek,
required this.tahun,
});
String infoKendaraan() => '$merek (Keluaran $tahun)';
}
class MobilListrik extends Kendaraan {
final int kapasitasBateraiKwh;
const MobilListrik({
required super.merek,
required super.tahun,
required this.kapasitasBateraiKwh,
});
double estimasiJarakTempuh() => kapasitasBateraiKwh * 6.5;
void cetakSpesifikasi() {
print('${infoKendaraan()} | Baterai: $kapasitasBateraiKwh kWh '
'| Jarak Tempuh: ${estimasiJarakTempuh().toStringAsFixed(0)} km');
}
}
void main() {
const mobil = MobilListrik(
merek: 'Hyundai Ioniq 5',
tahun: 2025,
kapasitasBateraiKwh: 72,
);
mobil.cetakSpesifikasi();
}
Pembahasan: Perhatikan bahwa const constructor juga dapat dikombinasikan dengan super parameters (required super.merek, required super.tahun) selama kelas induknya juga memiliki const constructor.
Latihan 2
class RekeningDasar {
final String nomorRekening;
final String pemilik;
int _saldo;
RekeningDasar({
required this.nomorRekening,
required this.pemilik,
int saldoAwal = 0,
}) : _saldo = saldoAwal;
int get saldo => _saldo;
void setor(int nominal) {
if (nominal > 0) _saldo += nominal;
}
void kurangiSaldo(int nominal) {
if (nominal > 0 && nominal <= _saldo) _saldo -= nominal;
}
}
class RekeningTabungan extends RekeningDasar {
final double bungaTahunanPersen;
RekeningTabungan({
required super.nomorRekening,
required super.pemilik,
super.saldoAwal,
this.bungaTahunanPersen = 3.6,
});
void tambahBungaBulanan() {
int bunga = (saldo * (bungaTahunanPersen / 100 / 12)).round();
setor(bunga);
print('Bunga Rp$bunga ditambahkan ke tabungan $pemilik. Saldo: Rp$saldo');
}
}
class RekeningGiro extends RekeningDasar {
final int biayaAdminBulanan;
RekeningGiro({
required super.nomorRekening,
required super.pemilik,
super.saldoAwal,
this.biayaAdminBulanan = 25000,
});
void potongAdmin() {
kurangiSaldo(biayaAdminBulanan);
print('Biaya admin Rp$biayaAdminBulanan dipotong dari giro $pemilik. Saldo: Rp$saldo');
}
}
void main() {
var tabungan = RekeningTabungan(
nomorRekening: 'TB-01',
pemilik: 'Nadia',
saldoAwal: 10000000,
);
var giro = RekeningGiro(
nomorRekening: 'GR-01',
pemilik: 'CV Maju Jaya',
saldoAwal: 5000000,
);
tabungan.tambahBungaBulanan();
giro.potongAdmin();
}
Pembahasan: Kedua subclass memanfaatkan method setor(), kurangiSaldo(), dan getter saldo yang diwarisi dari RekeningDasar, sementara masing-masing subclass fokus pada aturan bisnis spesifiknya (bunga vs biaya admin).
Latihan 3
class PerangkatElektronik {
final String nama;
final int dayaWatt;
PerangkatElektronik({
required this.nama,
required this.dayaWatt,
});
}
class Komputer extends PerangkatElektronik {
final int ramGb;
final int penyimpananGb;
Komputer({
required super.nama,
required super.dayaWatt,
required this.ramGb,
required this.penyimpananGb,
});
}
class LaptopGaming extends Komputer {
final String namaGpu;
final double ukuranLayarInci;
LaptopGaming({
required super.nama,
required super.dayaWatt,
required super.ramGb,
required super.penyimpananGb,
required this.namaGpu,
required this.ukuranLayarInci,
});
LaptopGaming.standar(String namaLaptop)
: namaGpu = 'RTX 4060',
ukuranLayarInci = 15.6,
super(
nama: namaLaptop,
dayaWatt: 240,
ramGb: 16,
penyimpananGb: 512,
);
void cetakRingkasan() {
print('$nama ($ukuranLayarInci") | RAM: ${ramGb}GB | SSD: ${penyimpananGb}GB '
'| GPU: $namaGpu | Daya: ${dayaWatt}W');
}
}
void main() {
var laptop = LaptopGaming.standar('ROG Zephyrus');
laptop.cetakRingkasan();
}
Pembahasan: Pada pewarisan bertingkat (PerangkatElektronik → Komputer → LaptopGaming), LaptopGaming mewarisi seluruh properti dari Komputer sekaligus PerangkatElektronik. Pada named constructor LaptopGaming.standar, perhatikan bahwa field milik LaptopGaming (namaGpu dan ukuranLayarInci) diinisialisasi terlebih dahulu sebelum pemanggilan super(...) di akhir initializer list.
Discussion (0)
We store your optional name, your comment, and technical hashes (not your raw IP address) to prevent spam. A functional cookie lets you edit or delete your own comments from this browser. To request deletion, use the contact page.
No comments yet. Start the discussion.