Veiledning til arv i Java

1. Oversikt

Et av hovedprinsippene for objektorientert programmering - arv - lar oss gjenbruke eksisterende kode eller utvide en eksisterende type.

Enkelt sagt, i Java kan en klasse arve en annen klasse og flere grensesnitt, mens et grensesnitt kan arve andre grensesnitt.

I denne artikkelen begynner vi med behovet for arv, og går til hvordan arv fungerer med klasser og grensesnitt.

Deretter vil vi dekke hvordan variabel- / metodenavnene og tilgangsmodifikatorene påvirker medlemmene som arves.

Og til slutt får vi se hva det betyr å arve en type.

2. Behovet for arv

Tenk deg at du som bilprodusent tilbyr flere bilmodeller til kundene dine. Selv om forskjellige bilmodeller kan tilby forskjellige funksjoner som et soltak eller skuddsikre vinduer, vil de alle inkludere vanlige komponenter og funksjoner, som motor og hjul.

Det gir mening å lage et grunnleggende design og utvide det til å lage sine spesialiserte versjoner, i stedet for å designe hver bilmodell separat, fra bunnen av.

På samme måte, med arv, kan vi opprette en klasse med grunnleggende funksjoner og atferd og lage dens spesialversjoner, ved å lage klasser, som arver denne basisklassen. På samme måte kan grensesnitt utvide eksisterende grensesnitt.

Vi vil merke bruken av flere termer for å referere til en type som er arvet av en annen type, spesielt:

  • en basetype kalles også en super- eller en foreldretype
  • en avledet type er referert til som en utvidet, sub- eller en barnetype

3. Klassearv

3.1. Utvide en klasse

En klasse kan arve en annen klasse og definere flere medlemmer.

La oss starte med å definere en basisklasse Bil:

