Ohjelmointi

Polymorfismi ja perintö Jaavassa

Legendan Venkat Subramaniam mukaan polymorfismi on olio-ohjelmoinnin tärkein käsite. Polymorfismi- tai kohteen kyky suorittaa erikoistoimintoja tyypin perusteella - tekee Java-koodista joustavan. Suunnittelumallit, kuten Command, Observer, Decorator, Strategy ja monet muut, jotka ovat muodostaneet Neljän jengi, käyttävät kaikki jonkinlaista polymorfismia. Tämän käsitteen hallitseminen parantaa suuresti kykyäsi ajatella ratkaisuja ohjelmointihaasteisiin.

Hanki koodi

Voit hankkia tämän haasteen lähdekoodin ja suorittaa omat testisi täältä: //github.com/rafadelnero/javaworld-challengers

Liitännät ja periytyminen polymorfismissa

Tämän Java Challengerin avulla keskitymme polymorfismin ja perinnöllisyyden väliseen suhteeseen. Tärkeintä on pitää mielessä, että polymorfismi vaatii perintö tai käyttöliittymän toteutus. Voit nähdä tämän alla olevasta esimerkistä, mukana Duke ja Juggy:

 julkinen abstrakti luokka JavaMascot {julkinen abstrakti void executeAction (); } public class Duke laajentaa JavaMascotia {@Override public void executeAction () {System.out.println ("Punch!"); }} public class Juggy laajentaa JavaMascotia {@Override public void executeAction () {System.out.println ("Fly!"); }} public class JavaMascotTest {public static void main (String ... args) {JavaMascot dukeMascot = new Duke (); JavaMascot juggyMascot = uusi Juggy (); dukeMascot.executeAction (); juggyMascot.executeAction (); }} 

Tämän koodin lähtö on:

 Booli! Lentää! 

Molempien erityisten toteutustensa vuoksi Duke ja JuggyToimet toteutetaan.

Kuormittako menetelmä polymorfismia?

Monet ohjelmoijat ovat hämmentyneitä polymorfismin suhteesta menetelmän ohittamiseen ja menetelmän ylikuormitukseen. Itse asiassa ainoa menetelmän ohittaminen on todellinen polymorfismi. Ylikuormituksella on sama menetelmä, mutta parametrit ovat erilaiset. Polymorfismi on laaja termi, joten tästä aiheesta keskustellaan aina.

Mikä on polymorfismin tarkoitus?

Polymorfismin käytön suuri etu ja tarkoitus on irrottaa asiakasluokka toteutuskoodista. Asiakasluokka saa kovakoodaamisen sijaan toteutuksen tarvittavien toimenpiteiden suorittamiseksi. Tällä tavalla asiakasluokka tietää vain tarpeeksi toimintojensa suorittamiseksi, mikä on esimerkki löysästä kytkennästä.

Jotta ymmärtäisit paremmin polymorfismin tarkoituksen, katso SweetCreator:

 julkinen abstrakti luokka SweetProducer {julkinen abstrakti void producSweet (); } julkisen luokan CakeProducer laajentaa SweetProducer {@Override public void producSweet () {System.out.println ("Kakku tuotettu"); }} julkisen luokan ChocolateProducer laajentaa SweetProducer {@Override public void producSweet () {System.out.println ("Suklaa tuotettu"); }} public class CookieProducer laajentaa SweetProducer {@Override public void producSweet () {System.out.println ("Eväste tuotettu"); }} julkinen luokka 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 (); }} 

Tässä esimerkissä näet, että SweetCreator luokka tietää vain  SweetProducer luokassa. Se ei tiedä kunkin toteutusta Makea. Tämä erottelu antaa meille joustavuutta päivittää ja käyttää luokitamme uudelleen, ja se tekee koodin ylläpidosta paljon helpompaa. Kun suunnittelet koodia, etsi aina tapoja tehdä siitä mahdollisimman joustava ja ylläpidettävä. polymorfismi on erittäin tehokas tekniikka, jota voidaan käyttää näihin tarkoituksiin.

