Programavimas

Paveldėjimas ir kompozicija: kaip pasirinkti

Paveldėjimas ir kompozicija yra dvi programavimo technikos, kurias kūrėjai naudoja ryšiams tarp klasių ir objektų užmegzti. Kadangi paveldėjimas iš vienos klasės kyla iš kitos, sudėtis klasę apibrėžia kaip jos dalių sumą.

Paveldėjimo būdu sukurtos klasės ir objektai yra sandariai sujungtas nes paveldėjimo santykiuose pakeitus tėvą ar superklasę rizikuojama sulaužyti jūsų kodą. Klasės ir objektai, sukurti per kompoziciją, yra silpnai sujungta, tai reiškia, kad galite lengviau pakeisti komponentus, nesulaužydami savo kodo.

Kadangi laisvai susietas kodas suteikia daugiau lankstumo, daugelis kūrėjų sužinojo, kad kompozicija yra geresnė technika nei paveldėjimas, tačiau tiesa yra sudėtingesnė. Programavimo įrankio pasirinkimas yra panašus į tinkamo virtuvės įrankio pasirinkimą: daržovių pjaustymui nenaudotumėte sviestinio peilio ir tokiu pačiu būdu neturėtumėte pasirinkti kiekvieno programavimo scenarijaus kompozicijos.

Šiame „Java Challenger“ sužinosite skirtumą tarp paveldėjimo ir sudėties bei kaip nuspręsti, kas tinka jūsų programai. Toliau aš supažindinsiu jus su keliais svarbiais, bet iššūkius keliančiais Java paveldėjimo aspektais: svarbiausiu metodu, super raktinis žodis ir tipo liejimas. Galiausiai išbandysite tai, ką išmokote, naudodamiesi paveldėjimo pavyzdžiu eilutėje po eilutę, kad nustatytumėte, kokia išvestis turėtų būti.

Kada naudoti paveldėjimą „Java“

Į objektinį programavimą galime naudoti paveldėjimą, kai žinome, kad tarp vaiko ir jo tėvų klasės yra „yra“ ryšys. Keletas pavyzdžių būtų:

  • Asmuo yra žmogus.
  • Katė yra gyvūnas.
  • Mašina yra transporto priemonės.

Kiekvienu atveju vaikas arba poklasis yra a specializuota tėvų arba superklasės versija. Paveldėjimas iš superklasės yra pakartotinio kodo naudojimo pavyzdys. Norėdami geriau suprasti šiuos santykius, skirkite šiek tiek laiko ir ištirkite Automobilis klasę, kuri paveldi iš Transporto priemonė:

 klasės transporto priemonė {String markė; Styginių spalva; dvigubas svoris; dvigubas greitis; void move () {System.out.println ("Transporto priemonė juda"); }} viešosios klasės automobilis pratęsia transporto priemonę {String licensePlateNumber; Stygų savininkas; Styginių kūno stilius; public static void main (eilutė ... paveldėjimo pavyzdys) {System.out.println (nauja transporto priemonė (). prekės ženklas); System.out.println (naujas automobilis (). Markė); naujas automobilis (). judėti (); }} 

Kai svarstote galimybę paveldėti, paklauskite savęs, ar poklasis tikrai yra labiau specializuota superklasės versija. Šiuo atveju automobilis yra transporto priemonės rūšis, todėl paveldėjimo santykiai yra prasmingi.

Kada naudoti kompoziciją „Java“

Į objektinį programavimą galime naudoti kompoziciją tais atvejais, kai vienas objektas „turi“ (arba yra jo dalis) kitą objektą. Keletas pavyzdžių būtų:

  • Mašina turi baterija (baterija yra dalis mašina).
  • Asmuo turi širdis (širdis yra dalis asmuo).
  • Namas turi svetainė (svetainė yra dalis Namas).

Norėdami geriau suprasti tokio tipo santykius, apsvarstykite a sudėtis Namas:

 public class CompositionPavyzdys {public static void main (String ... houseComposition) {naujas namas (naujas miegamasis (), naujas LivingRoom ()); // Dabar namą sudaro miegamasis ir „LivingRoom“ statinės klasės namas {miegamasis miegamasis; LivingRoom livingRoom; Namas (miegamasis miegamasis, „LivingRoom livingRoom“) {this.bedroom = bedroom; this.livingRoom = livingRoom; }} statinės klasės miegamasis {} statinės klasės „LivingRoom“ {}} 

Šiuo atveju mes žinome, kad name yra svetainė ir miegamasis, todėl galime naudotis Miegamasis ir Svetainė objektai, sudaryti a Namas

Gaukite kodą

Gaukite šio „Java Challenger“ pavyzdžių šaltinio kodą. Laikydamiesi pavyzdžių, galite atlikti savo testus.

Paveldėjimas ir sudėtis: du pavyzdžiai

Apsvarstykite šį kodą. Ar tai geras paveldėjimo pavyzdys?

 importuoti java.util.HashSet; public class CharacterBadExampleInheritance pratęsia HashSet {public static void main (String ... badExampleOfInheritance) {BadExampleInheritance badExampleInheritance = new BadExampleInheritance (); badExampleInheritance.add („Homeras“); badExampleInheritance.forEach (System.out :: println); } 

Šiuo atveju atsakymas yra neigiamas. Vaikų klasė paveldi daug metodų, kurių ji niekada nenaudos, todėl bus sukurtas glaudžiai susietas kodas, kuris yra ir painus, ir sunkiai prižiūrimas. Gerai įsižiūrėjus, taip pat aišku, kad šis kodas neišlaiko „yra“ testo.

Išbandykime tą patį pavyzdį naudodami kompoziciją:

 importuoti java.util.HashSet; importuoti java.util.Set; public class CharacterCompositionExample {static Set set = new HashSet (); public static void main (String ... goodExampleOfComposition) {set.add ("Homer"); set.forEach (System.out :: println); } 

Kompozicijos naudojimas šiam scenarijui leidžia CharacterCompositionExample klasės naudoti tik du iš „HashSet“metodus, nepaveldėdami jų visų. Dėl to gaunamas paprastesnis, mažiau susietas kodas, kurį bus lengviau suprasti ir prižiūrėti.

Paveldėjimo pavyzdžiai JDK

„Java“ kūrimo rinkinyje gausu gerų paveldėjimo pavyzdžių:

 klasė IndexOutOfBoundsException pratęsia RuntimeException {...} class ArrayIndexOutOfBoundsException pratęsia IndexOutOfBoundsException {...} klasė FileWriter išplečia OutputStreamWriter {...} klasė OutputStreamWriter pratęsia Writer {...} sąsajos srautas pratęsia BaseStream {...} 

Atkreipkite dėmesį, kad kiekviename iš šių pavyzdžių vaikų klasė yra specializuota jos tėvų versija; pavyzdžiui, IndexOutOfBoundsException yra RuntimeException.

Metodas, viršijantis Java paveldėjimą

Paveldėjimas leidžia mums pakartotinai naudoti vienos klasės metodus ir kitus atributus naujoje klasėje, o tai yra labai patogu. Tačiau norint, kad paveldėjimas iš tikrųjų veiktų, mes taip pat turime sugebėti pakeisti dalį paveldėto elgesio savo naujame poklasyje. Pavyzdžiui, galbūt norime specializuoti garsą a Katė gamina:

 klasės gyvūnas {void emitSound () {System.out.println ("Gyvūnas skleidė garsą"); }} klasės katė praplečia gyvūną {@Override void emitSound () {System.out.println ("Miau"); }} klasė Šuo pratęsia gyvūną {} viešoji klasė Pagrindinis {public static void main (String ... doYourBest) {Gyvūno katė = nauja Katė (); // Miau gyvūnų šuo = naujas šuo (); // Gyvūnas skleidė garsą Gyvūnas gyvūnas = naujas gyvūnas (); // Gyvūnas skleidė garsų katiną.emitSound (); dog.emitSound (); animal.emitSound (); }} 

Tai yra Java paveldėjimo su metodo viršijimu pavyzdys. Pirma, mes pratęsti Gyvūnas klasę sukurti naują Katė klasė. Toliau mes nepaisyti Gyvūnas klasės emitSound () būdas gauti specifinį garsą Katė daro. Nepaisant to, kad klasės tipą paskelbėme Gyvūnas, kai ją instituojame kaip Katė gausime katės miau.

Svarbiausias metodas yra polimorfizmas

Iš paskutinio mano įrašo galite prisiminti, kad svarbiausias metodas yra polimorfizmo arba virtualaus metodo iškvietimo pavyzdys.

Ar Java turi daug paveldėjimo būdų?

Skirtingai nuo kai kurių kalbų, pvz., C ++, „Java“ neleidžia daug kartų paveldėti su klasėmis. Tačiau galite naudoti kelis paveldėjimo būdus su sąsajomis. Šiuo atveju skirtumas tarp klasės ir sąsajos yra tas, kad sąsajos neišlaiko būsenos.

Jei bandysite paveldėti kelis būdus, kuriuos turiu toliau, kodas nebus sudarytas:

 klasės gyvūnų {}} klasės žinduolių {} klasės šunys pratęsia gyvūnų, žinduolių {} 

Sprendimas naudojant klases būtų paveldėti po vieną:

 gyvūnų klasės {} klasės žinduolių pratęsimas gyvūnų {} klasės šunų pratęsimo žinduoliais {} 

Kitas sprendimas yra klases pakeisti sąsajomis:

 sąsaja Gyvūnų {} sąsaja Žinduoliai {} klasės šuo įgyvendina gyvūną, žinduolį {} 

„Super“ naudojimas norint pasiekti tėvų klasių metodus

Kai dvi klasės yra susijusios su paveldėjimu, vaikų klasė turi galėti patekti į kiekvieną prieinamą savo tėvų klasės lauką, metodą ar konstruktorių. „Java“ kalboje vartojame rezervuotą žodį super užtikrinti, kad vaikų klasė vis tiek galėtų naudotis savo tėvų nepaisomu metodu:

 public class SuperWordExample {class Character {Character () {System.out.println ("Sukurtas simbolis"); } void move () {System.out.println ("Veikėjas vaikšto ..."); }} klasė Moe tęsiasi Simbolis {Moe () {super (); } void giveBeer () {super.move (); System.out.println („Duok alaus“); }}} 

Šiame pavyzdyje Charakteris yra Moe tėvų klasė. Naudojant super, mes galime prieiti Charakteris's perkelti () metodas, kad Moe duotų alaus.

Naudojant konstruktorius su paveldėjimu

Kai viena klasė paveldės iš kitos, prieš įkeliant jos poklasį, visada bus įkeliamas superklasės konstruktorius. Daugeliu atvejų rezervuotas žodis super bus automatiškai pridėtas prie konstruktoriaus. Tačiau, jei superklasė turi parametrą savo konstruktoriuje, turėsime sąmoningai pasinaudoti super konstruktorius, kaip parodyta žemiau:

 public class ConstructorSuper {class Character {Character () {System.out.println ("Supaprastintas konstruktorius buvo iškviestas"); }} klasė Barney praplečia simbolį {// Nereikia deklaruoti konstruktoriaus ar kviesti superkonstruktoriaus // JVM norės tai}} 

Jei pirminėje klasėje yra konstruktorius su bent vienu parametru, tada konstruktorių turime deklaruoti poklasyje ir naudoti super aiškiai iškviesti pirminį konstruktorių. super rezervuotas žodis nebus automatiškai pridėtas ir kodas nebus kompiliuojamas be jo. Pavyzdžiui:

 public class CustomizedConstructorSuper {class Character {Character (String name) {System.out.println (vardas + "buvo iškviestas"); }} klasė Barney praplečia simbolį {// Turėsime kompiliavimo klaidą, jei konstruktoriaus nekviesime aiškiai // Turime jį pridėti Barney () {super ("Barney Gumble"); }}} 

Tipo liejimas ir „ClassCastException“

Perdavimas yra būdas aiškiai pranešti kompiliatoriui, kad tikrai ketinate konvertuoti nurodytą tipą. Tai tarsi sakymas: „Ei, JVM, aš žinau, ką darau, prašau, meskite šią klasę su tokiu tipu“. Jei jūsų perduota klasė nesuderinama su deklaruotu klasės tipu, gausite „ClassCastException“.

Paveldėdami galime priskirti vaikų klasę tėvų klasei be liejimo, tačiau negalime priskirti tėvų klasės vaikų klasei nenaudodami liejimo.

Apsvarstykite šį pavyzdį:

 public class CastingExample {public static void main (String ... castingExample) {Animal animal = new Animal (); Šuns šuoAnimal = (Šuo) gyvūnas; // Mes gausime „ClassCastException Dog dog“ = naujas šuo (); Gyvūno šuoWithAnimalType = naujas šuo (); Šuo specifinis Šuo = (Šuo) dogWithAnimalType; specifinis Šuo.loti (); Gyvūnas kitas Šuo = šuo; // Čia puiku, nereikia mesti System.out.println ((((Šuo) anotherDog)); // Tai dar vienas būdas išmesti objektą}} klasė Gyvūnas {} klasė Šuo pratęsia gyvūną {void bark () {System.out.println ("Au au"); }} 

Kai bandysime mesti Gyvūnas egzempliorius a Šuo gauname išimtį. Taip yra todėl, kad Gyvūnas nieko nežino apie savo vaiką. Tai gali būti katė, paukštis, driežas ir kt. Nėra informacijos apie konkretų gyvūną.

Šiuo atveju problema yra ta, kad mes iš karto susijaudinome Gyvūnas kaip šitas:

 Gyvūninis gyvūnas = naujas gyvūnas (); 

Tada bandė tai perduoti taip:

 Šuns šuoAnimal = (Šuo) gyvūnas; 

Nes mes neturime a Šuo Pavyzdžiui, neįmanoma priskirti Gyvūnas į Šuo. Jei bandysime, gausime a „ClassCastException“

Kad išvengtume išimties, turėtume akimirksniu parodyti Šuo kaip šitas:

 Šuns šuo = naujas šuo (); 

tada priskirkite jį Gyvūnas:

 Gyvūnas kitas Šuo = šuo; 

Šiuo atveju, nes mes išplėtėme Gyvūnas klasė Šuo egzemplioriaus net nereikia mesti; Gyvūnas tėvų klasės tipas paprasčiausiai priima užduotį.

Liejimas su supertipais

Galima paskelbti a Šuo su supertipu Gyvūnas, bet jei norime pasinaudoti konkrečiu metodu iš Šuo, mums reikės jį mesti. Pavyzdžiui, kas būtų, jei norėtume pasinaudoti žievė () metodas? Gyvūnas supertipas niekaip negali tiksliai žinoti, kokio gyvūno pavyzdį mes naudojame, todėl turime mesti Šuo rankiniu būdu, kol galėsime iškviesti žievė () metodas:

 Gyvūno šuoWithAnimalType = naujas šuo (); Šuo specifinis Šuo = (Šuo) dogWithAnimalType; specifinis Šuo. žievė (); 

Taip pat galite naudoti liejimą nepriskirdami objekto klasės tipui. Šis metodas yra patogus, kai nenorite deklaruoti kito kintamojo:

 System.out.println (((Šuo) kitas Šuo)); // Tai dar vienas būdas perduoti objektą 

Priimkite „Java“ paveldėjimo iššūkį!

Jūs sužinojote keletą svarbių paveldėjimo sąvokų, todėl dabar laikas išbandyti paveldėjimo iššūkį. Norėdami pradėti, ištirkite šį kodą: