Menu

Polimorfisme Java: Satu Interface, Banyak Bentuk

Cara polimorfisme di Java membuat satu variabel bisa merujuk ke banyak tipe, kenapa method yang di-override dipilih saat runtime, dan cara memakai upcasting, downcasting, dan instanceof dengan aman.

Halaman ini dilengkapi editor yang bisa dijalankan. Edit, jalankan, dan lihat output-nya seketika.

Satu Referensi, Banyak Tipe

Polimorfisme adalah hasil yang kamu petik dari inheritance dan interface. Artinya, satu variabel yang bertipe parent (atau interface) bisa berisi objek dari subtipe mana pun, dan saat kamu memanggil method padanya, Java menjalankan versi milik class asli objek itu, bukan versi yang tersirat dari tipe variabel yang dideklarasikan.

Kamu sudah melihat dasarnya di halaman inheritance: subclass meng-override method dari parent-nya. Polimorfisme membuat override itu berguna: kamu bisa menulis kode untuk tipe umumnya dan membiarkan setiap objek konkret berperilaku dengan caranya sendiri.

a dan b sama-sama dideklarasikan sebagai Animal, tetapi masing-masing mencetak suaranya sendiri. Pilihan itu terjadi saat runtime berdasarkan objek yang sebenarnya.

Dynamic Method Dispatch

Mekanisme di baliknya disebut dynamic method dispatch: untuk method instance yang di-override, JVM melihat class runtime objek untuk memutuskan implementasi mana yang dipanggil. Compiler hanya memeriksa bahwa method itu ada di tipe yang dideklarasikan; pemilihan sebenarnya ditunda sampai program berjalan.

Inilah yang membuat satu loop bisa menangani campuran banyak tipe tanpa pernah bertanya tipe masing-masing:

Loop itu hanya tahu tentang Shape. Kalau nanti kamu menambahkan Triangle extends Shape, kode ini tetap berjalan tanpa diubah, dan memang itulah intinya. Kode bergantung pada abstraksinya, bukan pada daftar tipe konkretnya.

Upcasting dan Downcasting

Menyimpan Dog di variabel Animal disebut upcasting, yaitu naik di hierarki ke tipe yang lebih umum. Ini selalu aman dan Java melakukannya secara implisit, karena setiap Dog adalah Animal.

Arah sebaliknya disebut downcasting, yaitu mengambil referensi parent dan memperlakukannya sebagai subtipe tertentu. Ini adalah bentuk type casting antar tipe referensi, dan hanya valid kalau objeknya memang subtipe itu. Jadi kamu harus menulis cast-nya secara eksplisit, dan kamu berisiko terkena ClassCastException kalau salah:

Cast terakhir lolos kompilasi, karena compiler tidak bisa membuktikan bahwa cast itu salah, tetapi gagal saat dijalankan karena Cat bukan Dog. Jangan pernah melakukan downcast hanya berdasarkan keyakinan.

Lindungi Downcast dengan instanceof

Sebelum downcasting, cek tipe aslinya dengan instanceof. Java modern memungkinkanmu mengikat hasilnya di ekspresi yang sama (pattern matching untuk instanceof), jadi kamu tidak perlu cast terpisah:

instanceof mengembalikan false untuk null, jadi pengecekan ini juga melindungimu dari NullPointerException. Meski begitu, kalau kamu mendapati dirimu menulis rantai instanceof yang panjang, sering kali itu tanda bahwa perilakunya seharusnya berada di dalam class sebagai method yang di-override. Biarkan polimorfisme yang melakukan percabangan untukmu.

Overriding vs Overloading

Keduanya terdengar mirip tetapi tidak berhubungan, dan tertukar di antara keduanya adalah sumber kebingungan yang klasik.

Overriding adalah subclass yang mengganti method parent dengan signature yang identik. Pemilihannya terjadi saat runtime berdasarkan tipe objek, dan inilah polimorfisme yang sejak tadi kita pakai.

Overloading adalah satu class yang punya beberapa method dengan nama sama tetapi daftar parameter berbeda. Pemilihannya terjadi saat kompilasi berdasarkan tipe argumen, tanpa ada dispatch saat runtime:

Compiler memilih describe yang cocok murni dari tipe statis argumennya. Tidak ada objek parent/child yang terlibat, jadi ini bukan polimorfisme runtime, melainkan sekadar memakai ulang nama method.

Jebakan Umum: Field Tidak Polimorfik

Hanya method instance yang dipilih saat runtime. Field dan method static ditentukan berdasarkan tipe yang dideklarasikan, dan ini menjebak banyak orang:

p.name() menjalankan versi milik Child (polimorfisme), tetapi p.label membaca field milik Parent, karena field itu disembunyikan (hidden), bukan di-override. Solusinya sederhana: jadikan field private dan akses hanya lewat method, supaya pemanggilan polimorfik selalu menang.

Berikutnya: Access Modifier

Polimorfisme hanya berjalan mulus kalau subclass bisa melihat dan meng-override member yang tepat, sementara bagian lain dari kodemu tidak bisa masuk dan merusak aturan yang harus dijaga. Keseimbangan itu diatur oleh akses public, protected, private, dan package-private, yaitu access modifier, yang dibahas berikutnya.

Pertanyaan yang sering diajukan

Apa itu polimorfisme di Java?

Polimorfisme berarti satu tipe referensi bisa menunjuk ke objek dari banyak class yang berbeda, dan method yang benar-benar dijalankan dipilih berdasarkan tipe asli objek saat runtime, bukan berdasarkan tipe variabel yang dideklarasikan. Jadi variabel Shape shape bisa berisi Circle atau Square, dan memanggil shape.area() otomatis menjalankan versi yang tepat.

Apa perbedaan overriding dan overloading di Java?

Overriding terjadi saat subclass mengganti method superclass dengan signature yang sama, dan inilah yang menggerakkan polimorfisme saat runtime. Overloading terjadi saat satu class punya beberapa method dengan nama sama tetapi daftar parameter berbeda; compiler memilih salah satunya saat kompilasi berdasarkan argumennya. Overriding diputuskan saat runtime berdasarkan tipe objek; overloading diputuskan saat kompilasi berdasarkan tipe argumen.

Apa perbedaan upcasting dan downcasting di Java?

Upcasting memperlakukan objek child sebagai tipe parent-nya (Animal a = new Dog();), selalu aman dan biasanya implisit. Downcasting berjalan ke arah sebaliknya (Dog d = (Dog) a;) dan hanya aman kalau objeknya memang subtipe itu; kalau tidak, akan melempar ClassCastException. Lindungi setiap downcast dengan instanceof lebih dulu.

Ilustrasi bahasa pemrograman di Coddy

Belajar coding dengan Coddy

Mulai