Polimorfisme dan pewarisan di Jawa

Menurut legenda Venkat Subramaniam, polimorfisme adalah konsep terpenting dalam pemrograman berorientasi objek. Polimorfisme - atau kemampuan suatu objek untuk mengeksekusi tindakan khusus berdasarkan tipenya - adalah yang membuat kode Java menjadi fleksibel. Pola desain seperti Command, Observer, Decorator, Strategy, dan banyak lainnya yang dibuat oleh Gang Of Four, semuanya menggunakan beberapa bentuk polimorfisme. Menguasai konsep ini sangat meningkatkan kemampuan Anda untuk memikirkan solusi untuk tantangan pemrograman.

Dapatkan kodenya

Anda bisa mendapatkan kode sumber untuk tantangan ini dan menjalankan pengujian Anda sendiri di sini: //github.com/rafadelnero/javaworld-challengers

Antarmuka dan pewarisan dalam polimorfisme

Dengan Java Challenger ini, kami berfokus pada hubungan antara polimorfisme dan pewarisan. Hal utama yang harus diingat adalah bahwa polimorfisme membutuhkan pewarisan atau implementasi antarmuka . Anda dapat melihat ini pada contoh di bawah, 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(); } } 

Output dari kode ini adalah:

 Punch! Fly! 

Karena penerapan spesifiknya, baik tindakan Dukemaupun Juggytindakan akan dijalankan.

Apakah metode overloading polimorfisme?

Banyak programmer bingung tentang hubungan polimorfisme dengan metode overriding dan metode overloading. Faktanya, hanya metode yang menimpa polimorfisme sejati. Overloading memiliki nama metode yang sama tetapi parameternya berbeda. Polimorfisme adalah istilah yang luas, jadi akan selalu ada diskusi tentang topik ini.

Apa tujuan polimorfisme?

Keuntungan dan tujuan besar menggunakan polimorfisme adalah untuk memisahkan kelas klien dari kode implementasi. Alih-alih menggunakan hard-code, kelas klien menerima implementasi untuk menjalankan tindakan yang diperlukan. Dengan cara ini, kelas klien cukup tahu untuk menjalankan aksinya, yang merupakan contoh kopling longgar.

Untuk lebih memahami tujuan polimorfisme, lihat 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 bahwa SweetCreatorkelas tersebut hanya mengetahui  SweetProducer kelas tersebut. Itu tidak tahu implementasi masing-masing Sweet. Pemisahan itu memberi kita fleksibilitas untuk memperbarui dan menggunakan kembali kelas kita, dan itu membuat kode lebih mudah dipelihara. Saat mendesain kode Anda, selalu cari cara untuk membuatnya sefleksibel dan mudah dipelihara. polimorfisme adalah teknik yang sangat ampuh untuk digunakan untuk tujuan ini.

Tip : @OverrideAnotasi mewajibkan programmer untuk menggunakan tanda tangan metode yang sama yang harus diganti. Jika metode ini tidak diganti, akan ada kesalahan kompilasi.

Jenis pengembalian kovarian dalam penggantian metode

Dimungkinkan untuk mengubah jenis kembalian dari metode yang diganti jika itu adalah jenis kovarian. Sebuah jenis kovarian pada dasarnya adalah subclass dari jenis kembali. Perhatikan sebuah contoh:

 public abstract class JavaMascot { abstract JavaMascot getMascot(); } public class Duke extends JavaMascot { @Override Duke getMascot() { return new Duke(); } } 

Karena Dukea JavaMascot, kita bisa mengubah tipe kembalian saat menimpa.

Polimorfisme dengan kelas-kelas inti Java

Kami menggunakan polimorfisme sepanjang waktu di kelas inti Java. Salah satu contoh yang sangat sederhana adalah ketika kita memberi contoh ArrayListkelas yang menyatakan   Listantarmuka sebagai tipe:

 List list = new ArrayList(); 

Untuk melangkah lebih jauh, pertimbangkan contoh kode 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()); } 

Kode jelek, bukan? Bayangkan mencoba mempertahankannya! 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 fleksibilitas dan ekstensibilitas. Daripada membuat beberapa metode berbeda, kita bisa mendeklarasikan hanya satu metode yang menerima Listtipe generik .

Memanggil metode tertentu dalam panggilan metode polimorfik

Dimungkinkan untuk memanggil metode tertentu dalam panggilan polimorfik, tetapi melakukannya akan mengorbankan fleksibilitas. Berikut 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 mentransmisikan , atau dengan sengaja mengubah jenis objek saat runtime.

Perhatikan bahwa mungkin untuk memanggil metode tertentu hanya saat mentransmisikan tipe generik ke tipe tertentu. Sebuah analogi yang baik akan mengatakan secara eksplisit kepada kompiler, "Hei, saya tahu apa yang saya lakukan di sini, jadi saya akan mentransmisikan objek ke jenis tertentu dan menggunakan metode tertentu."  

Mengacu pada contoh di atas, ada alasan penting mengapa compiler menolak untuk menerima pemanggilan metode tertentu: kelas yang diteruskan bisa jadi SolidSnake. Dalam kasus ini, tidak ada cara bagi kompilator untuk memastikan setiap subkelas MetalGearCharactermemiliki giveOrderToTheArmymetode yang dideklarasikan.

Kata instanceofkunci yang dipesan

Perhatikan kata yang dipesan instanceof. Sebelum menjalankan metode tertentu, kami telah menanyakan apakah MetalGearCharacteradalah " instanceof" BigBoss. Jika itu bukan sebuah BigBosscontoh, kami akan menerima pesan 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 superkunci yang dipesan

Bagaimana jika kita ingin mereferensikan atribut atau metode dari superclass Java? Dalam hal ini kita bisa menggunakan superkata yang dipesan. 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.

Yang perlu diingat tentang polimorfisme

  • Instance yang dibuat akan menentukan metode apa yang akan dipanggil saat menggunakan polimorfisme.
  • The @Overridepenjelasan mewajibkan programmer untuk menggunakan metode ditimpa; jika tidak, akan ada kesalahan kompilator.
  • Polimorfisme dapat digunakan dengan kelas normal, kelas abstrak, dan antarmuka.
  • Kebanyakan pola desain bergantung pada beberapa bentuk polimorfisme.
  • Satu-satunya cara untuk menggunakan metode tertentu dalam subkelas polimorfik Anda adalah dengan menggunakan transmisi.
  • Anda dapat mendesain struktur yang kuat dalam kode Anda menggunakan polimorfisme.
  • Jalankan pengujian Anda. Dengan melakukan ini, Anda akan dapat menguasai konsep yang kuat ini!

Kunci jawaban

Jawaban untuk penantang Java ini D . Outputnya adalah:

 I love Sax! Eat my shorts! Simpson! D'oh Knock Homer down 

Cerita, "Polimorfisme dan Warisan di Jawa" ini awalnya diterbitkan oleh JavaWorld.