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ą: