Programmering

Polymorfisme og arv på Java

I følge legenden Venkat Subramaniam er polymorfisme det viktigste begrepet innen objektorientert programmering. Polymorfisme--eller et objekts evne til å utføre spesialiserte handlinger basert på sin type - er det som gjør Java-koden fleksibel. Designmønstre som Command, Observer, Decorator, Strategy og mange andre opprettet av Gang Of Four, bruker alle noen form for polymorfisme. Å mestre dette konseptet forbedrer din evne til å tenke gjennom løsninger på programmeringsutfordringer.

Få koden

Du kan få kildekoden for denne utfordringen og kjøre dine egne tester her: //github.com/rafadelnero/javaworld-challengers

Grensesnitt og arv i polymorfisme

Med denne Java Challenger fokuserer vi på forholdet mellom polymorfisme og arv. Det viktigste å huske på er at polymorfisme krever arv eller grensesnittimplementering. Du kan se dette i eksemplet nedenfor, med Duke og Juggy:

 offentlig abstrakt klasse JavaMascot {offentlig abstrakt ugyldig executeAction (); } offentlig klasse Duke utvider JavaMascot {@Override public void executeAction () {System.out.println ("Punch!"); }} offentlig klasse Juggy utvider JavaMascot {@Override public void executeAction () {System.out.println ("Fly!"); }} JavaMascotTest i offentlig klasse {public static void main (String ... args) {JavaMascot dukeMascot = new Duke (); JavaMascot juggyMascot = ny Juggy (); dukeMascot.executeAction (); juggyMascot.executeAction (); }} 

Resultatet fra denne koden vil være:

 Slå! Fly! 

På grunn av deres spesifikke implementeringer, begge deler hertug og JuggySine handlinger vil bli utført.

Er metode overbelastning av polymorfisme?

Mange programmerere er forvirret om forholdet mellom polymorfisme og metodeoverstyring og metodeoverbelastning. Faktisk er det bare metodeoverstyring som er ekte polymorfisme. Overbelastning deler samme metodes navn, men parametrene er forskjellige. Polymorfisme er et bredt begrep, så det vil alltid være diskusjoner om dette emnet.

Hva er hensikten med polymorfisme?

Den store fordelen og hensikten med å bruke polymorfisme er å koble klientklassen fra implementeringskoden. I stedet for å være hardkodet, mottar klientklassen implementeringen for å utføre den nødvendige handlingen. På denne måten vet klientklassen akkurat nok til å utføre sine handlinger, som er et eksempel på løs kobling.

For å bedre forstå formålet med polymorfisme, ta en titt på SweetCreator:

 offentlig abstrakt klasse SweetProducer {offentlig abstrakt ugyldig produceSweet (); } offentlig klasse CakeProducer utvider SweetProducer {@Override offentlig ugyldig produceSweet () {System.out.println ("Kake produsert"); }} offentlig klasse ChocolateProducer utvider SweetProducer {@Override offentlig ugyldig produceSweet () {System.out.println ("Sjokolade produsert"); }} offentlig klasse CookieProducer utvider SweetProducer {@Override offentlig ugyldig produceSweet () {System.out.println ("Cookie produsert"); }} offentlig klasse SweetCreator {privat liste sweetProducer; offentlig SweetCreator (liste sweetProducer) {this.sweetProducer = sweetProducer; } offentlig tomrom createSweets () {sweetProducer.forEach (sweet -> sweet.produceSweet ()); }} offentlig klasse SweetCreatorTest {public static void main (String ... args) {SweetCreator sweetCreator = new SweetCreator (Arrays.asList (new CakeProducer (), new ChocolateProducer (), new CookieProducer ())); sweetCreator.createSweets (); }} 

I dette eksemplet kan du se at SweetCreator klassen bare vet  SweetProducer klasse. Den vet ikke implementeringen av hver Søt. Denne separasjonen gir oss fleksibilitet til å oppdatere og gjenbruke klassene våre, og det gjør koden mye enklere å vedlikeholde. Når du designer koden din, må du alltid se etter måter å gjøre den så fleksibel og vedlikeholdbar som mulig. polymorfisme er en veldig kraftig teknikk å bruke til disse formålene.

Tips: The @Overstyring kommentar forplikter programmereren til å bruke den samme metodesignaturen som må overstyres. Hvis metoden ikke overstyres, vil det være en kompileringsfeil.

Kovariante returtyper i metodeoverstyring

Det er mulig å endre returtype for en overstyrt metode hvis det er en samvariant. EN kovariant type er i utgangspunktet en underklasse av returtypen. Tenk på et eksempel:

 offentlig abstrakt klasse JavaMascot {abstrakt JavaMascot getMascot (); } offentlig klasse Duke utvider JavaMascot {@Override Duke getMascot () {return new Duke (); }} 

Fordi hertug er en JavaMascot, kan vi endre returtype når vi overstyrer.

Polymorfisme med de viktigste Java-klassene

Vi bruker polymorfisme hele tiden i de viktigste Java-klassene. Et veldig enkelt eksempel er når vi instantierer ArrayList klasse som erklærerListe grensesnitt som type:

 Listeliste = ny ArrayList (); 