offentlig klasse bil {int hjul; Strengmodell; ugyldig start () {// Sjekk viktige deler}}

Klassen Pansret bil kan arve medlemmene av Bil klasse etter ved hjelp av nøkkelordet strekker i sin erklæring:

offentlig klasse ArmoredCar utvider Car {int bulletProofWindows; void remoteStartCar () {// dette kjøretøyet kan startes ved hjelp av en fjernkontroll}}

Vi kan nå si at Pansret bil klasse er en underklasse av Bil, og sistnevnte er en superklasse av Pansret bil.

Klasser i Java støtter enkelt arv; de Pansret bil klasse kan ikke utvide flere klasser.

Vær også oppmerksom på at i fravær av et strekker nøkkelord, en klasse arver implisitt klassen java.lang.Objekt.

En underklasseklasse arver det ikke-statiske beskyttet og offentlig medlemmer fra superklasseklassen. I tillegg har medlemmene med misligholde og pakke tilgang arves hvis de to klassene er i samme pakke.

På den annen side, den privat og statisk medlemmer av en klasse arves ikke.

3.2. Få tilgang til foreldremedlemmer fra en barneklasse

For å få tilgang til arvede egenskaper eller metoder, kan vi bare bruke dem direkte:

offentlig klasse ArmoredCar utvider Car {public String registerModel () {returmodell; }}

Merk at vi ikke trenger en referanse til superklassen for å få tilgang til medlemmene.

4. Grensesnittarv

4.1. Implementering av flere grensesnitt

Selv om klasser bare kan arve en klasse, kan de implementere flere grensesnitt.

Tenk deg Pansret bil som vi definerte i forrige avsnitt kreves for en super spion. Så Bil produksjonsfirma tenkte å legge til flygende og flytende funksjonalitet:

offentlig grensesnitt Flytbart {void floatOnWater (); }
offentlig grensesnitt Flyable {void fly (); }
public class ArmoredCar utvider Bilimplementerer Floatable, Flyable {public void floatOnWater () {System.out.println ("Jeg kan flyte!"); } public void fly () {System.out.println ("Jeg kan fly!"); }}

I eksemplet ovenfor merker vi bruken av nøkkelordet redskaper å arve fra et grensesnitt.

4.2. Problemer med flere arv

Java tillater flere arv ved bruk av grensesnitt.

Inntil Java 7 var dette ikke et problem. Grensesnitt kunne bare definere abstrakt metoder, det vil si metoder uten implementering. Så hvis en klasse implementerte flere grensesnitt med samme metodesignatur, var det ikke noe problem. Implementeringsklassen hadde til slutt bare en metode å implementere.

La oss se hvordan denne enkle ligningen endret seg med introduksjonen av misligholde metoder i grensesnitt, med Java 8.

Fra og med Java 8, kunne grensesnitt velge å definere standardimplementeringer for metodene (et grensesnitt kan fremdeles definere abstrakt metoder). Dette betyr at hvis en klasse implementerer flere grensesnitt, som definerer metoder med samme signatur, vil underordnede klasse arve separate implementeringer. Dette høres komplekst ut og er ikke tillatt.

Java tillater ikke arv av flere implementeringer av de samme metodene, definert i separate grensesnitt.

Her er et eksempel:

offentlig grensesnitt Flytbart {standard ugyldig reparasjon () {System.out.println ("Reparerer flytbart objekt"); }}
offentlig grensesnitt Flybart {standard ugyldig reparasjon () {System.out.println ("Reparerer flybart objekt"); }}
offentlig klasse ArmoredCar utvider Bilredskaper Flytbar, Flybar {// dette vil ikke kompilere}

Hvis vi ønsker å implementere begge grensesnittene, må vi overstyre reparere() metode.

Hvis grensesnittene i de foregående eksemplene definerer variabler med samme navn, si varighet, vi får ikke tilgang til dem uten å gå foran variabelnavnet med grensesnittnavnet:

offentlig grensesnitt Flytbar {int varighet = 10; }
offentlig grensesnitt Flybart {int varighet = 20; }
offentlig klasse ArmoredCar utvider Bilredskaper Floatable, Flyable {public void aMethod () {System.out.println (duration); // vil ikke kompilere System.out.println (Floatable.duration); // utganger 10 System.out.println (Flyable.duration); // utganger 20}}

4.3. Grensesnitt som utvider andre grensesnitt

Et grensesnitt kan utvide flere grensesnitt. Her er et eksempel:

offentlig grensesnitt Flytbart {void floatOnWater (); }
grensesnittgrensesnitt Flybart {void fly (); }
offentlig grensesnitt SpaceTraveller utvider Floatable, Flyable {void remoteControl (); }

Et grensesnitt arver andre grensesnitt ved å bruke nøkkelordet strekker. Klasser bruker nøkkelordet redskaper å arve et grensesnitt.

5. Arvetype

Når en klasse arver en annen klasse eller grensesnitt, bortsett fra å arve medlemmene, arver den også deres type. Dette gjelder også et grensesnitt som arver andre grensesnitt.

Dette er et veldig kraftig konsept, som lar utviklere program til et grensesnitt (baseklasse eller grensesnitt), snarere enn å programmere til deres implementeringer.

Tenk deg for eksempel en tilstand der en organisasjon fører en liste over bilene som de ansatte eier. Selvfølgelig kan alle ansatte eie forskjellige bilmodeller. Så hvordan kan vi referere til forskjellige bilforekomster? Her er løsningen:

offentlig klasse Ansatt {privat strengnavn; privat bil bil; // standard konstruktør}

Fordi alle avledede klasser av Bil arve typen Bil, kan de avledede klasseinstansene henvises ved å bruke en variabel av klasse Bil:

Ansatt e1 = ny ansatt ("Shreya", ny pansret bil ()); Ansatt e2 = ny ansatt ("Paul", ny SpaceCar ()); Ansatt e3 = ny ansatt ("Pavni", ny BMW ());

6. Skjulte klassemedlemmer

6.1. Skjulte instansmedlemmer

Hva skjer hvis både superklassen og underklassen definerer en variabel eller metode med samme navn? Ikke bekymre deg; vi kan fremdeles få tilgang til dem begge. Vi må imidlertid gjøre intensjonen vår klar for Java ved å prefiksere variabelen eller metoden med nøkkelordene dette eller super.

De dette nøkkelord refererer til forekomsten det brukes i. De super nøkkelord (som det virker opplagt) refererer til foreldreklasseforekomsten:

offentlig klasse ArmoredCar utvider Car {private String model; public String getAValue () {return super.model; // returnerer verdien av modellen definert i basisklassen Car // returnerer denne.modellen; // vil returnere verdien av modellen definert i ArmoredCar // returmodellen; // vil returnere verdien til modellen definert i ArmoredCar}}

Mange utviklere bruker dette og super nøkkelord for eksplisitt å oppgi hvilken variabel eller metode de refererer til. Imidlertid kan bruk av dem med alle medlemmer gjøre at koden vår blir rotete.

6.2. Skjulte statiske medlemmer

Hva skjer når basisklassen og underklassene våre definerer statiske variabler og metoder med samme navn? Kan vi få tilgang til en statisk medlem fra basisklassen, i den avledede klassen, slik vi gjør for forekomstvariablene?

La oss finne ut med et eksempel:

public class Car {public static String msg () {return "Car"; }}
public class ArmoredCar utvider Car {public static String msg () {return super.msg (); // dette vil ikke kompilere. }}

Nei, vi kan ikke. De statiske medlemmene tilhører en klasse og ikke til forekomster. Så vi kan ikke bruke det ikke-statiske super nøkkelord i msg ().

Siden statiske medlemmer tilhører en klasse, kan vi endre forrige samtale på følgende måte:

returner Car.msg ();

Tenk på følgende eksempel, der både baseklassen og den avledede klassen definerer en statisk metode msg () med samme signatur:

public class Car {public static String msg () {return "Car"; }}
public class ArmoredCar utvider bil {public static String msg () {return "ArmoredCar"; }}

Slik kan vi kalle dem:

Car first = new ArmoredCar (); ArmoredCar second = new ArmoredCar ();

For den foregående koden, first.msg () vil sende ut “Bil og second.msg () vil sende ut “ArmoredCar”. Den statiske meldingen som kalles, avhenger av typen variabel som brukes til å referere til Pansret bil forekomst.

7. Konklusjon

I denne artikkelen dekket vi et kjerneaspekt av Java-språket - arv.

Vi så hvordan Java støtter enkelt arv med klasser og flere arv med grensesnitt og diskuterte komplikasjonene i hvordan mekanismen fungerer på språket.

Som alltid er hele kildekoden for eksemplene tilgjengelig på GitHub.