Menguasai pilar ketiga OOP: perbedaan polymorphism compile-time (method overloading) dan runtime (overriding dengan dynamic dispatch), praktik program to interface, serta polymorphic collections yang membuat kode fleksibel terhadap perubahan.

Di episode 7 kita melihat overriding sebagai cara subclass menimpa perilaku superclass. Episode ini melebarkan lensa: polymorphism — kemampuan satu pesan menghasilkan banyak bentuk perilaku. Inilah pilar yang membuat OOP benar-benar "bernyawa", dan inilah yang membedakan kode yang harus di-edit berkali-kali dari kode yang bisa diperluas dengan menambahkan class baru.
Mengapa polymorphism adalah keterampilan paling berharga di series ini? Karena hampir semua desain modern — interface, strategy pattern, dependency injection, framework seperti Spring — berdiri di atasnya. Episode 8 adalah fondasi teknisnya.
| Jenis | Waktu keputusan | Mekanisme |
|---|---|---|
| Compile-time | Saat compile | Method overloading |
| Runtime | Saat program berjalan | Method overriding + dynamic dispatch |
Sudah kita lihat di episode 5: beberapa method dengan nama sama, parameter berbeda. Compiler memilih yang tepat berdasarkan tipe argumen sebelum program berjalan:
public class Formatter {
String format(int angka) {
return "Angka: " + angka;
}
String format(String teks) {
return "Teks: " + teks;
}
String format(int angka, boolean panjang) {
return panjang ? "Angka detail: " + angka : "Angka: " + angka;
}
}format(42) vs format("halo") vs format(42, true) — kompiler sudah tahu method mana yang dipanggil. Nol biaya runtime, tetapi juga nol fleksibilitas: tipe sudah ditentukan.
Inilah polymorphism yang sebenarnya. Kunci dari dynamic dispatch: method yang dieksekusi ditentukan oleh tipe object yang sebenarnya saat runtime, bukan tipe variabelnya.
public class Hewan {
void suara() {
System.out.println("...");
}
}
public class Kucing extends Hewan {
@Override
void suara() {
System.out.println("Meong");
}
}
public class Anjing extends Hewan {
@Override
void suara() {
System.out.println("Guk");
}
}Sekarang bagian ajaibnya:
public class Main {
public static void main(String[] args) {
Hewan hewan = new Kucing();
hewan.suara(); // Meong — bukan "..."
Hewan hewan2 = new Anjing();
hewan2.suara(); // Guk
}
}Variabel bertipe Hewan, tetapi object sebenarnya Kucing — dan Java memanggil method Kucing.suara() karena keputusan diambil saat runtime. Inilah dynamic dispatch: pemanggil hanya tahu "saya mengirim pesan suara ke Hewan", dan setiap object menjawab dengan caranya sendiri.
Pola paling kuat: deklarasikan variabel/parameter dengan tipe yang paling abstrak, isi dengan object konkret. Ini disebut program to interface.
public interface Kendaraan {
void jalan();
}
public class Motor implements Kendaraan {
public void jalan() {
System.out.println("Motor berjalan");
}
}
public class Kapal implements Kendaraan {
public void jalan() {
System.out.println("Kapal berlayar");
}
}public class Main {
public static void main(String[] args) {
Kendaraan kendaraan = new Motor();
kendaraan.jalan();
}
}Kekuatan sebenarnya: kode yang memakai Kendaraan tidak perlu diubah ketika implementasi baru muncul. Tambah class Pesawat implements Kendaraan — pemanggil tetap kendaraan.jalan(). Tanpa polymorphic thinking, menambah jenis kendaraan berarti menulis if (jenis == Motor) {...} else if (jenis == Kapal) {...} — dan itulah spiral kode yang memburuk.
Collection Java (detail lengkap di episode 11) memakai polymorphism ini: deklarasikan collection dengan tipe umum, isi dengan berbagai subclass.
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<Hewan> kebunBinatang = new ArrayList<>();
kebunBinatang.add(new Kucing());
kebunBinatang.add(new Anjing());
kebunBinatang.add(new Kucing());
for (Hewan hewan : kebunBinatang) {
hewan.suara();
}
}
}Output:
Meong
Guk
MeongPerhatikan: satu loop yang sama menangani tiga object berbeda tanpa satu pun if untuk membedakan tipe. Menambah jenis hewan baru = menambah class baru, tanpa menyentuh loop. Ini pola yang akan kalian lihat berulang di SOLID (episode 14) dan design patterns (episode 15).
if/else?Kode berbasis if (instanceof ...) menumbuhkan cabang setiap kali ada jenis baru — melanggar Open/Closed Principle (episode 14). Polymorphism membalik arah: alih-alih pemanggil memeriksa jenis, setiap object tahu jawabannya sendiri. Hasilnya kode pemanggil tetap stabil saat domain tumbuh.
Tip
Kalian sering mendengar "program to interface". Praktiknya sederhana: variabel dan parameter bertipe interface/abstrak, nilai diisi object konkret. Jangan deklarasikan ArrayList bila List cukup, dan jangan List bila pemakai hanya butuh iterasi.
instanceof BerlebihanKadang instanceof memang perlu (misal type safety di kasus tertentu), tetapi jika dipakai untuk menggantikan polymorphism, itu bau kode:
public class Main {
static void bunyi(Object hewan) {
if (hewan instanceof Kucing k) {
k.suara();
} else if (hewan instanceof Anjing a) {
a.suara();
}
}
}Setiap hewan baru = tambah cabang baru. Bandingkan dengan polymorphic call hewan.suara() yang tidak pernah berubah. Episode 21 akan menunjukkan cara menyingkirkan cabang-cabang ini lewat refactoring.
Dua-duanya polymorphism, tetapi titik keputusannya berbeda. Ingat: overriding butuh @Override; overloading tidak.
Warning
Dynamic dispatch hanya berlaku untuk method, bukan untuk field. Field di Java tidak polymorphic: ((Hewan) kucing).nama membaca field Hewan, bukan field Kucing. Inilah salah satu alasan field seharusnya private dan diakses lewat method (episode 6).
Inti yang harus dibawa pulang:
if/else berantai dan mendukung pertumbuhan domain.Di episode 9 selanjutnya kita akan membahas abstraction: abstract class & interface — kapan memakai mana, method default/static/private di interface, dan sealed interface. Sampai jumpa di episode 9!