Hvis du vil gå videre, bør du vurdere dette kodeeksemplet ved hjelp av Java Collections API uten polymorfisme:

 public class ListActionWithoutPolymorphism {// Eksempel uten polymorfisme void executeVectorActions (Vector vector) {/ * Code repetition here * /} void executeArrayListActions (ArrayList arrayList) {/ * Code repetition here * /} void executeLinkedListActions (LinkedList linkedList) repetisjon) her * /} ugyldig executeCopyOnWriteArrayListActions (CopyOnWriteArrayList copyOnWriteArrayList) {/ * Kode gjentakelse her * /}} offentlig klasse ListActionInvokerWithoutPolymorphism {listAction.executeVectorActions (new Vector ()); listAction.executeArrayListActions (ny ArrayList ()); listAction.executeLinkedListActions (ny LinkedList ()); listAction.executeCopyOnWriteArrayListActions (ny CopyOnWriteArrayList ()); } 

Stygg kode, er det ikke? Tenk deg å prøve å opprettholde det! Se nå på det samme eksemplet med polymorfisme:

 public static void main (String ... polymorphism) {ListAction listAction = new ListAction (); listAction.executeListActions (); } public class ListAction {void executeListActions (List list) {// Utfør handlinger med forskjellige lister}} public class ListActionInvoker {public static void main (String ... masterPolymorphism) {ListAction listAction = new ListAction (); listAction.executeListActions (ny Vector ()); listAction.executeListActions (ny ArrayList ()); listAction.executeListActions (ny LinkedList ()); listAction.executeListActions (ny CopyOnWriteArrayList ()); }} 

Fordelen med polymorfisme er fleksibilitet og utvidbarhet. I stedet for å lage flere forskjellige metoder, kan vi kun erklære én metode som mottar generikken Liste type.

Påkalle spesifikke metoder i en polymorf metode

Det er mulig å påkalle spesifikke metoder i en polymorf samtale, men å gjøre det kommer på bekostning av fleksibilitet. Her er et eksempel:

 offentlig abstrakt klasse MetalGearCharacter {abstrakt ugyldig brukWeapon (strengvåpen); } offentlig klasse BigBoss utvider MetalGearCharacter {@ Override ugyldig useWeapon (strengvåpen) {System.out.println ("Big Boss bruker et" + våpen); } ugyldig giveOrderToTheArmy (String orderMessage) {System.out.println (orderMessage); }} offentlig klasse SolidSnake utvider MetalGearCharacter {void useWeapon (String våpen) {System.out.println ("Solid Snake bruker et" + våpen); }} offentlig klasse UseSpecificMethod {public static void executeActionWith (MetalGearCharacter metalGearCharacter) {metalGearCharacter.useWeapon ("SOCOM"); // Linjen nedenfor fungerer ikke // metalGearCharacter.giveOrderToTheArmy ("Attack!"); if (metalGearCharacter instance of BigBoss) {((BigBoss) metalGearCharacter) .giveOrderToTheArmy ("Attack!"); }} public static void main (String ... specificPolymorphismInvocation) {executeActionWith (new SolidSnake ()); executeActionWith (nye BigBoss ()); }} 

Teknikken vi bruker her er avstøpning, eller med vilje endre objekttypen ved kjøretid.

Vær oppmerksom på at det er mulig å påkalle en bestemt metode kun når du støper den generiske typen til den spesifikke typen. En god analogi vil være å si eksplisitt til kompilatoren: "Hei, jeg vet hva jeg gjør her, så jeg skal kaste objektet til en bestemt type og bruke en bestemt metode."

Med henvisning til eksemplet ovenfor er det en viktig grunn til at kompilatoren nekter å akseptere spesifikk metodeinnkalling: klassen som blir bestått kan være Solid slange. I dette tilfellet er det ingen måte for kompilatoren å sikre hver underklasse av MetalGearCharacter har giOrderToTheArmy metoden erklært.

De tilfelle av reservert nøkkelord

Vær oppmerksom på det reserverte ordet tilfelle av. Før vi påkaller den spesifikke metoden, har vi spurt om MetalGearCharacter er "tilfelle avStor sjef. Hvis det var ikke en Stor sjef For eksempel vil vi motta følgende unntaksmelding:

 Unntak i tråden "hoved" java.lang.ClassCastException: com.javaworld.javachallengers.polymorphism.specificinvocation.SolidSnake kan ikke kastes til com.javaworld.javachallengers.polymorphism.specificinvocation.BigBoss 

De super reservert nøkkelord

Hva om vi ønsket å referere til en attributt eller metode fra en Java-superklasse? I dette tilfellet kan vi bruke super reservert ord. For eksempel:

 offentlig klasse JavaMascot {void executeAction () {System.out.println ("Java Mascot er i ferd med å utføre en handling!"); }} offentlig klasse Duke utvider JavaMascot {@Override void executeAction () {super.executeAction (); System.out.println ("Duke kommer til å slå!"); } public static void main (String ... superReservedWord) {new Duke (). executeAction (); }} 

Bruke det reserverte ordet super i hertug’S executeAction metoden påkaller superklassemetoden. Vi utfører deretter den spesifikke handlingen fra hertug. Derfor kan vi se begge meldingene i utdataene nedenfor:

 Java Mascot er i ferd med å utføre en handling! Duke kommer til å slå! 

Ta polymorfismeutfordringen!

La oss prøve det du har lært om polymorfisme og arv. I denne utfordringen får du en håndfull metoder fra Matt Groening's The Simpsons, og utfordringen din er å utlede hva resultatet for hver klasse vil være. For å starte, analyser følgende kode nøye:

 offentlig klasse PolymorphismChallenge {statisk abstrakt klasse Simpson {void talk () {System.out.println ("Simpson!"); } beskyttet ugyldig prank (String prank) {System.out.println (prank); }} statisk klasse Bart utvider Simpson {String prank; Bart (String-prank) {this.prank = prank; } beskyttet void talk () {System.out.println ("Spis shortsen min!"); } beskyttet ugyldig prank () {super.prank (prank); System.out.println ("Knock Homer down"); }} statisk klasse Lisa utvider Simpson {void talk (String toMe) {System.out.println ("Jeg elsker Sax!"); }} public static void main (String ... doYourBest) {new Lisa (). talk ("Sax :)"); Simpson simpson = nye Bart ("D'oh"); simpson.talk (); Lisa lisa = nye Lisa (); lisa.talk (); ((Bart) simpson). Prank (); }} 

Hva tror du? Hva blir den endelige produksjonen? Ikke bruk en IDE for å finne ut av dette! Poenget er å forbedre kodeanalyseferdighetene dine, så prøv å bestemme utdataene selv.

Velg svaret ditt, så finner du riktig svar nedenfor.

 A) Jeg elsker Sax! D'oh Simpson! D'oh B) Sax :) Spis shortsen min! Jeg elsker Sax! D'oh banker Homer ned C) Sax :) D'oh Simpson! Slå Homer ned D) Jeg elsker Sax! Spis min shorts! Simpson! D'oh slår ned Homer 

Hva skjedde nå? Forstå polymorfisme

For følgende metodeinnkalling:

 nye Lisa (). snakk ("Sax :)"); 

utgangen vil være “Jeg elsker Sax!”Dette er fordi vi passerer en String til metoden og Lisa har metoden.

For neste påkallelse:

 Simpson simpson = nye Bart ("D'oh");

simpson.talk ();

Resultatet blir "Spis min shorts!"Dette er fordi vi instantierer Simpson skriv med Bart.

Sjekk nå denne, som er litt vanskeligere:

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

Her bruker vi metodeoverbelastning med arv. Vi gir ikke noe til samtalemetoden, og det er derfor Simpson snakke metoden påberopes. I dette tilfellet vil utgangen være:

 "Simpson!" 

Her er en til:

 ((Bart) simpson). Prank (); 

I dette tilfellet prank String ble bestått da vi instantiserte Bart klasse med nye Bart ("D'oh");. I dette tilfellet, først super.prank metoden vil bli påkalt, etterfulgt av den spesifikke spøk metode fra Bart. Resultatet blir:

 "D'oh" "Slå Homer ned" 

Videoutfordring! Feilsøking av Java polymorfisme og arv

Feilsøking er en av de enkleste måtene å fullt ut absorbere programmeringskonsepter samtidig som du forbedrer koden din. I denne videoen kan du følge med mens jeg feilsøker og forklarer Java polymorfisme-utfordringen:

Vanlige feil med polymorfisme

Det er en vanlig feil å tro at det er mulig å påkalle en bestemt metode uten å bruke casting.

En annen feil er å være usikker på hvilken metode som vil bli påkalt når man starter en klasse polymorf. Husk at metoden som skal påberopes er metoden for den opprinnelige forekomsten.

Husk også at metodeoverstyring ikke er metodeoverbelastning.

Det er umulig å overstyre en metode hvis parametrene er forskjellige. Den er mulig for å endre returtypen for den overstyrte metoden hvis returtypen er en underklasse av superklassemetoden.

Hva du skal huske på polymorfisme

  • Den opprettede forekomsten vil avgjøre hvilken metode som skal påberopes når du bruker polymorfisme.
  • De @Overstyring kommentar forplikter programmereren til å bruke en overstyrt metode; hvis ikke, vil det være en kompilatorfeil.
  • Polymorfisme kan brukes med normale klasser, abstrakte klasser og grensesnitt.
  • De fleste designmønstre avhenger av en eller annen form for polymorfisme.
  • Den eneste måten å bruke en bestemt metode i din polymorfe underklasse er å bruke støping.
  • Det er mulig å designe en kraftig struktur i koden din ved hjelp av polymorfisme.
  • Kjør testene dine. Gjør du dette, vil du kunne mestre dette kraftige konseptet!

Fasit

Svaret på denne Java-utfordreren er D. Resultatet vil være:

 Jeg elsker Sax! Spis min shorts! Simpson! D'oh slår ned Homer 

Denne historien, "Polymorfisme og arv i Java" ble opprinnelig utgitt av JavaWorld.

$config[zx-auto] not found$config[zx-overlay] not found