
Foto oleh Myburgh Roux via Pexels
Polymorphism, yang berarti 'banyak bentuk', adalah kemampuan satu interface atau method yang sama untuk berperilaku berbeda tergantung pada object yang sebenarnya memanggilnya. Konsep ini dicapai melalui dua mekanisme utama di C#: overriding dan overloading.
Overriding mengganti implementasi method dari class induk dengan versi baru di class turunan (seperti dibahas pada artikel inheritance), sementara overloading adalah mendefinisikan beberapa method dengan nama sama namun signature parameter yang berbeda dalam class yang sama.
Artikel ini membahas perbedaan mendasar antara overriding dan overloading, bagaimana keduanya sama-sama disebut polymorphism namun bekerja dengan cara yang sangat berbeda, serta contoh praktis penerapannya dalam skenario nyata.
Runtime Polymorphism Lewat Override
Method Luas berperilaku berbeda sesuai tipe object sebenarnya
Compile-Time Polymorphism Lewat Overload
Compiler memilih overload berdasarkan tipe dan jumlah parameter
Abstract Method untuk Memaksa Override
Abstract method wajib di-override oleh semua class turunan
Polymorphism Lewat Interface
Interface memungkinkan polymorphism antar class yang tidak sekerabat
Dampak Lupa Kata Kunci override Terhadap Polymorphism
Tanpa override, pemanggilan lewat reference parent tidak bersifat polymorphic
Kesalahan yang sering tidak disadari saat menerapkan polymorphism adalah lupa menuliskan kata kunci override pada method turunan yang dimaksudkan untuk menimpa method virtual milik parent. Tanpa override, compiler akan menganggap method Suara() pada Kucing sebagai method baru yang berdiri sendiri (perilaku method hiding), bukan sebagai penimpaan method parent, sehingga ketika dipanggil melalui reference bertipe parent seperti variabel h, yang dieksekusi tetaplah versi milik Hewan.
Polymorphism yang sebenarnya hanya berjalan ketika method pada parent ditandai virtual (atau abstract) dan method pada class turunan ditandai override, karena kombinasi inilah yang membuat CLR melakukan dynamic dispatch, yaitu menentukan implementasi method mana yang dijalankan berdasarkan tipe objek sesungguhnya saat runtime, bukan berdasarkan tipe variabel yang dideklarasikan saat compile time.
Selain override, C# juga mengenal method overloading, yaitu beberapa method dengan nama sama namun signature parameter berbeda dalam class yang sama. Overloading diselesaikan oleh compiler saat compile time (static binding) berdasarkan tipe dan jumlah argumen yang dipakai saat pemanggilan, sehingga sifatnya sangat berbeda dari overriding yang diselesaikan saat runtime.
Operator Overloading untuk Kustomisasi Perilaku Operator pada Class
Operator + didefinisikan ulang agar bisa dipakai pada objek Uang
Operator overloading memungkinkan operator bawaan seperti +, -, ==, atau < didefinisikan ulang agar dapat bekerja secara bermakna pada tipe data kustom buatan sendiri, dengan cara mendeklarasikan method static bernama operator diikuti simbol operator yang ingin didefinisikan ulang. Pada contoh di atas, operator + pada class Uang didefinisikan agar menjumlahkan nilai Nominal dari dua objek Uang dan mengembalikan objek Uang baru sebagai hasilnya, alih-alih menimbulkan compile error seperti jika operator ini tidak didefinisikan.
Fitur ini merupakan bentuk polymorphism ad-hoc, karena operator yang sama (+) berperilaku berbeda tergantung tipe operand yang dipakai, baik untuk angka biasa, string yang digabungkan, maupun objek kustom seperti Uang yang perilakunya didefinisikan sendiri oleh developer. Beberapa operator seperti == dan != biasanya didefinisikan berpasangan, karena compiler akan memberikan warning jika hanya salah satunya yang dioverload tanpa pasangannya.
Operator overloading sebaiknya dipakai secukupnya dan hanya ketika maknanya benar-benar intuitif bagi pembaca kode, karena penyalahgunaannya dapat membuat kode menjadi sulit dipahami ketika operator yang terlihat sederhana ternyata menjalankan logika tersembunyi yang kompleks atau memiliki efek samping yang tidak terduga. Sebagai alternatif yang lebih eksplisit, method biasa seperti Tambah() seringkali lebih disarankan ketika operasi yang dilakukan tidak sepenuhnya mengikuti konvensi matematika umum. Selain operator aritmatika dan perbandingan, C# juga memperbolehkan overloading operator konversi implisit maupun eksplisit antar tipe kustom, sehingga objek seperti Uang bisa dikonversi otomatis ke decimal atau sebaliknya tanpa perlu method konversi terpisah.
Enum dan FlagsAttribute untuk Menggabungkan Beberapa Nilai
Enum biasa digunakan untuk merepresentasikan satu pilihan dari sekumpulan nilai tetap, namun dengan menambahkan atribut [Flags] dan memberi setiap anggota nilai berupa pangkat dua (1, 2, 4, 8, dan seterusnya), enum dapat digabungkan menggunakan operator bitwise OR (|) untuk merepresentasikan beberapa kombinasi nilai sekaligus dalam satu variabel, seperti hak akses Baca dan Tulis yang aktif bersamaan pada contoh di atas. Tanpa pola nilai pangkat dua ini, kombinasi antar anggota enum bisa saling tumpang tindih dan menghasilkan nilai yang ambigu.
Method HasFlag() memudahkan pengecekan apakah sebuah kombinasi mengandung nilai tertentu tanpa perlu menulis operasi bitwise AND manual, seperti pada contoh yang memeriksa apakah hakAkses mencakup Akses.Hapus dan mengembalikan false karena belum ditambahkan ke kombinasi. Pola ini umum dipakai untuk merepresentasikan hak akses pengguna, status gabungan suatu proses, atau opsi konfigurasi yang bisa aktif lebih dari satu secara bersamaan tanpa harus membuat banyak variabel boolean terpisah untuk setiap kemungkinan kombinasi yang ada.
Kesalahan Umum yang Sering Terjadi
Mengira Overloading dan Overriding Adalah Hal yang Sama
Overloading terjadi dalam satu class dengan signature parameter berbeda dan ditentukan saat kompilasi, sementara overriding terjadi antara base class dan derived class dengan signature yang sama persis dan ditentukan saat runtime; mencampurkan keduanya adalah kesalahan konsep yang umum terjadi.
Lupa Abstract Class Tidak Bisa Dibuat Objectnya Langsung
Mencoba membuat object langsung dari abstract class dengan new akan menghasilkan error kompilasi, karena abstract class memang dirancang hanya sebagai cetakan bagi class turunannya, bukan untuk dipakai langsung.
Overload yang Ambigu Antar Tipe yang Mirip
Mendefinisikan overload dengan parameter yang terlalu mirip, misalnya int dan long yang bisa saling dikonversi implisit, bisa menyebabkan ambiguitas yang membuat compiler bingung menentukan overload mana yang dimaksud.
Kapan Pendekatan Ini Tidak Cocok Dipakai
Override (runtime polymorphism) memberikan fleksibilitas untuk mengubah perilaku berdasarkan tipe object sebenarnya saat program berjalan, sangat berguna untuk desain yang perlu diperluas tanpa mengubah kode yang sudah ada (mengikuti Open-Closed Principle).
Overload (compile-time polymorphism) memberikan kenyamanan API yang lebih natural bagi pemanggil method, memungkinkan nama method yang sama dipakai untuk variasi parameter yang masuk akal, namun harus dirancang hati-hati agar tidak menimbulkan ambiguitas antar overload yang mirip.
Ringkasan
Polymorphism, baik lewat override maupun overload, memberikan fleksibilitas besar dalam desain OOP: override memungkinkan perilaku berbeda berdasarkan tipe runtime, sementara overload memberikan variasi parameter untuk method dengan nama sama. Pahami perbedaan keduanya dengan jelas, karena keduanya sering disebut dengan istilah yang sama namun bekerja dengan mekanisme yang sangat berbeda.
Seri Belajar C# Fundamental:
← Sebelumnya: Inheritance dan Penggunaannya di C#
→ Selanjutnya: Interface vs Abstract Class di C#