Antarmuka memungkinkan bahasa yang diketik secara statis untuk mendukung polimorfisme. Purist Berorientasi Objek akan bersikeras bahwa bahasa harus menyediakan warisan, enkapsulasi, modularitas dan polimorfisme agar menjadi bahasa Berorientasi Objek dengan fitur lengkap. Dalam bahasa yang diketik secara dinamis - atau diketik bebek - (seperti Smalltalk,) polimorfisme adalah sepele; Namun, dalam bahasa yang diketik secara statis (seperti Java atau C #,) polimorfisme jauh dari sepele (pada kenyataannya, di permukaan tampaknya bertentangan dengan gagasan mengetik kuat.)
Biarkan saya menunjukkan:
Dalam bahasa yang diketik secara dinamis (atau diketik bebek) (seperti Smalltalk), semua variabel adalah referensi ke objek (tidak kurang dan tidak lebih.) Jadi, di Smalltalk, saya bisa melakukan ini:
|anAnimal|
anAnimal := Pig new.
anAnimal makeNoise.
anAnimal := Cow new.
anAnimal makeNoise.
Kode itu:
- Deklarasikan variabel lokal yang disebut anAnimal (perhatikan bahwa kami TIDAK menentukan TYPE dari variabel - semua variabel adalah referensi ke objek, tidak lebih dan tidak kurang.)
- Membuat instance baru dari kelas bernama "Babi"
- Tetapkan instance baru Pig tersebut ke variabel anAnimal.
- Mengirim pesan
makeNoise
ke babi.
- Mengulangi semuanya menggunakan sapi, tetapi menugaskannya ke variabel yang sama persis seperti Babi.
Kode Java yang sama akan terlihat seperti ini (membuat asumsi bahwa Bebek dan Sapi adalah subkelas Hewan:
Animal anAnimal = new Pig();
duck.makeNoise();
anAnimal = new Cow();
cow.makeNoise();
Itu semua baik dan bagus, sampai kami memperkenalkan kelas Sayuran. Sayuran memiliki beberapa perilaku yang sama dengan Hewan, tetapi tidak semua. Misalnya, Hewan dan Sayuran mungkin bisa tumbuh, tetapi jelas sayuran tidak membuat kebisingan dan hewan tidak bisa dipanen.
Di Smalltalk, kita bisa menulis ini:
|aFarmObject|
aFarmObject := Cow new.
aFarmObject grow.
aFarmObject makeNoise.
aFarmObject := Corn new.
aFarmObject grow.
aFarmObject harvest.
Ini berfungsi dengan baik di Smalltalk karena itu diketik bebek (jika berjalan seperti bebek, dan dukun seperti bebek - itu adalah bebek.) Dalam hal ini, ketika pesan dikirim ke objek, pencarian dilakukan pada daftar metode penerima, dan jika metode yang cocok ditemukan, itu disebut. Jika tidak, beberapa jenis pengecualian NoSuchMethodError dilemparkan - tetapi semuanya dilakukan saat runtime.
Tetapi di Jawa, bahasa yang diketik secara statis, jenis apa yang bisa kita tetapkan untuk variabel kita? Jagung perlu diwariskan dari Sayuran, untuk mendukung tumbuh, tetapi tidak dapat mewarisi dari Hewan, karena tidak membuat kebisingan. Sapi perlu diwariskan dari Hewan untuk mendukung makeNoise, tetapi tidak dapat mewarisi dari Sayuran karena tidak boleh menerapkan panen. Sepertinya kita membutuhkan banyak warisan - kemampuan untuk mewarisi dari lebih dari satu kelas. Tapi itu ternyata menjadi fitur bahasa yang cukup sulit karena semua kasus tepi yang muncul (apa yang terjadi ketika lebih dari satu superclass paralel menerapkan metode yang sama ?, dll.)
Datanglah antarmuka ...
Jika kita membuat kelas Hewan dan Sayuran, dengan masing-masing menerapkan Growable, kita dapat mendeklarasikan bahwa Sapi kita adalah Hewan dan Jagung kita adalah Sayuran. Kami juga dapat menyatakan bahwa Hewan dan Sayuran adalah Growable. Itu memungkinkan kami menulis ini untuk menumbuhkan segalanya:
List<Growable> list = new ArrayList<Growable>();
list.add(new Cow());
list.add(new Corn());
list.add(new Pig());
for(Growable g : list) {
g.grow();
}
Dan ini memungkinkan kita melakukan ini, untuk membuat suara binatang:
List<Animal> list = new ArrayList<Animal>();
list.add(new Cow());
list.add(new Pig());
for(Animal a : list) {
a.makeNoise();
}
Keuntungan dari bahasa yang diketik bebek adalah Anda mendapatkan polimorfisme yang benar-benar bagus: semua yang harus dilakukan kelas untuk memberikan perilaku adalah menyediakan metode. Selama semua orang bermain bagus, dan hanya mengirim pesan yang cocok dengan metode yang ditentukan, semuanya baik. Kelemahannya adalah jenis kesalahan di bawah ini tidak diketahui sampai runtime:
|aFarmObject|
aFarmObject := Corn new.
aFarmObject makeNoise. // No compiler error - not checked until runtime.
Bahasa yang diketik secara statis memberikan "pemrograman berdasarkan kontrak" yang jauh lebih baik, karena mereka akan menangkap dua jenis kesalahan di bawah ini pada waktu kompilasi:
// Compiler error: Corn cannot be cast to Animal.
Animal farmObject = new Corn();
farmObject makeNoise();
-
// Compiler error: Animal doesn't have the harvest message.
Animal farmObject = new Cow();
farmObject.harvest();
Jadi .... untuk meringkas:
Implementasi antarmuka memungkinkan Anda menentukan jenis hal yang dapat dilakukan objek (interaksi) dan pewarisan Kelas memungkinkan Anda menentukan cara melakukan sesuatu (implementasi).
Antarmuka memberi kita banyak manfaat polimorfisme "benar", tanpa harus mengorbankan pemeriksaan tipe kompiler.