Polimorfisme dan pewarisan di Jawa
Menurut legenda Venkat Subramaniam, polimorfisme adalah konsep terpenting dalam pengaturcaraan berorientasikan objek. Polimorfisme - atau kemampuan objek untuk melakukan tindakan khusus berdasarkan jenisnya - inilah yang menjadikan kod Java fleksibel. Corak reka bentuk seperti Command, Observer, Decorator, Strategy, dan banyak lagi yang dibuat oleh Gang Of Four, semuanya menggunakan beberapa bentuk polimorfisme. Menguasai konsep ini sangat meningkatkan kemampuan anda untuk memikirkan penyelesaian terhadap cabaran pengaturcaraan.
Dapatkan kodnya
Anda boleh mendapatkan kod sumber untuk cabaran ini dan menjalankan ujian anda sendiri di sini: //github.com/rafadelnero/javaworld-challengers
Antara muka dan pewarisan dalam polimorfisme
Dengan Java Challenger ini, kami memfokuskan pada hubungan antara polimorfisme dan pewarisan. Perkara utama yang perlu diingat adalah bahawa polimorfisme memerlukan warisan atau pelaksanaan antara muka . Anda dapat melihatnya dalam contoh di bawah, yang menampilkan Duke dan Juggy:
public abstract class JavaMascot { public abstract void executeAction(); } public class Duke extends JavaMascot { @Override public void executeAction() { System.out.println("Punch!"); } } public class Juggy extends JavaMascot { @Override public void executeAction() { System.out.println("Fly!"); } } public class JavaMascotTest { public static void main(String... args) { JavaMascot dukeMascot = new Duke(); JavaMascot juggyMascot = new Juggy(); dukeMascot.executeAction(); juggyMascot.executeAction(); } }
Keluaran dari kod ini adalah:
Punch! Fly!
Kerana pelaksanaan khusus mereka, kedua-dua Duke
dan Juggy
tindakan 's akan dilaksanakan.
Adakah kaedah membebankan polimorfisme?
Banyak pengaturcara keliru mengenai hubungan polimorfisme dengan kaedah overriding dan kaedah overloading. Sebenarnya, hanya kaedah mengatasi adalah polimorfisme yang benar. Overloading berkongsi nama kaedah yang sama tetapi parameternya berbeza. Polimorfisme adalah istilah yang luas, jadi akan selalu ada perbincangan mengenai topik ini.
Apa tujuan polimorfisme?
Kelebihan dan tujuan penggunaan polimorfisme adalah untuk memutuskan kelas pelanggan dari kod pelaksanaan. Daripada dikodkan, kelas klien menerima pelaksanaan untuk melaksanakan tindakan yang diperlukan. Dengan cara ini, kelas klien cukup tahu untuk melaksanakan tindakannya, yang merupakan contoh gandingan longgar.
Untuk lebih memahami tujuan polimorfisme, perhatikan SweetCreator
:
public abstract class SweetProducer { public abstract void produceSweet(); } public class CakeProducer extends SweetProducer { @Override public void produceSweet() { System.out.println("Cake produced"); } } public class ChocolateProducer extends SweetProducer { @Override public void produceSweet() { System.out.println("Chocolate produced"); } } public class CookieProducer extends SweetProducer { @Override public void produceSweet() { System.out.println("Cookie produced"); } } public class SweetCreator { private List sweetProducer; public SweetCreator(List sweetProducer) { this.sweetProducer = sweetProducer; } public void createSweets() { sweetProducer.forEach(sweet -> sweet.produceSweet()); } } public class SweetCreatorTest { public static void main(String... args) { SweetCreator sweetCreator = new SweetCreator(Arrays.asList(new CakeProducer(), new ChocolateProducer(), new CookieProducer())); sweetCreator.createSweets(); } }
Dalam contoh ini, anda dapat melihat bahawa SweetCreator
kelas hanya mengetahui
SweetProducer
kelas tersebut. Ia tidak mengetahui pelaksanaannya masing-masing Sweet
. Pemisahan itu memberi kami fleksibiliti untuk mengemas kini dan menggunakan semula kelas kami, dan ini menjadikan kodnya lebih mudah dijaga. Semasa merancang kod anda, selalu cari cara untuk menjadikannya seluas dan fleksibel. polimorfisme adalah teknik yang sangat kuat untuk digunakan untuk tujuan ini.
Petua : @Override
Anotasi mewajibkan pengaturcara menggunakan tandatangan kaedah yang sama yang mesti diganti. Sekiranya kaedah ini tidak diganti, akan ada kesalahan penyusunan.
Jenis pengembalian kovarian dalam kaedah mengatasi
Adalah mungkin untuk mengubah jenis pengembalian kaedah yang diganti jika itu adalah jenis kovarian. A Jenis kovarian pada dasarnya adalah subkelas jenis pulangan. Pertimbangkan satu contoh:
public abstract class JavaMascot { abstract JavaMascot getMascot(); } public class Duke extends JavaMascot { @Override Duke getMascot() { return new Duke(); } }
Kerana Duke
adalah JavaMascot
, kita dapat mengubah jenis pengembalian ketika menimpa.
Polimorfisme dengan kelas inti Java
Kami menggunakan polimorfisme sepanjang masa di kelas inti Java. Salah satu contoh yang sangat mudah adalah ketika kita membuat ArrayList
kelas yang menyatakan List
antaramuka sebagai jenis:
List list = new ArrayList();
Untuk melangkah lebih jauh, pertimbangkan contoh kod ini menggunakan Java Collections API tanpa polimorfisme:
public class ListActionWithoutPolymorphism { // Example without polymorphism void executeVectorActions(Vector vector) {/* Code repetition here*/} void executeArrayListActions(ArrayList arrayList) {/*Code repetition here*/} void executeLinkedListActions(LinkedList linkedList) {/* Code repetition here*/} void executeCopyOnWriteArrayListActions(CopyOnWriteArrayList copyOnWriteArrayList) { /* Code repetition here*/} } public class ListActionInvokerWithoutPolymorphism { listAction.executeVectorActions(new Vector()); listAction.executeArrayListActions(new ArrayList()); listAction.executeLinkedListActions(new LinkedList()); listAction.executeCopyOnWriteArrayListActions(new CopyOnWriteArrayList()); }
Kod jelek, bukan? Bayangkan cuba mengekalkannya! Sekarang lihat contoh yang sama dengan polimorfisme:
public static void main(String … polymorphism) { ListAction listAction = new ListAction(); listAction.executeListActions(); } public class ListAction { void executeListActions(List list) { // Execute actions with different lists } } public class ListActionInvoker { public static void main(String... masterPolymorphism) { ListAction listAction = new ListAction(); listAction.executeListActions(new Vector()); listAction.executeListActions(new ArrayList()); listAction.executeListActions(new LinkedList()); listAction.executeListActions(new CopyOnWriteArrayList()); } }
Manfaat polimorfisme adalah fleksibiliti dan kepanjangan. Daripada membuat beberapa kaedah yang berbeza, kita dapat menyatakan hanya satu kaedah yang menerima List
jenis generik .
Memohon kaedah tertentu dalam panggilan kaedah polimorfik
Ada kemungkinan untuk menggunakan kaedah tertentu dalam panggilan polimorfik, tetapi melakukannya dengan kos fleksibiliti. Inilah contohnya:
public abstract class MetalGearCharacter { abstract void useWeapon(String weapon); } public class BigBoss extends MetalGearCharacter { @Override void useWeapon(String weapon) { System.out.println("Big Boss is using a " + weapon); } void giveOrderToTheArmy(String orderMessage) { System.out.println(orderMessage); } } public class SolidSnake extends MetalGearCharacter { void useWeapon(String weapon) { System.out.println("Solid Snake is using a " + weapon); } } public class UseSpecificMethod { public static void executeActionWith(MetalGearCharacter metalGearCharacter) { metalGearCharacter.useWeapon("SOCOM"); // The below line wouldn't work // metalGearCharacter.giveOrderToTheArmy("Attack!"); if (metalGearCharacter instanceof BigBoss) { ((BigBoss) metalGearCharacter).giveOrderToTheArmy("Attack!"); } } public static void main(String... specificPolymorphismInvocation) { executeActionWith(new SolidSnake()); executeActionWith(new BigBoss()); } }
Teknik yang kami gunakan di sini adalah casting , atau sengaja mengubah jenis objek pada waktu runtime.
Perhatikan bahawa mungkin menggunakan kaedah tertentu hanya ketika menghantar jenis generik ke jenis tertentu. Analogi yang baik akan mengatakan secara eksplisit kepada penyusun, "Hei, saya tahu apa yang saya lakukan di sini, jadi saya akan membuang objek ke jenis tertentu dan menggunakan kaedah tertentu."
Merujuk kepada contoh di atas, ada sebab penting penyusun menolak untuk menerima pemohon kaedah tertentu: kelas yang dilalui mungkin SolidSnake
. Dalam kes ini, tidak ada cara bagi penyusun untuk memastikan setiap subkelas MetalGearCharacter
mempunyai giveOrderToTheArmy
kaedah yang dinyatakan.
Kata instanceof
kunci terpelihara
Perhatikan kata terpelihara instanceof
. Sebelum menggunakan kaedah khusus, kami telah bertanya apakah MetalGearCharacter
itu " instanceof
" BigBoss
. Jika ia bukan satu BigBoss
contoh, kita akan menerima mesej pengecualian berikut:
Exception in thread "main" java.lang.ClassCastException: com.javaworld.javachallengers.polymorphism.specificinvocation.SolidSnake cannot be cast to com.javaworld.javachallengers.polymorphism.specificinvocation.BigBoss
Kata super
kunci terpelihara
Bagaimana jika kita ingin merujuk atribut atau kaedah dari superclass Java? Dalam kes ini, kita dapat menggunakan super
kata terpelihara. Sebagai contoh:
public class JavaMascot { void executeAction() { System.out.println("The Java Mascot is about to execute an action!"); } } public class Duke extends JavaMascot { @Override void executeAction() { super.executeAction(); System.out.println("Duke is going to punch!"); } public static void main(String... superReservedWord) { new Duke().executeAction(); } }
Using the reserved word super
in Duke
’s executeAction
method invokes the superclass method. We then execute the specific action from Duke
. That’s why we can see both messages in the output below:
The Java Mascot is about to execute an action! Duke is going to punch!
Take the polymorphism challenge!
Let’s try out what you’ve learned about polymorphism and inheritance. In this challenge, you’re given a handful of methods from Matt Groening’s The Simpsons, and your challenge is to deduce what the output for each class will be. To start, analyze the following code carefully:
public class PolymorphismChallenge { static abstract class Simpson { void talk() { System.out.println("Simpson!"); } protected void prank(String prank) { System.out.println(prank); } } static class Bart extends Simpson { String prank; Bart(String prank) { this.prank = prank; } protected void talk() { System.out.println("Eat my shorts!"); } protected void prank() { super.prank(prank); System.out.println("Knock Homer down"); } } static class Lisa extends Simpson { void talk(String toMe) { System.out.println("I love Sax!"); } } public static void main(String... doYourBest) { new Lisa().talk("Sax :)"); Simpson simpson = new Bart("D'oh"); simpson.talk(); Lisa lisa = new Lisa(); lisa.talk(); ((Bart) simpson).prank(); } }
What do you think? What will the final output be? Don’t use an IDE to figure this out! The point is to improve your code analysis skills, so try to determine the output for yourself.
Choose your answer and you’ll be able to find the correct answer below.
A) I love Sax! D'oh Simpson! D'oh B) Sax :) Eat my shorts! I love Sax! D'oh Knock Homer down C) Sax :) D'oh Simpson! Knock Homer down D) I love Sax! Eat my shorts! Simpson! D'oh Knock Homer down
What just happened? Understanding polymorphism
For the following method invocation:
new Lisa().talk("Sax :)");
the output will be “I love Sax!
” This is because we are passing a String
to the method and Lisa
has the method.
For the next invocation:
Simpson simpson = new Bart("D'oh");
simpson.talk();
The output will be "Eat my shorts!
" This is because we’re instantiating the Simpson
type with Bart
.
Now check this one, which is a little trickier:
Lisa lisa = new Lisa(); lisa.talk();
Here, we are using method overloading with inheritance. We are not passing anything to the talk method, which is why the Simpson
talk
method is invoked. In this case the output will be:
"Simpson!"
Here’s one more:
((Bart) simpson).prank();
In this case, the prank String
was passed when we instantiated the Bart
class with new Bart("D'oh");
. In this case, first the super.prank
method will be invoked, followed by the specific prank
method from Bart
. The output will be:
"D'oh" "Knock Homer down"
Video challenge! Debugging Java polymorphism and inheritance
Debugging is one of the easiest ways to fully absorb programming concepts while also improving your code. In this video you can follow along while I debug and explain the Java polymorphism challenge:
Common mistakes with polymorphism
It’s a common mistake to think it’s possible to invoke a specific method without using casting.
Another mistake is being unsure what method will be invoked when instantiating a class polymorphically. Remember that the method to be invoked is the method of the created instance.
Also remember that method overriding is not method overloading.
It’s impossible to override a method if the parameters are different. It is possible to change the return type of the overridden method if the return type is a subclass of the superclass method.
Apa yang perlu diingat mengenai polimorfisme
- Contoh yang dibuat akan menentukan kaedah apa yang akan digunakan semasa menggunakan polimorfisme.
- The
@Override
anotasi mewajibkan programmer untuk menggunakan kaedah Pembatal; jika tidak, akan berlaku ralat penyusun. - Polimorfisme boleh digunakan dengan kelas normal, kelas abstrak, dan antara muka.
- Sebilangan besar corak reka bentuk bergantung pada beberapa bentuk polimorfisme.
- Satu-satunya cara untuk menggunakan kaedah tertentu dalam subkelas polimorfik anda adalah dengan menggunakan pemutus.
- Anda boleh merancang struktur yang kuat dalam kod anda menggunakan polimorfisme.
- Jalankan ujian anda. Dengan melakukan ini, anda akan dapat menguasai konsep hebat ini!
Kunci jawapan
Jawapan kepada pencabar Java ini adalah D . Hasilnya adalah:
I love Sax! Eat my shorts! Simpson! D'oh Knock Homer down
Kisah ini, "Polimorfisme dan warisan di Jawa" awalnya diterbitkan oleh JavaWorld.