Programmering

Arv i Java, del 1: Nøgleordet udvider

Java understøtter genbrug af klasser gennem arv og komposition. Denne tutorial i to dele lærer dig, hvordan du bruger arv i dine Java-programmer. I del 1 lærer du, hvordan du bruger strækker sig nøgleord for at udlede en underordnet klasse fra en overordnet klasse, påberåbe sig overordnede klassekonstruktører og metoder og tilsidesætte metoder. I del 2 turnerer du java.lang.Objekt, som er Java's superklasse, som hver anden klasse arver fra.

For at fuldføre din læring om arv skal du sørge for at tjekke mit Java-tip, der forklarer, hvornår du skal bruge komposition vs arv. Du lærer, hvorfor komposition er et vigtigt supplement til arv, og hvordan du bruger den til at beskytte mod problemer med indkapsling i dine Java-programmer.

download Hent koden Download kildekoden for eksempel applikationer i denne vejledning. Oprettet af Jeff Friesen til JavaWorld.

Java-arv: To eksempler

Arv er en programmeringskonstruktion, som softwareudviklere bruger til at etablere er-et forhold mellem kategorier. Arv giver os mulighed for at udlede mere specifikke kategorier fra mere generiske. Den mere specifikke kategori er en slags den mere generiske kategori. For eksempel er en kontrolkonto en slags konto, hvor du kan foretage indbetalinger og udbetalinger. Tilsvarende er en lastbil en slags køretøj, der bruges til at trække store genstande.

Arv kan falde ned ad flere niveauer, hvilket fører til stadig mere specifikke kategorier. Som et eksempel viser figur 1 bil og lastbil arvet fra køretøjet; stationcar arve fra bil; og skraldebil arve fra lastbil. Pile peger fra mere specifikke "underordnede" kategorier (lavere ned) til mindre specifikke "overordnede" kategorier (højere op).

Jeff Friesen

Dette eksempel illustrerer enkelt arv hvor en barnekategori arver tilstand og adfærd fra en umiddelbar forældrekategori. I modsætning, flere arv gør det muligt for en underkategori at arve tilstand og adfærd fra to eller flere umiddelbare forældrekategorier. Hierarkiet i figur 2 illustrerer flere arv.

Jeff Friesen

Kategorier er beskrevet af klasser. Java understøtter enkelt arv igennem klasseudvidelse, hvor en klasse direkte arver tilgængelige felter og metoder fra en anden klasse ved at udvide denne klasse. Java understøtter dog ikke flere arv gennem klasseudvidelser.

Når du ser et arvshierarki, kan du nemt registrere flere arv ved tilstedeværelsen af ​​et diamantmønster. Figur 2 viser dette mønster i sammenhæng med køretøj, landkøretøj, vandkøretøj og svævefly.

Nøgleordet udvider

Java understøtter klasseudvidelse via strækker sig nøgleord. Når til stede, strækker sig angiver et forhold mellem forældre og barn mellem to klasser. Nedenfor bruger jeg strækker sig at etablere et forhold mellem klasser Køretøj og Bilog derefter mellem Konto og Opsparingskonto:

Notering 1. strækker sig nøgleord angiver et forhold mellem forælder og barn

