Vejledning til arv i Java

1. Oversigt

Et af de grundlæggende principper for objektorienteret programmering - arv - gør det muligt for os at genbruge eksisterende kode eller udvide en eksisterende type.

Kort sagt, i Java kan en klasse arve en anden klasse og flere grænseflader, mens en grænseflade kan arve andre grænseflader.

I denne artikel begynder vi med behovet for arv og går til, hvordan arv fungerer med klasser og grænseflader.

Derefter dækker vi, hvordan variabel- / metodenavne og adgangsmodifikatorer påvirker de medlemmer, der arves.

Og i slutningen ser vi, hvad det betyder at arve en type.

2. Behovet for arv

Forestil dig, at du som bilproducent tilbyder flere bilmodeller til dine kunder. Selvom forskellige bilmodeller muligvis tilbyder forskellige funktioner som et soltag eller skudsikre vinduer, vil de alle omfatte almindelige komponenter og funktioner som motor og hjul.

Det giver mening at skabe et grundlæggende design og udvide det til at oprette deres specialversioner, snarere end at designe hver bilmodel separat fra bunden.

På samme måde med arv kan vi oprette en klasse med grundlæggende funktioner og adfærd og oprette dens specialversioner ved at oprette klasser, der arver denne basisklasse. På samme måde kan grænseflader udvide eksisterende grænseflader.

Vi bemærker brugen af ​​flere udtryk for at henvise til en type, der arves af en anden type, specifikt:

  • en basistype kaldes også en super- eller en overordnet type
  • en afledt type kaldes en udvidet, under- eller en underordnet type

3. Klassearv

3.1. Udvidelse af en klasse

En klasse kan arve en anden klasse og definere yderligere medlemmer.

Lad os starte med at definere en basisklasse Bil:

offentlig klasse bil {int hjul; Strengmodel; ugyldig start () {// Kontroller vigtige dele}}

Klassen Pansret bil kan arve medlemmerne af Bil klasse efter ved hjælp af nøgleordet strækker sig i sin erklæring:

offentlig klasse ArmoredCar udvider Car {int bulletProofWindows; ugyldig remoteStartCar () {// dette køretøj kan startes ved hjælp af en fjernbetjening}}

Vi kan nu sige, at Pansret bil klasse er en underklasse af Bil, og sidstnævnte er en superklasse af Pansret bil.

Klasser i Java understøtter enkelt arv; det Pansret bil klasse kan ikke udvide flere klasser.

Bemærk også, at i mangel af et strækker sig nøgleord, en klasse arver implicit klasse java.lang.Objekt.

En underklasseklasse arver det ikke-statiske beskyttet og offentlig medlemmer fra superklasseklassen. Derudover har medlemmerne med Standard og pakke adgang arves, hvis de to klasser er i samme pakke.

På den anden side er privat og statisk medlemmer af en klasse arves ikke.

3.2. Adgang til forældremedlemmer fra en børneklasse

For at få adgang til nedarvede egenskaber eller metoder kan vi blot bruge dem direkte:

public class ArmoredCar udvider Car {public String registerModel () {returmodel; }}

Bemærk, at vi ikke har brug for en henvisning til superklassen for at få adgang til dens medlemmer.

4. Grænsearv

4.1. Implementering af flere grænseflader

Selvom klasser kun kan arve en klasse, kan de implementere flere grænseflader.

Forestil dig Pansret bil som vi definerede i det foregående afsnit er påkrævet for en super spion. Så Bil produktionsvirksomhed tænkte på at tilføje flyvende og flydende funktionalitet:

offentlig grænseflade Floatable {void floatOnWater (); }
offentlig grænseflade Flyable {void fly (); }
offentlig klasse ArmoredCar udvider Bilværktøjer Floatable, Flyable {public void floatOnWater () {System.out.println ("Jeg kan flyde!"); } public void fly () {System.out.println ("Jeg kan flyve!"); }}

I eksemplet ovenfor bemærker vi brugen af ​​nøgleordet redskaber at arve fra en grænseflade.

4.2. Problemer med flere arv

Java tillader flere arv ved hjælp af grænseflader.

Indtil Java 7 var dette ikke et problem. Grænseflader kunne kun definere abstrakt metoder, dvs. metoder uden nogen implementering. Så hvis en klasse implementerede flere grænseflader med den samme metodesignatur, var det ikke et problem. Implementeringsklassen havde til sidst kun en metode at implementere.

Lad os se, hvordan denne enkle ligning ændrede sig med introduktionen af Standard metoder i grænseflader med Java 8.

Fra og med Java 8, kunne grænseflader vælge at definere standardimplementeringer til dens metoder (en grænseflade kan stadig definere abstrakt metoder). Dette betyder, at hvis en klasse implementerer flere grænseflader, der definerer metoder med den samme signatur, vil underordnede klasse arve separate implementeringer. Dette lyder komplekst og er ikke tilladt.

Java tillader ikke arv af flere implementeringer af de samme metoder, defineret i separate grænseflader.

Her er et eksempel:

offentlig grænseflade Flydbar {standard ugyldig reparation () {System.out.println ("Reparation af flydende objekt"); }}
offentlig grænseflade Flybar {standard ugyldig reparation () {System.out.println ("Reparation af flygtigt objekt"); }}
offentlig klasse ArmouredCar udvider bilværktøjer Flydbare, flyvbare {// dette kompileres ikke}

Hvis vi ønsker at implementere begge grænseflader, bliver vi nødt til at tilsidesætte reparation() metode.

Hvis grænsefladerne i de foregående eksempler definerer variabler med samme navn, sig varighed, vi kan ikke få adgang til dem uden at gå forud for variabelnavnet med interface-navnet:

offentlig grænseflade Flydbar {int varighed = 10; }
offentlig grænseflade Flybar {int varighed = 20; }
offentlig klasse ArmoredCar udvider Bilværktøjer Flydbare, flybare {offentlige ugyldige aMethod () {System.out.println (varighed); // kompilerer ikke System.out.println (Floatable.duration); // udgange 10 System.out.println (Flyable.duration); // output 20}}

4.3. Grænseflader, der udvider andre grænseflader

En grænseflade kan udvide flere grænseflader. Her er et eksempel:

offentlig grænseflade Floatable {void floatOnWater (); }
interface interface Flyable {void fly (); }
offentlig grænseflade SpaceTraveller udvider Floatable, Flyable {void remoteControl (); }

En grænseflade arver andre grænseflader ved hjælp af nøgleordet strækker sig. Klasser bruger nøgleordet redskaber at arve en grænseflade.

5. Arvetype

Når en klasse arver en anden klasse eller grænseflader, bortset fra at arve deres medlemmer, arver den også deres type. Dette gælder også for en grænseflade, der arver andre grænseflader.

Dette er et meget kraftigt koncept, som gør det muligt for udviklere at program til en grænseflade (basisklasse eller grænseflade), snarere end at programmere til deres implementeringer.

Forestil dig f.eks. En tilstand, hvor en organisation fører en liste over de biler, der ejes af sine ansatte. Selvfølgelig kan alle medarbejdere eje forskellige bilmodeller. Så hvordan kan vi henvise til forskellige bilforekomster? Her er løsningen:

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

Fordi alle afledte klasser af Bil arve typen Bil, kan de afledte klasseinstanser henvises ved hjælp af en variabel af klasse Bil:

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

6. Skjulte klassemedlemmer

6.1. Medlemmer af skjulte instanser

Hvad sker der hvis både superklassen og underklassen definerer en variabel eller metode med samme navn? Bare rolig; vi kan stadig få adgang til dem begge. Vi skal dog gøre vores hensigt klar over for Java ved at forudse variablen eller metoden med nøgleordene det her eller super.

Det det her nøgleord refererer til det tilfælde, hvor det bruges. Det super nøgleord (som det ser ud til at være indlysende) henviser til den overordnede klasseinstans:

offentlig klasse ArmoredCar udvider Car {private String model; public String getAValue () {return super.model; // returnerer værdi af model defineret i basisklasse Car // returnerer this.model; // returnerer værdi af model defineret i ArmoredCar // returmodel; // returnerer værdien af ​​modellen defineret i ArmoredCar}}

Mange udviklere bruger det her og super nøgleord for eksplicit at angive, hvilken variabel eller metode de henviser til. Brug af dem med alle medlemmer kan dog få vores kode til at se rodet ud.

6.2. Skjulte statiske medlemmer

Hvad der sker når vores basisklasse og underklasser definerer statiske variabler og metoder med samme navn? Kan vi få adgang til en statisk medlem fra basisklassen, i den afledte klasse, den måde, vi gør for instansvariablerne på?

Lad os finde ud af ved hjælp af et eksempel:

public class Car {public static String msg () {return "Car"; }}
public class ArmoredCar udvider bil {public static String msg () {return super.msg (); // dette kompileres ikke. }}

Nej, det kan vi ikke. De statiske medlemmer hører til en klasse og ikke til tilfælde. Så vi kan ikke bruge det ikke-statiske super nøgleord i msg ().

Da statiske medlemmer tilhører en klasse, kan vi ændre det foregående opkald som følger:

returner Car.msg ();

Overvej følgende eksempel, hvor både baseklassen og den afledte klasse definerer en statisk metode msg () med samme underskrift:

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

Sådan kan vi kalde dem:

Bil først = ny pansret bil (); ArmoredCar second = new ArmoredCar ();

For den foregående kode, first.msg () vil sende “Bil og second.msg () vil sende "ArmoredCar". Den statiske meddelelse, der kaldes, afhænger af typen af ​​den variabel, der bruges til at henvise til Pansret bil eksempel.

7. Konklusion

I denne artikel dækkede vi et kerneaspekt af Java-sproget - arv.

Vi så, hvordan Java understøtter enkelt arv med klasser og flere arv med grænseflader og diskuterede indviklingen i, hvordan mekanismen fungerer på sproget.

Som altid er den fulde kildekode til eksemplerne tilgængelig på GitHub.