Operator armada mengeluarkan laporan deskripsi untuk seluruh kendaraan di garasi. Menulis satu blok if untuk setiap tipe kendaraan membuat kode laporan terus bertambah setiap kali tipe baru hadir. Runtime polymorphism menyelesaikan masalah ini: kode pemanggil cukup memanggil satu method melalui reference bertipe dasar, dan versi implementasi yang benar dipilih runtime sesuai tipe object yang sesungguhnya. Mekanisme ini disebut juga dynamic dispatch, dan output program membuktikan versi mana yang benar-benar berjalan.
Compile-time Type versus Runtime Type #
Satu object membawa dua tipe yang berbeda peran. Compile-time type adalah tipe yang tertulis pada deklarasi variable; tipe ini menentukan member apa yang boleh dipanggil. Runtime type adalah tipe class yang dibuat oleh operator new; tipe inilah yang menentukan versi override mana yang dijalankan. Method GetType() menampilkan runtime type, sehingga kita dapat membuktikan dispatch tersebut pada output.
Contoh: Satu Reference, Dua Tipe #
public class Kendaraan
{
public virtual string Deskripsi()
{
return "Kendaraan umum";
}
}
public class Mobil : Kendaraan
{
public override string Deskripsi()
{
return "Mobil pribadi empat roda";
}
}
public class Motor : Kendaraan
{
public override string Deskripsi()
{
return "Motor roda dua";
}
}
Kendaraan kendaraanPertama = new Mobil();
Console.WriteLine($"{kendaraanPertama.Deskripsi()} | tipe nyata: {kendaraanPertama.GetType().Name}");Output yang Dihasilkan:
Mobil pribadi empat roda | tipe nyata: MobilMari kita bedah baris demi baris:
public virtual string Deskripsi(): Base class menandai method sebagai titik penggantian, syarat bagi dynamic dispatch.public override string Deskripsi(): Dua derived class mengisi method yang sama dengan deskripsi berbeda.Kendaraan kendaraanPertama = new Mobil();: Compile-time type variable adalahKendaraan, sedangkan runtime type-nyaMobil; dua tipe itu sengaja dibedakan di sini.kendaraanPertama.Deskripsi(): Runtime memeriksa tipe object yang ditunjuk, lalu menjalankan versiMobil, bukan versiKendaraan; output baris pertama menunjukkan hasilnya.GetType().Name: Runtime type tercetak sebagaiMobil, memperlihatkan bahwa keputusan dispatch mengikuti tipe nyata object, bukan tipe variable.
Dispatch pada Koleksi Campuran #
Polimorfisme baru terasa penuh ketika banyak tipe berdiri dalam satu daftar:
List<Kendaraan> armada = new List<Kendaraan>
{
new Mobil(),
new Motor(),
new Kendaraan()
};
foreach (Kendaraan kendaraan in armada)
{
Console.WriteLine($"{kendaraan.Deskripsi()} | tipe nyata: {kendaraan.GetType().Name}");
}Output yang Dihasilkan:
Mobil pribadi empat roda | tipe nyata: Mobil
Motor roda dua | tipe nyata: Motor
Kendaraan umum | tipe nyata: KendaraanPerulangan hanya memuat satu baris pemanggilan, tetapi output menampilkan tiga deskripsi berbeda. Runtime memilih versi Mobil untuk elemen pertama, versi Motor untuk elemen kedua, dan versi Kendaraan untuk elemen ketiga; tiga hasil itu terjadi tanpa satu pun pemeriksaan tipe manual di kode kita.
Bab penutup section ini membandingkan inheritance dengan composition, dua cara memakai ulang perilaku yang sering bersaing dalam keputusan desain.