Polimorfizam i nasljeđivanje u Javi

Prema legendi Venkat Subramaniam, polimorfizam je najvažniji pojam u objektno orijentiranom programiranju. Polimorfizam - ili sposobnost objekta da izvršava specijalizirane radnje na temelju svoje vrste - ono je što Java kod čini fleksibilnim. Uzorci dizajna poput Command, Observer, Decorator, Strategy i mnogi drugi koje je stvorila Gang Of Four, svi koriste neki oblik polimorfizma. Ovladavanje ovim konceptom uvelike poboljšava vašu sposobnost razmišljanja kroz rješenja programskih izazova.

Uzmi kod

Izvorni kod za ovaj izazov možete dobiti i pokrenuti vlastite testove ovdje: //github.com/rafadelnero/javaworld-challengers

Sučelja i nasljeđivanje u polimorfizmu

Ovim Java Challengerom usredotočujemo se na odnos polimorfizma i nasljeđivanja. Glavna stvar koju treba imati na umu jest da polimorfizam zahtijeva nasljeđivanje ili implementaciju sučelja . To možete vidjeti u primjeru ispod, uključujući Dukea i Juggyja:

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

Izlaz iz ovog koda bit će:

 Punch! Fly! 

Zbog svojih specifičnih implementacija, kako Dukei Juggy„s akcije će se izvršiti.

Preopterećuje li metoda polimorfizam?

Mnogi su programeri zbunjeni u vezi polimorfizma s nadjačavanjem metode i preopterećenjem metode. U stvari, jedino prevladavanje metode je istinski polimorfizam. Preopterećenje dijeli ime iste metode, ali parametri su različiti. Polimorfizam je širok pojam, pa će se o ovoj temi uvijek raspravljati.

Koja je svrha polimorfizma?

Velika prednost i svrha upotrebe polimorfizma je odvojiti klasu klijenta od implementacijskog koda. Umjesto da je teško kodiran, klasa klijenta prima implementaciju za izvršenje potrebne radnje. Na taj način, klijentska klasa zna dovoljno dovoljno da izvrši svoje radnje, što je primjer labavog spajanja.

Da biste bolje razumjeli svrhu polimorfizma, pogledajte 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(); } } 

U ovom primjeru možete vidjeti da SweetCreatorrazred poznaje samo  SweetProducer razred. Ne zna provedbu svake Sweet. To odvajanje daje nam fleksibilnost za ažuriranje i ponovnu upotrebu naših predavanja, a kôd čini mnogo lakšim za održavanje. Kada dizajnirate svoj kod, uvijek potražite načine da ga učinite što fleksibilnijim i održivijim. polimorfizam je vrlo moćna tehnika koja se koristi u ove svrhe.

Savjet : @OverrideBilješka obavezuje programera da koristi isti potpis metode koji mora biti nadjačan. Ako se metoda ne poništi, pojavit će se pogreška u kompilaciji.

Kovarijantni tipovi povrata u nadjačavanju metode

Moguće je promijeniti tip povrata nadjačane metode ako je to kovarijantni tip. Kovarijantna tip je u osnovi podrazred tipa povratka. Razmotrimo primjer:

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

Budući da Dukeje a JavaMascot, kad nadjačavamo, možemo promijeniti vrstu povrata.

Polimorfizam s jezgrom Java klasa

Stalno koristimo polimorfizam u osnovnim Java satovima. Jedan vrlo jednostavan primjer je kada instanciramo ArrayListklasu koja Listsučelje deklarira   kao tip:

 List list = new ArrayList(); 

Da idete dalje, razmotrite ovaj uzorak koda koristeći API Java Collections bez polimorfizma:

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

Ružna šifra, zar ne? Zamislite da to pokušavate održavati! Sada pogledajte isti primjer s polimorfizmom:

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

Prednost polimorfizma je fleksibilnost i rastezljivost. Umjesto stvaranja nekoliko različitih metoda, možemo proglasiti samo jednu metodu koja prima generički Listtip.

Pozivanje specifičnih metoda u pozivu polimorfne metode

Moguće je pozvati se na određene metode u polimorfnom pozivu, ali to se radi po cijenu fleksibilnosti. Evo primjera:

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

Tehnika koju ovdje koristimo je lijevanje ili namjerno mijenjanje vrste predmeta tijekom izvođenja.

Imajte na umu da je moguće pozvati određenu metodu samo prilikom lijevanja generičkog tipa na određeni tip. Dobra analogija bila bi izričita izjava kompajleru: "Hej, znam što radim ovdje, pa ću objekt prebaciti na određenu vrstu i koristiti određenu metodu."  

Pozivajući se na gornji primjer, postoji važan razlog zbog kojeg prevoditelj odbija prihvatiti pozivanje određene metode: klasa koja se prenosi može biti SolidSnake. U ovom slučaju, kompajler ne može osigurati da svaka podklasa od MetalGearCharacterima giveOrderToTheArmydeklariranu metodu.

instanceofPridržana ključne riječi

Obratite pažnju na rezerviranu riječ instanceof. Prije poziva na određenu metodu pitali smo je li MetalGearCharacter" instanceof" BigBoss. Ako to nije bioBigBoss primjer, mi bi dobiti sljedeću iznimka poruku:

 Exception in thread "main" java.lang.ClassCastException: com.javaworld.javachallengers.polymorphism.specificinvocation.SolidSnake cannot be cast to com.javaworld.javachallengers.polymorphism.specificinvocation.BigBoss 

superPridržana ključne riječi

Što ako bismo željeli uputiti atribut ili metodu iz Java superklase? U ovom bismo slučaju mogli upotrijebiti superrezerviranu riječ. Na primjer:

 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.

Što se sjetiti polimorfizma

  • Stvorena će instanca odrediti koja će se metoda pozvati kada se koristi polimorfizam.
  • @OverrideZapažanje obvezuje programer za korištenje metode nadjačati; ako ne, pojavit će se pogreška kompajlera.
  • Polimorfizam se može koristiti s normalnim klasama, apstraktnim klasama i sučeljima.
  • Većina dizajnerskih uzoraka ovisi o nekom obliku polimorfizma.
  • Jedini način na koji možete upotrijebiti određenu metodu u polimorfnoj podklasi je korištenje lijevanja.
  • Moguće je dizajnirati moćnu strukturu u vašem kodu koristeći polimorfizam.
  • Pokrenite svoje testove. Čineći to, moći ćete svladati ovaj snažni koncept!

Kljucni odgovor

Odgovor na ovo Java izazivač je D . Rezultat bi bio:

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

Ovu priču "Polimorfizam i nasljeđivanje na Javi" izvorno je objavio JavaWorld.