Kärki: @Ohittaa merkintä velvoittaa ohjelmoijan käyttämään samaa menetelmän allekirjoitusta, joka on ohitettava. Jos menetelmää ei ohiteta, tapahtuu käännösvirhe.

Kovariaattiset palautustyypit menetelmän ohittamisessa

Ohitetun menetelmän palautustyyppi on mahdollista muuttaa, jos se on kovariaaninen tyyppi. A kovariaaninen tyyppi on pohjimmiltaan palautustyypin alaluokka. Harkitse esimerkkiä:

 julkinen abstrakti luokka JavaMascot {tiivistelmä JavaMascot getMascot (); } public class Duke laajentaa JavaMascotia {@Override Duke getMascot () {return new Duke (); }} 

Koska Duke on JavaMascot, voimme muuttaa palautustyyppiä ohitettaessa.

Polymorfismi Java-ydinluokkien kanssa

Käytämme polymorfismia koko ajan Java-ydinluokissa. Yksi hyvin yksinkertainen esimerkki on, kun me instantisoimme ArrayList luokka julistaaLista käyttöliittymä tyypinä:

 List list = new ArrayList (); 

Harkitse tätä koodinäytettä Java Collections -sovellusliittymän avulla ilman polymorfismi:

 public class ListActionWithoutPolymorphism {// Esimerkki ilman polymorfiaa void executeVectorActions (Vector vector) {/ * Koodin toistaminen tässä * /} void executeArrayListActions (ArrayList arrayList) {/ * Koodin toistaminen tässä * /} void executeLinkedListActions * LinkedList here * /} void executeCopyOnWriteArrayListActions (CopyOnWriteArrayList copyOnWriteArrayList) {/ * Koodin toistaminen tässä * /}} public class ListActionInvokerWithoutPolymorphism {listAction.executeVectorActions (uusi vektori ()); listAction.executeArrayListActions (uusi ArrayList ()); listAction.executeLinkedListActions (uusi LinkedList ()); listAction.executeCopyOnWriteArrayListActions (uusi CopyOnWriteArrayList ()); } 

Ruma koodi, eikö olekin? Kuvittele, että yrität ylläpitää sitä! Katsokaa nyt samaa esimerkkiä kanssa polymorfismi:

 public static void main (String ... polymorphism) {ListAction listAction = uusi ListAction (); listAction.executeListActions (); } public class ListAction {void executeListActions (List list) {// Suorita toiminnot eri luetteloilla}} public class ListActionInvoker {public static void main (String ... masterPolymorphism) {ListAction listAction = new ListAction (); listAction.executeListActions (uusi vektori ()); listAction.executeListActions (uusi ArrayList ()); listAction.executeListActions (uusi LinkedList ()); listAction.executeListActions (uusi CopyOnWriteArrayList ()); }} 

Polymorfismin etuna on joustavuus ja venyvyys. Useiden erilaisten menetelmien luomisen sijasta voimme julistaa vain yhden menetelmän, joka vastaanottaa yleisen Lista tyyppi.

Tiettyjen menetelmien kutsuminen polymorfiseen menetelmäkutsuun

Polymorfisessa puhelussa on mahdollista vedota tiettyihin menetelmiin, mutta sen tekeminen tulee joustavuuden kustannuksella. Tässä on esimerkki:

 julkinen abstrakti luokka MetalGearCharacter {abstrakti void useWeapon (merkkijonoase); } julkisen luokan BigBoss laajentaa MetalGearCharacter {@Override void useWeapon (String ase) {System.out.println ("Big Boss käyttää" + asetta); } void giveOrderToTheArmy (Merkkijono orderMessage) {System.out.println (orderMessage); }} julkisen luokan SolidSnake laajentaa MetalGearCharacter {void useWeapon (String-ase) {System.out.println ("Solid Snake käyttää" + -asetta); }} public class UseSpecificMethod {public static void executeActionWith (MetalGearCharacter metalGearCharacter) {metalGearCharacter.useWeapon ("SOCOM"); // Alla oleva rivi ei toimisi // metalGearCharacter.giveOrderToTheArmy ("Hyökkäys!"); if (metalGearCharacter esimerkki BigBossista) {((BigBoss) metalGearCharacter) .giveOrderToTheArmy ("Hyökkäys!"); }} public static void main (String ... specificPolymorphismInvocation) {executeActionWith (uusi SolidSnake ()); executeActionWith (uusi BigBoss ()); }} 

Tässä käytettävä tekniikka on valutai tarkoituksella muuttaa objektityyppiä ajon aikana.

Huomaa, että on mahdollista käyttää tiettyä menetelmää vain kun heitetään yleinen tyyppi tietylle tyypille. Hyvä analogia olisi sanoa kääntäjälle nimenomaisesti: "Hei, tiedän mitä teen täällä, joten aion heittää objektin tiettyyn tyyppiin ja käyttää tiettyä menetelmää."

Edelliseen esimerkkiin viitaten, on tärkeä syy, jonka kääntäjä kieltäytyy hyväksymästä tiettyä menetelmän kutsua: välitettävä luokka voidaan Kiinteä käärme. Tällöin kääntäjä ei voi mitenkään varmistaa kaikkia alaluokkia MetalGearMerkki on giveOrderToTheArmy ilmoitettu menetelmä.

esiintymä varattu avainsana

Kiinnitä huomiota varattuun sanaan esiintymä. Ennen tietyn menetelmän käyttämistä olemme kysyneet MetalGearMerkki On "esiintymäIso pomo. Jos se ei ollut a Iso pomo saamme esimerkiksi seuraavan poikkeussanoman:

 Poikkeus säikeessä "main" java.lang.ClassCastException: com.javaworld.javachallengers.polymorphism.specificinvocation.SolidSnake ei voi heittää com.javaworld.javachallengers.polymorphism.specificinvocation.BigBoss 

super varattu avainsana

Entä jos haluaisimme viitata attribuuttiin tai menetelmään Java-superluokasta? Tässä tapauksessa voimme käyttää super varattu sana. Esimerkiksi:

 public class JavaMascot {void executeAction () {System.out.println ("Java-maskotti on aikeissa suorittamaan toiminnon!"); }} public class Duke laajentaa JavaMascotia {@Override void executeAction () {super.executeAction (); System.out.println ("Duke aikoo lyödä!"); } public staattinen void main (String ... superReservedWord) {uusi Duke (). executeAction (); }} 

Varatun sanan käyttö super sisään DukeS executeAction method käyttää superluokan menetelmää. Sitten suoritamme tietyn toiminnan Duke. Siksi voimme nähdä molemmat viestit alla olevasta lähdöstä:

 Java-maskotti on suorittamassa toimintoa! Duke aikoo lyödä! 

Vastaa polymorfismin haasteeseen!

Kokeillaan, mitä olet oppinut polymorfismista ja perinnöstä. Tässä haasteessa sinulle annetaan kourallinen menetelmiä Matt Groeningin Simpsoneista, ja haasteenasi on päätellä, millainen tulos kullekin luokalle tulee. Aloita analysoimalla seuraava koodi huolellisesti:

 public class PolymorphismChallenge {staattinen abstrakti luokka Simpson {void talk () {System.out.println ("Simpson!"); } suojattu void kepponen (String kepponen) {System.out.println (kepponen); }} staattinen luokka Bart jatkaa Simpsonia {String kepponen; Bart (String kepponen) {this. kepponen = kepponen; } suojattu void talk () {System.out.println ("Syö shortsini!"); } suojattu void kepponen () {super.prank (kepponen); System.out.println ("Koputa Homer alas"); }} staattinen luokka Lisa jatkaa Simpsonia {void talk (String toMe) {System.out.println ("Rakastan Saxia!"); }} public staattinen void main (String ... doYourBest) {uusi Lisa (). talk ("Sax :)"); Simpson simpson = uusi Bart ("D'oh"); simpson.talk (); Lisa lisa = uusi Lisa (); lisa.talk (); ((Bart) simpson). Kepponen (); }} 

Mitä mieltä sinä olet? Mikä on lopullinen tuotos? Älä käytä IDE: tä tämän selvittämiseen! Tarkoituksena on parantaa koodianalyysitaitojasi, joten yritä määrittää tulos itse.

Valitse vastauksesi ja löydät oikean vastauksen alta.

 A) Rakastan Saxia! D'oh Simpson! D'oh B) Sax :) Syö shortsini! Rakastan Saxia! D'oh Knock Homer alas C) Sax :) D'oh Simpson! Kolhi Homer alas D) Rakastan Saxia! Syö shortsini! Simpson! D'oh Knock Homer alas 

Mitä juuri tapahtui? Polymorfisuuden ymmärtäminen

Seuraavaa menetelmän kutsua varten:

 uusi Lisa (). puhua ("Sax :)"); 

tulos on “Rakastan Saxia!”Tämä johtuu siitä, että ohitamme a Merkkijono menetelmään ja Lisa on menetelmä.

Seuraava kutsu:

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

simpson.talk ();

Tulos on "Syö shortsini!"Tämä johtuu siitä, että me välitämme Simpson kirjoita Bart.

Tarkista nyt tämä, joka on hieman hankalampi:

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

Tässä käytetään menetelmän ylikuormitusta perinnöllä. Emme anna mitään puhemenetelmälle, minkä vuoksi Simpson puhua menetelmää käytetään. Tässä tapauksessa lähtö on:

 "Simpson!" 

Tässä on vielä yksi:

 ((Bart) simpson). Kepponen (); 

Tässä tapauksessa kepponen merkkijono hyväksyttiin, kun me instantiated Bart luokan kanssa uusi Bart ("D'oh");. Tässä tapauksessa ensin super. kepponen menetelmä, jota seuraa spesifinen kepponen menetelmä Bart. Tulos on:

 "D'oh" "Kaataa Homer alas" 

Video-haaste! Java-polymorfismin ja perinnön virheenkorjaus

Virheenkorjaus on yksi helpoimmista tavoista hyödyntää ohjelmointikonsepteja täysin ja parantaa samalla koodiasi. Tässä videossa voit seurata mukana, kun minä virheenkorjaan ja selitän Java-polymorfismihaastetta:

Yleisiä virheitä polymorfismin kanssa

On yleinen virhe ajatella, että on mahdollista vedota tiettyyn menetelmään ilman heittoa.

Toinen virhe on olla epävarma, mitä menetelmää käytetään, kun luokkaa havainnollistetaan polymorfisesti. Muista, että kutsuttava menetelmä on luodun ilmentymän menetelmä.

Muista myös, että menetelmän ohittaminen ei ole menetelmän ylikuormitus.

Menetelmän ohittaminen on mahdotonta, jos parametrit ovat erilaiset. Se on mahdollista muuttaa ohitetun menetelmän palautustyyppiä, jos palautustyyppi on yliluokan menetelmän alaluokka.

Mitä muistaa polymorfismista

  • Luotu instanssi määrittää, mitä menetelmää käytetään, kun käytetään polymorfiaa.
  • @Ohittaa merkintä velvoittaa ohjelmoijan käyttämään ohitettua menetelmää; jos ei, kääntäjävirhe.
  • Polymorfismia voidaan käyttää normaaliluokkien, abstraktien luokkien ja rajapintojen kanssa.
  • Suurin osa suunnittelumalleista riippuu jostakin polymorfismin muodosta.
  • Ainoa tapa käyttää tiettyä menetelmää polymorfisessa alaluokassasi on valu.
  • Koodiin on mahdollista suunnitella tehokas rakenne polymorfismin avulla.
  • Suorita testisi. Tällöin voit hallita tämän tehokkaan konseptin!

Vastausavain

Vastaus tähän Java-haastajaan on D.. Tuotos olisi:

 Rakastan Saxia! Syö shortsini! Simpson! D'oh Knock Homer alas 

Tämän tarinan "Polymorfismi ja perintö Javassa" julkaisi alun perin JavaWorld.