klasse Køretøj {// medlemserklæringer} klasse Bil udvider Køretøj {// arver tilgængelige medlemmer fra køretøj // leverer egne medlemserklæringer} klasse Konto {// medlemserklæringer} klasse SavingsAccount udvider konto {// arver tilgængelige medlemmer fra konto // giver egne medlemserklæringer}

Det strækker sig nøgleord er angivet efter holdnavnet og før et andet holdnavn. Klassens navn før strækker sig identificerer barnet og klassens navn efter strækker sig identificerer forælderen. Det er umuligt at specificere flere klassenavne efter strækker sig fordi Java ikke understøtter klassebaseret flere arv.

Disse eksempler kodificerer is-a-forhold: Biler en specialiseret Køretøj og Opsparingskontoer en specialiseret Konto. Køretøj og Konto er kendt som baseklasser, forældreklasser, eller superklasser. Bil og Opsparingskonto er kendt som afledte klasser, børneklasser, eller underklasser.

Afsluttende klasser

Du kan muligvis erklære en klasse, der ikke bør udvides; for eksempel af sikkerhedsmæssige årsager. I Java bruger vi endelig nøgleord for at forhindre, at nogle klasser udvides. Du skal bare prefixe en klasseoverskrift med endelig, som i sidste klasse adgangskode. I betragtning af denne erklæring rapporterer compileren en fejl, hvis nogen forsøger at udvide Adgangskode.

Børneklasser arver tilgængelige felter og metoder fra deres overordnede klasser og andre forfædre. De arver dog aldrig konstruktører. I stedet erklærer børneklasser deres egne konstruktører. Desuden kan de erklære deres egne felter og metoder til at skelne dem fra deres forældre. Overvej liste 2.

Notering 2. An Konto forældreklasse

klassekonto {privat strengnavn; privat langt beløb Konto (strengnavn, langt beløb) {this.name = navn; setAmount (beløb); } ugyldigt depositum (langt beløb) {this.amount + = beløb; } String getName () {return name; } lang getAmount () {returbeløb; } ugyldigt setAmount (langt beløb) {this.amount = beløb; }}

Fortegnelse 2 beskriver en generisk bankkontoklasse, der har et navn og et indledende beløb, som begge er angivet i konstruktøren. Det lader også brugerne foretage indbetalinger. (Du kan foretage udbetalinger ved at deponere negative pengebeløb, men vi ignorerer denne mulighed.) Bemærk, at kontonavnet skal indstilles, når en konto oprettes.

Repræsenterer valuta værdier

antal øre. Du foretrækker måske at bruge en dobbelt eller a flyde at gemme monetære værdier, men at gøre det kan føre til unøjagtigheder. Overvej for en bedre løsning BigDecimal, som er en del af Java's standard klassebibliotek.

Liste 3 præsenterer a Opsparingskonto børneklasse, der udvider sin Konto forældreklasse.

Liste 3. A Opsparingskonto børneklasse udvider sin Konto forældreklasse

klasse SavingsAccount udvider konto {SavingsAccount (langt beløb) {super ("besparelser", beløb); }}

Det Opsparingskonto klasse er trivielt, fordi det ikke behøver at erklære yderligere felter eller metoder. Det erklærer dog en konstruktør, der initialiserer felterne i sin Konto superklasse. Initialisering sker når Kontokonstruktør kaldes via Java super nøgleord efterfulgt af en parentes-argumentliste.

Hvornår og hvor skal man kalde super ()

Ligesom det her() skal være det første element i en konstruktør, der kalder en anden konstruktør i samme klasse, super() skal være det første element i en konstruktør, der kalder en konstruktør i sin superklasse. Hvis du bryder denne regel, rapporterer compileren en fejl. Compileren rapporterer også en fejl, hvis den registrerer en super() indkald en metode kun altid ringe super() i en konstruktør.

Liste 4 udvides yderligere Konto med en Checkkonto klasse.

Fortegnelse 4. A Checkkonto børneklasse udvider sin Konto forældreklasse

klasse CheckingAccount udvider konto {CheckingAccount (langt beløb) {super ("kontrol", beløb); } ugyldig tilbagetrækning (langt beløb) {setAmount (getAmount () - beløb); }}

Checkkonto er lidt mere omfattende end Opsparingskonto fordi det erklærer en trække sig tilbage () metode. Bemærk denne metodes opkald til setAmount () og getAmount (), hvilken Checkkonto arver fra Konto. Du kan ikke direkte få adgang til beløb felt i Konto fordi dette felt er erklæret privat (se liste 2).

super () og konstruktøren uden argument

Hvis super() er ikke specificeret i en underklassekonstruktør, og hvis superklassen ikke erklærer en ikke-argument konstruktør, så rapporterer compileren en fejl. Dette skyldes, at underklassekonstruktøren skal kalde en ikke-argument superklasse konstruktør hvornår super() er ikke til stede.

Eksempel på klassehierarki

Jeg har oprettet en AccountDemo applikationsklasse, der giver dig mulighed for at prøve Konto klassehierarki. Se først på AccountDemokildekode.

Liste 5. AccountDemo viser kontoklassens hierarki

klasse AccountDemo {public static void main (String [] args) {SavingsAccount sa = new SavingsAccount (10000); System.out.println ("kontonavn:" + sa.getName ()); System.out.println ("startbeløb:" + sa.getAmount ()); sa. deponering (5000); System.out.println ("nyt beløb efter indbetaling:" + sa.getAmount ()); CheckingAccount ca = ny CheckingAccount (20000); System.out.println ("kontonavn:" + ca.getName ()); System.out.println ("startbeløb:" + ca.getAmount ()); ca. deponering (6000); System.out.println ("nyt beløb efter indbetaling:" + ca.getAmount ()); ca. træk (3000); System.out.println ("nyt beløb efter tilbagetrækning:" + ca.getAmount ()); }}

Det hoved () metode i Listing 5 demonstrerer først Opsparingskonto, derefter Checkkonto. Antager Konto.java, SavingsAccount.java, CheckingAccount.javaog AccountDemo.java kildefiler er i samme bibliotek, udfør en af ​​følgende kommandoer for at kompilere alle disse kildefiler:

javac AccountDemo.java javac * .java

Udfør følgende kommando for at køre applikationen:

java AccountDemo

Du skal overholde følgende output:

kontonavn: besparelse startbeløb: 10000 nyt beløb efter indbetaling: 15000 kontonavn: kontrol af oprindeligt beløb: 20000 nyt beløb efter indbetaling: 26000 nyt beløb efter udbetaling: 23000

Metodeoverstyring (og metodeoverbelastning)

En underklasse kan tilsidesætte (erstat) en nedarvet metode, så subklassens version af metoden kaldes i stedet. En overordnet metode skal angive samme navn, parameterliste og returtype som den metode, der tilsidesættes. For at demonstrere har jeg erklæret en Print() metode i Køretøj klasse nedenfor.

Liste 6. Erklæring om a Print() metode, der skal tilsidesættes

klasse køretøj {privat streng mærke; privat streng model; privat int år; Vehicle (String make, String model, int year) {this.make = make; this.model = model; dette.år = år; } String getMake () {return make; } String getModel () {returmodel; } int getYear () {returår; } ugyldig udskrivning () {System.out.println ("Make:" + make + ", Model:" + model + ", Year:" + year); }}

Dernæst tilsidesætter jeg Print() i Lastbil klasse.

Fortegnelse 7. Tilsidesættelse Print() i en Lastbil underklasse

klasse Truck udvider køretøj {privat dobbelt tonnage; Lastbil (streng mærke, streng model, int år, dobbelt tonnage) {super (mærke, model, år); denne.tonnage = tonnage; } dobbelt getTonnage () {retur tonnage; } ugyldig udskrivning () {super.print (); System.out.println ("Tonnage:" + tonnage); }}

Lastbil's Print() metoden har samme navn, returtype og parameterliste som Køretøj's Print() metode. Bemærk også det Lastbil's Print() metode første opkald Køretøj's Print() metode ved at prefikse super. til metodens navn. Det er ofte en god ide at udføre superklasselogikken først og derefter udføre subklasselogikken.

Opkald til superklassemetoder fra underklassemetoder

For at kalde en superklassemetode fra den overordnede underklassemetode skal du prefikse metodens navn med det reserverede ord super og medlemsadgangsoperatøren. Ellers vil du ende med at rekursivt kalde underklassens overordnede metode. I nogle tilfælde maskerer en underklasse ikkeprivat superklassefelter ved at erklære felter med samme navn. Du kan bruge super og medlemsadgangsoperatøren for at få adgang til den ikke-privat superklasse felter.

For at fuldføre dette eksempel har jeg uddraget a VehicleDemo klassens hoved () metode:

Lastbil = ny lastbil ("Ford", "F150", 2008, 0,5); System.out.println ("Make =" + truck.getMake ()); System.out.println ("Model =" + truck.getModel ()); System.out.println ("Year =" + truck.getYear ()); System.out.println ("Tonnage =" + truck.getTonnage ()); truck.print ();

Den sidste linje, truck.print ();, opkald lastbil's Print() metode. Denne metode kalder først Køretøj's Print() at levere truckens mærke, model og årgang så leverer den lastbilens tonnage. Denne del af output er vist nedenfor:

Mærke: Ford, Model: F150, Årgang: 2008 Tonnage: 0.5

Brug endelig til at blokere metode tilsidesættelse

Lejlighedsvis kan det være nødvendigt at deklarere en metode, der af sikkerhed eller af en anden grund ikke bør tilsidesættes. Du kan bruge endelig nøgleord til dette formål. For at forhindre tilsidesættelse skal du blot foretage en metodeoverskrift med endelig, som i endelig String getMake (). Compileren rapporterer derefter en fejl, hvis nogen forsøger at tilsidesætte denne metode i en underklasse.

Metodeoverbelastning vs tilsidesættelse

Antag at du udskiftede Print() metode i liste 7 med nedenstående:

ugyldig udskrivning (streng ejer) {System.out.print ("Ejer:" + ejer); super.print (); }

Den modificerede Lastbil klasse har nu to Print() metoder: den foregående eksplicit deklarerede metode og metoden nedarvet fra Køretøj. Det ugyldigt print (streng ejer) metoden tilsidesætter ikke Køretøj's Print() metode. I stedet for det overbelastning det.

Du kan registrere et forsøg på at overbelaste i stedet for at tilsidesætte en metode på kompileringstidspunktet ved at prefikse en underklasses metodeoverskrift med @Override kommentar:

@Override ugyldigt print (streng ejer) {System.out.print ("Ejer:" + ejer); super.print (); }

Specificering @Override fortæller kompilatoren, at den givne metode tilsidesætter en anden metode. Hvis nogen forsøgte at overbelaste metoden i stedet, rapporterede compileren en fejl. Uden denne kommentar ville compileren ikke rapportere en fejl, fordi metodeoverbelastning er lovlig.

Hvornår skal jeg bruge @Override

Udvikle en vane med at prefixere overordnede metoder med @Override. Denne vane hjælper dig med at opdage overbelastningsfejl meget hurtigere.

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