Java Challengers # 3: Polimorfisme dan Warisan

Java Challengers # 3: Polimorfisme dan Warisan


Kami terus menerjemahkan serangkaian artikel dengan masalah Java. Posting terakhir tentang dialog menyebabkan diskusi yang mengejutkan. Kami harap Anda tidak akan melewati artikel ini juga. Dan ya - kami sekarang mengundang Anda ke aliran peringatan sepuluh tahun kursus Java Developer kami.


Menurut Venkat Subramaniam yang legendaris , polimorfisme adalah konsep terpenting dalam pemrograman berorientasi objek. Polimorfisme - atau kemampuan suatu objek untuk melakukan tindakan khusus berdasarkan jenisnya - adalah apa yang membuat kode Java fleksibel. Pola desain seperti Command, the Observer, the Decorator, the Strategy, dan banyak lainnya yang dibuat oleh Gang of Four semuanya menggunakan beberapa bentuk polimorfisme. Menguasai konsep ini akan sangat meningkatkan kemampuan Anda untuk memikirkan solusi perangkat lunak.



Anda dapat mengambil kode sumber untuk artikel ini dan bereksperimen di sini: https://github.com/rafadelnero/javaworld-challengers


Antarmuka dan warisan dalam polimorfisme


Dalam artikel ini, kita akan fokus pada hubungan antara polimorfisme dan pewarisan. Hal utama yang perlu diingat adalah bahwa polimorfisme membutuhkan pewarisan atau implementasi antarmuka . Anda dapat melihat ini dalam contoh di bawah ini dengan 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 akan seperti ini:


 Punch! Fly! 

Karena implementasi spesifik didefinisikan, metode Duke dan Juggy akan dipanggil.


Apakah metode kelebihan polimorfisme? Banyak programmer yang mengacaukan hubungan polimorfisme dengan overriding dan overloading . Faktanya, hanya mendefinisikan ulang metode tersebut adalah polimorfisme sejati. Overloading menggunakan nama metode yang sama, tetapi parameternya berbeda. Polimorfisme adalah istilah yang luas, sehingga akan selalu ada diskusi tentang topik ini.


Apa tujuan dari polimorfisme?


Keuntungan besar dan tujuan menggunakan polimorfisme adalah untuk mengurangi koneksi kelas klien dengan implementasi. Alih-alih hardcode, kelas klien mendapatkan implementasi ketergantungan untuk melakukan tindakan yang diperlukan. Dengan demikian, kelas klien tahu minimum untuk menyelesaikan tindakannya, yang merupakan contoh dari ikatan yang lemah.


Untuk lebih memahami tujuan polimorfisme, lihatlah 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> sweetProducer; public SweetCreator(List<SweetProducer> 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 bisa melihat bahwa kelas SweetCreator tahu tentang kelas SweetProducer . Dia tidak tahu implementasi setiap Sweet . Pemisahan ini memberi kami fleksibilitas untuk memperbarui dan menggunakan kembali kelas kami, dan ini membuat kode lebih mudah untuk dipelihara. Saat merancang kode, selalu cari cara untuk membuatnya sefleksibel dan senyaman mungkin. Polimorfisme adalah cara yang sangat kuat untuk digunakan untuk tujuan ini.


@Override mengharuskan pemrogram untuk menggunakan metode tanda tangan yang sama, yang harus diganti. Jika metode ini tidak diganti, akan ada kesalahan kompilasi.

Jenis kembalinya kembalinya ketika mengganti metode


Anda bisa mengubah tipe pengembalian dari metode yang ditimpa jika itu adalah tipe kovarian . Jenis kovarian pada dasarnya adalah subkelas dari nilai kembali.


Pertimbangkan sebuah contoh:


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

Karena Duke adalah JavaMascot , kita dapat mengubah jenis nilai pengembalian saat mengganti.


Polimorfisme di Kelas Basis Jawa


Kami terus menggunakan polimorfisme di kelas dasar Java. Salah satu contoh yang sangat sederhana adalah untuk instantiate kelas ArrayList dengan deklarasi tipe sebagai antarmuka List .


 List<String> list = new ArrayList<>(); 

Pertimbangkan kode sampel yang menggunakan Java Collections API tanpa polimorfisme:


 public class ListActionWithoutPolymorphism { //    void executeVectorActions(Vector<Object> vector) {/*    */} void executeArrayListActions(ArrayList<Object> arrayList) {/*    */} void executeLinkedListActions(LinkedList<Object> linkedList) {/*    */} void executeCopyOnWriteArrayListActions(CopyOnWriteArrayList<Object> copyOnWriteArrayList) { /*    */} } public class ListActionInvokerWithoutPolymorphism { listAction.executeVectorActions(new Vector<>()); listAction.executeArrayListActions(new ArrayList<>()); listAction.executeLinkedListActions(new LinkedList<>()); listAction.executeCopyOnWriteArrayListActions(new CopyOnWriteArrayList<>()); } 

Kode menjijikkan, kan? Bayangkan Anda perlu menemaninya! Sekarang perhatikan contoh yang sama dengan polimorfisme:


 public static void main(String... polymorphism) { ListAction listAction = new ListAction(); listAction.executeListActions(); } public class ListAction { void executeListActions(List<Object> list) { //      } } 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<>()); } } 

Keuntungan dari polimorfisme adalah fleksibilitas dan ekstensibilitas. Alih-alih membuat beberapa metode yang berbeda, kita dapat mendeklarasikan satu metode yang mendapatkan tipe List .


Memanggil metode spesifik untuk metode polimorfik


Anda dapat memanggil metode tertentu dengan panggilan metode polimorfik, ini karena fleksibilitas. Berikut ini sebuah contoh:


 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"); //      // 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 secara sadar mengubah jenis objek saat runtime.


Harap perhatikan bahwa panggilan ke metode tertentu hanya mungkin dilakukan saat melakukan casting tipe yang lebih umum ke tipe yang lebih spesifik. Sebuah analogi yang baik adalah memberi tahu kompiler secara eksplisit: "Hei, saya tahu apa yang saya lakukan di sini, jadi saya akan melemparkan objek ke tipe tertentu dan saya akan menggunakan metode ini."


Mengacu pada contoh di atas, kompiler memiliki alasan yang baik untuk tidak menerima panggilan metode tertentu: kelas yang dilewati harus SolidSnake . Dalam hal ini, kompiler tidak memiliki cara untuk memastikan bahwa setiap subkelas dari MetalGearCharacter memiliki metode giveOrderToTheArmy .


Contoh kata kunci


Perhatikan instanceof kata yang dicadangkan dari. Sebelum memanggil metode tertentu, kami bertanya apakah MetalGearCharacter turunan dari BigBoss . Jika ini bukan instance dari BigBoss , kami akan mendapatkan 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 kunci super


Bagaimana jika kita ingin merujuk ke atribut atau metode dari kelas induk? Dalam hal ini, kita dapat menggunakan kata kunci super .
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(); } } 

Menggunakan kata super dilindungi dalam metode executeAction dari kelas Duke memanggil metode dari kelas induk. Kemudian kami melakukan tindakan spesifik dari kelas Duke . Inilah sebabnya kami dapat melihat kedua pesan di output:


 The Java Mascot is about to execute an action! Duke is going to punch! 

Selesaikan masalah polimorfisme


Mari kita periksa apa yang Anda pelajari tentang polimorfisme dan pewarisan.


Dalam tugas ini, Anda diberikan beberapa metode dari The Simpsons karya Matt Groening, dari Wavs Anda diminta untuk menguraikan apa output yang akan dihasilkan untuk setiap kelas. Pertama, hati-hati menganalisis kode berikut:


 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(); } } 

