Programiranje

Dedovanje proti sestavi: Kako izbrati

Dedovanje in sestava sta dve programski tehniki, ki jo razvijalci uporabljajo za vzpostavljanje odnosov med razredi in predmeti. Medtem ko dedovanje izhaja iz enega razreda iz drugega, sestava opredeljuje razred kot vsoto njegovih delov.

Razredi in predmeti, ustvarjeni z dedovanjem, so tesno povezani ker zamenjava nadrejenega ali superrazreda v dednem razmerju tvega zlom kode. Razredi in predmeti, ustvarjeni s kompozicijo, so ohlapno sklopljen, kar pomeni, da lahko sestavne dele lažje spremenite, ne da bi pri tem zlomili kodo.

Ker ohlapno povezana koda ponuja večjo prilagodljivost, so se mnogi razvijalci naučili, da je sestava boljša tehnika kot dedovanje, resnica pa je bolj zapletena. Izbira programskega orodja je podobna izbiri ustreznega kuhinjskega orodja: za rezanje zelenjave ne bi uporabljali noža za maslo in na enak način ne bi smeli izbirati sestave za vsak scenarij programiranja.

V tem Java Challengerju boste izvedeli razliko med dedovanjem in sestavo ter kako se odločiti, kateri je pravi za vaš program. Nato vam predstavim nekaj pomembnih, a zahtevnih vidikov dedovanja Java: preglasitev metode, super ključna beseda in vlivanje vrste. Na koncu boste preizkusili, kaj ste se naučili, tako da boste po vzoru dedovanja sledili vrstici za vrstico in ugotovili, kakšen mora biti rezultat.

Kdaj uporabiti dedovanje v Javi

Pri objektno usmerjenem programiranju lahko dedovanje uporabimo, ko vemo, da obstaja povezava "je" med otrokom in njegovim nadrejenim razredom. Nekaj ​​primerov bi bilo:

  • Oseba je človek.
  • Mačka je žival.
  • Avto je vozilo.

V vsakem primeru je otrok ali podrazred a specializirano različica nadrejenega ali nadrazreda. Podedovanje superklase je primer ponovne uporabe kode. Če želite bolje razumeti to razmerje, si vzemite trenutek za študij Avto razred, ki podeduje od Vozilo:

 razred Vozilo {znamka niza; Barva strune; dvojna teža; dvojna hitrost; void move () {System.out.println ("Vozilo se premika"); }} javni razred avtomobila podaljša vozilo {String licensePlateNumber; Lastnik niza; String bodyStyle; public static void main (String ... nasledstvoExample) {System.out.println (novo vozilo (). blagovna znamka); System.out.println (nov avtomobil (). Znamka); nov Car (). move (); }} 

Ko razmišljate o dedovanju, se vprašajte, ali je podrazred res bolj specializirana različica superrazreda. V tem primeru je avto vrsta vozila, zato je dedno razmerje smiselno.

Kdaj uporabiti sestavo v Javi

Pri objektno usmerjenem programiranju lahko uporabimo sestavo v primerih, ko en objekt "ima" (ali je del) drugega predmeta. Nekaj ​​primerov bi bilo:

  • Avto ima baterija (baterija je del avto).
  • Oseba ima srce (srce je del oseba).
  • Hiša ima dnevna soba (dnevna soba je del hiša).

