1. Mengapa Kita Membutuhkan Generics?
Generics memungkinkan kita menulis kode yang fleksibel, dapat digunakan kembali (reusable), namun tetap mempertahankan keamanan tipe data ketat (Type Safety). Dengan Generics, kita tidak perlu menduplikasi kode untuk tipe data yang berbeda (seperti int, String, atau objek custom).
// Tanpa Generics (Membutuhkan kelas terpisah untuk setiap tipe data)
class KotakString { String isi; KotakString(this.isi); }
class KotakInt { int isi; KotakInt(this.isi); }
// DENGAN GENERICS: Satu Class untuk Segala Tipe Data!
class Kotak<T> {
T isi;
Kotak(this.isi);
T ambilIsi() => isi;
}
void main() {
var k1 = Kotak<String>("Kabel Fiber Optik");
var k2 = Kotak<int>(1000); // 1000 Mbps
print("Isi K1: ${k1.ambilIsi()} | Isi K2: ${k2.ambilIsi()}");
}
2. Bounded Generics (Membatasi Tipe dengan 'extends')
Kadang kita ingin parameter generik hanya menerima tipe data turunan tertentu. Contohnya, membuat kalkulator yang hanya menerima angka (num, int, double):
// T hanya boleh turunan dari kelas num
class KalkulatorStatistik<T extends num> {
T nilaiA;
T nilaiB;
KalkulatorStatistik(this.nilaiA, this.nilaiB);
double hitungRataRata() {
return (nilaiA + nilaiB) / 2;
}
}
void main() {
var stat1 = KalkulatorStatistik<double>(85.5, 92.0);
print("Rata-rata: ${stat1.hitungRataRata()}");
// var statError = KalkulatorStatistik<String>("A", "B"); // ERROR KOMPILASI!
}
3. Generic Methods (Fungsi Generik)
Fungsi juga dapat memiliki parameter tipe generik tersendiri:
T ambilElemenPertama<T>(List<T> list) {
if (list.isEmpty) {
throw Exception("List kosong!");
}
return list.first;
}
void main() {
var listRouter = ['MikroTik', 'Cisco', 'Juniper'];
String rUtama = ambilElemenPertama<String>(listRouter);
print("Router Utama: $rUtama");
}