Apa yang kamu pikirkan Apa hasilnya? Jangan gunakan IDE untuk mencari tahu! Tujuannya adalah untuk meningkatkan keterampilan analisis kode Anda, jadi cobalah putuskan sendiri.


Pilih jawaban Anda (Anda dapat menemukan jawaban yang benar di akhir artikel).


A)
Saya suka Sax!
Doh
Simpson!
Doh


B)
Sax :)
Makan celana pendek saya!
Saya suka Sax!
Doh
Hancurkan homer


C)
Sax :)
Doh
Simpson!
Hancurkan homer


D)
Saya suka Sax!
Makan celana pendek saya!
Simpson!
Doh
Hancurkan homer


Apa yang terjadi Memahami polimorfisme


Untuk panggilan metode berikut:


 new Lisa().talk("Sax :)"); 

keluarannya adalah "I love Sax!". Ini karena kita meneruskan string ke metode dan kelas Lisa memiliki metode seperti itu.


Untuk panggilan selanjutnya:


 Simpson simpson = new Bart("D'oh"); simpson.talk(); 

Outputnya adalah "Makan celana pendek saya!". Ini karena kita menginisialisasi tipe Simpson dengan Bart .


Sekarang lihat, ini sedikit rumit:


 Lisa lisa = new Lisa(); lisa.talk(); 

Di sini kita menggunakan metode overloading dengan pewarisan. Kami tidak meneruskan apa pun ke metode talk , jadi metode talk dari Simpson dipanggil.


Dalam hal ini, outputnya adalah "Simpson!".


Ini satu lagi:


 ((Bart) simpson).prank(); 

Dalam hal ini, string prank dilewatkan ketika membuat instance kelas Bart melalui new Bart("D'oh"); . Dalam hal ini, metode super.prank() dipanggil terlebih dahulu, dan kemudian metode prank() dari kelas Bart dipanggil. Kesimpulannya adalah:


 "D'oh" "Knock Homer down" 

Kesalahan polimorfisme umum


Kesalahan umum adalah berpikir bahwa Anda dapat memanggil metode tertentu tanpa menggunakan tipe gips.


Kesalahan lain adalah ketidakpastian tentang metode mana yang akan dipanggil ketika secara polimorfik instantiating kelas. Ingat bahwa metode yang dipanggil adalah metode dari instance yang dipakai.


Juga ingat bahwa mengganti metode bukan merupakan kelebihan metode.


Tidak dapat mengganti metode jika parameternya berbeda. Anda bisa mengubah tipe pengembalian dari metode yang diganti jika jenis kembali adalah subkelas.


Apa yang perlu Anda ingat tentang polimorfisme


  • Instance yang dibuat menentukan metode mana yang akan dipanggil ketika menggunakan polimorfisme.


  • @Override mengharuskan programmer untuk menggunakan metode yang diganti; jika tidak, kesalahan kompiler akan terjadi.


  • Polimorfisme dapat digunakan dengan kelas reguler, kelas abstrak, dan antarmuka.


  • Kebanyakan pola desain tergantung pada beberapa bentuk polimorfisme.


  • Satu-satunya cara untuk memanggil metode Anda dalam subkelas polimorfik adalah dengan menggunakan tipe casting.


  • Anda dapat membuat struktur kode yang kuat menggunakan polimorfisme.


  • Eksperimen. Melalui ini, Anda akan dapat menguasai konsep kuat ini!



Jawabannya


Jawabannya adalah D.


Kesimpulannya adalah:


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

Seperti biasa, saya menyambut komentar dan pertanyaan Anda. Dan kami sedang menunggu Vitaly dalam pelajaran terbuka .

Source: https://habr.com/ru/post/id429120/


All Articles