Če želite bolje razumeti to vrsto odnosov, razmislite o sestavi a Hiša:

 javni razred CompositionExample {public static void main (String ... houseComposition) {new House (new Bedroom (), new LivingRoom ()); // Hiša je zdaj sestavljena iz Spalnice in LivingRoom} Hiša statičnega razreda {Spalnica; LivingRoom livingRoom; Hiša (spalnica spalnica, LivingRoom livingRoom) {this.bedroom = spalnica; this.livingRoom = livingRoom; }} spalnica statičnega razreda {} statični razred LivingRoom {}} 

V tem primeru vemo, da ima hiša dnevno sobo in spalnico, zato lahko uporabimo Spalnica in Dnevna soba predmeti v sestavi a Hiša

Pridobite kodo

Pridobite izvorno kodo za primere v tem Java Challengerju. Med sledenjem primerom lahko izvajate lastne teste.

Dedovanje vs sestava: Dva primera

Upoštevajte naslednjo kodo. Je to dober primer dedovanja?

 uvoz java.util.HashSet; javni razred CharacterBadExampleInheritance razširja HashSet {public static void main (String ... badExampleOfInheritance) {BadExampleInheritance badExampleInheritance = new BadExampleInheritance (); badExampleInheritance.add ("Homer"); badExampleInheritance.forEach (System.out :: println); } 

V tem primeru je odgovor ne. Otroški razred podeduje številne metode, ki jih nikoli ne bo uporabil, kar ima za posledico tesno povezano kodo, ki je hkrati zmedena in jo je težko vzdrževati. Če dobro pogledate, je tudi jasno, da ta koda ne prestane preizkusa "je".

Zdaj poskusimo isti primer z uporabo sestave:

 uvoz java.util.HashSet; import java.util.Set; javni razred CharacterCompositionExample {static set set = new HashSet (); javna statična void main (String ... goodExampleOfComposition) {set.add ("Homer"); set.forEach (System.out :: println); } 

Uporaba sestave za ta scenarij omogoča CharacterCompositionExample razredu uporabiti samo dva od HashSetmetode, ne da bi jih podedovali. Rezultat je enostavnejša, manj povezana koda, ki jo bo lažje razumeti in vzdrževati.

Primeri dedovanja v JDK

Java Development Kit je poln dobrih primerov dedovanja:

 class IndexOutOfBoundsException razširja RuntimeException {...} class ArrayIndexOutOfBoundsException extends IndexOutOfBoundsException {...} class FileWriter extends OutputStreamWriter {...} class OutputStreamWriter extends Writer {...} interface Stream extends BaseStream Stream {...} 

Upoštevajte, da je v vsakem od teh primerov podrejeni razred specializirana različica svojega nadrejenega; na primer, IndexOutOfBoundsException je vrsta RuntimeException.

Preglasitev metode z dedovanjem Java

Dedovanje nam omogoča, da ponovno uporabimo metode in druge atribute enega razreda v novem razredu, kar je zelo priročno. Da pa bi dedovanje zares delovalo, moramo biti sposobni spremeniti tudi nekaj podedovanega vedenja v novem podrazredu. Na primer, morda bi želeli specializirati zvok a Mačka naredi:

 razred Animal {void emitSound () {System.out.println ("Žival je oddala zvok"); }} razred Cat razširja Animal {@Override void emitSound () {System.out.println ("Meow"); }} class Dog razširja Animal {} public class Main {public static void main (String ... doYourBest) {Animal cat = new Cat (); // Meow Animal dog = new Dog (); // Žival je oddala zvok Živalska žival = nova žival (); // Žival je oddala zvok cat.emitSound (); dog.emitSound (); animal.emitSound (); }} 

To je primer dedovanja Java s preglasitvijo metode. Najprej mi podaljšati Žival razred, da ustvarite novo Mačka razred. Nato pa mi preglasiti Žival razredov emitSound () metoda, da dobite določen zvok Mačka naredi. Čeprav smo tip razreda razglasili za Žival, ko ga primerimo kot Mačka dobili bomo mačje mijavkanje.

Prevlada metoda je polimorfizem

Morda se spomnite iz mojega zadnjega prispevka, da je preglasitev metode primer polimorfizma ali priklica navidezne metode.

Ali ima Java večkratno dedovanje?

Za razliko od nekaterih jezikov, na primer C ++, Java ne dovoljuje večkratnega dedovanja z razredi. Vendar lahko z vmesniki uporabite večkratno dedovanje. Razlika med razredom in vmesnikom je v tem primeru v tem, da vmesniki ne ohranjajo stanja.

Če poskusite več dedovanja, kot sem spodaj, se koda ne prevede:

 razred Animal {} class Mammal {} class Dog podaljša Animal, Mammal {} 

Rešitev z uporabo razredov bi bila podedovanje enega po enega:

 class Animal {} class Mammal extends Animal {} class Dog podaljša Mammal {} 

Druga rešitev je zamenjava razredov z vmesniki:

 vmesnik Animal {} vmesnik Mammal {} razred Dog izvaja Animal, Mammal {} 

Uporaba "super" za dostop do metod nadrejenih razredov

Ko sta dva razreda povezana z dedovanjem, mora imeti podrejeni razred dostop do vseh dostopnih polj, metod ali konstruktorjev nadrejenega razreda. V Javi uporabljamo rezervirano besedo super da zagotovimo, da lahko podrejeni razred še vedno dostopa do nadrejene metode nadrejenega:

 javni razred SuperWordExample {class Character {Character () {System.out.println ("Znak je bil ustvarjen"); } void move () {System.out.println ("Sprehod po znakih ..."); }} razred Moe razširja znak {Moe () {super (); } void giveBeer () {super.move (); System.out.println ("Daj pivo"); }}} 

V tem primeru Značaj je nadrejeni razred za Moe. Uporaba super, imamo dostop Značajje premakni () metoda, da Moe dobi pivo.

Uporaba konstruktorjev z dedovanjem

Ko en razred podeduje drugega, bo konstruktor superklase vedno naložen najprej, preden naloži svoj podrazred. V večini primerov pridržana beseda super bo samodejno dodan konstruktorju. Če pa ima superrazred parameter v svojem konstruktorju, bomo morali namerno priklicati super konstruktor, kot je prikazano spodaj:

 javni razred ConstructorSuper {class Character {Character () {System.out.println ("Poklican je bil super konstruktor"); }} razred Barney razširi znak {// ni treba razglasiti konstruktorja ali priklicati super konstruktorja // JVM bo do tega}} 

Če ima nadrejeni razred konstruktor z vsaj enim parametrom, moramo konstruktor prijaviti v podrazred in uporabiti super da izrecno prikliče nadrejeni konstruktor. The super rezervirana beseda ne bo dodana samodejno in koda brez nje ne bo prevedena. Na primer:

 javni razred CustomizedConstructorSuper {razred Znak {Znak (ime niza) {System.out.println (ime + "je bil poklican"); }} razred Barney razširi Znak {// Napaka pri sestavljanju bo, če konstruktorja ne prikličemo izrecno // Dodati ga moramo Barney () {super ("Barney Gumble"); }}} 

Vnesite ulivanje in ClassCastException

Casting je način, kako prevajalniku izrecno sporočite, da nameravate pretvoriti določeno vrsto. Kot da bi rekli: "Hej, JVM, vem, kaj počnem, zato prosim oddajte ta razred tej vrsti." Če razred, ki ste ga oddali, ni združljiv s tipom razreda, ki ste ga prijavili, boste dobili ClassCastException.

Pri dedovanju lahko nadrejeni razred dodelimo nadrejenemu razredu brez oddajanja, vendar nadrejenega razreda podrejenemu razredu ne moremo dodeliti brez uporabe vlivanja.

Upoštevajte naslednji primer:

 javni razred CastingExample {javna statična void main (String ... castingExample) {Živalska žival = nova Animal (); Dog dogAnimal = (Dog) žival; // Dobili bomo ClassCastException Dog dog = new Dog (); Animal dogWithAnimalType = new Dog (); Dog specificDog = (Dog) dogWithAnimalType; specificDog.bark (); Žival drugDog = pes; // Tukaj je v redu, ni potrebe po oddaji System.out.println (((Dog) anotherDog)); // To je še en način oddajanja predmeta}} razreda Animal {} razreda Dog razširja Animal {void bark () {System.out.println ("Au au"); }} 

Ko poskušamo oddati Žival primer do a Pes dobimo izjemo. To je zato, ker Žival ne ve ničesar o svojem otroku. Lahko je mačka, ptica, kuščar itd. O določeni živali ni podatkov.

Težava v tem primeru je, da smo ustvarili primerek Žival Všečkaj to:

 Živalska žival = nova žival (); 

Potem je poskušal oddati tako:

 Dog dogAnimal = (Dog) žival; 

Ker nimamo a Pes Na primer, je nemogoče dodeliti Žival do Pes. Če poskusimo, bomo dobili ClassCastException

Da bi se izognili izjemi, bi morali primerjati Pes Všečkaj to:

 Pasji pes = nov pes (); 

nato dodelite Žival:

 Žival drugDog = pes; 

V tem primeru, ker smo podaljšali Žival razred, Pes primerka niti ni treba oddati; Žival tip nadrejenega razreda preprosto sprejme nalogo.

Igranje s supertipi

Možno je razglasiti a Pes s supertipom Žival, toda če želimo iz Pes, morali ga bomo oddati. Kot primer, kaj če bi želeli uporabiti lubje () metoda? The Žival supertip nikakor ne more natančno vedeti, na kakšen primer živali se sklicujemo, zato moramo oddati Pes ročno, preden lahko prikličemo lubje () metoda:

 Animal dogWithAnimalType = new Dog (); Dog specificDog = (Dog) dogWithAnimalType; specificDog.bark (); 

Lahko uporabite tudi oddajanje, ne da bi predmeta dodelili tipu razreda. Ta pristop je priročen, če ne želite razglasiti druge spremenljivke:

 System.out.println (((Dog) anotherDog)); // To je še en način za oddajo predmeta 

Sprejmite izziv dedovanja Java!

Izvedeli ste nekaj pomembnih konceptov dedovanja, zato je zdaj čas, da preizkusite dediščinski izziv. Za začetek preučite naslednjo kodo:

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