En klassdefinition
class A extends B { ... }
definierar en klass A
som ärver av B
.
Här gäller:
A
ärver alla instansvariabler och metoder som är definierade för B
.A
är en subklass till B
.B
är en superklass till A
.Anta att vi vill definiera fyra klasser:
En bil är förstås ett slags motorfordon, varje motorfordon är också ett fordon och en cykel är också ett fordon (men inte ett motorfordon).
Vi antar att bilar och cyklar har en toppfart. För att göra exemplet intressantare antar vi att:
Klassen Fordon
definierar inga metoder eller instansvariabler:
class Fordon { }
Klassen Motorfordon
ärver av Fordon
. Här introduceras en
instansvariabel toppfart
samt en metod getToppfart
.
Konstuktorn tar ett argument t
(toppfart) och tilldelar
instansvariabeln detta värde.
class Motorfordon extends Fordon { private double toppfart; Motorfordon (double t) { toppfart = t; } double getToppfart() { return toppfart; } }
Klassen Bil
ärver av MotorFordon
. Vi lägger till två instansvariabler,
antal_hjul
och motorstyrka
.
Konstruktorn tar två argument, m
(motorstyrka) och h
(antal hjul),
tilldelar instansvariablerna dessa värden samt anropar superklassens
konstruktor med motorstyrka gånger 1.5.
class Bil extends Motorfordon { int motorstyrka; int antal_hjul; Bil(int m, int h){ super(m * 1.5); //Antar att toppfarten är motorstyrkan * 1.5 motorstyrka = m; antal_hjul = h; } }
Klassen Cykel
har en instansvariabel, cyklist
.
Konstruktorn tar en person (dvs ett objekt av klassen Person
) som
argument och tilldelar instansvariabeln cyklist
detta värde.
Klassen Person
har ni redan sett, men här kommer den igen:
class Person { int ålder; String namn; }
Metoden getToppfart
definieras så att den beror av cyklistens ålder.
(Låt mig här be alla äldre cyklister om ursäkt—exemplet är larvigt men jag har inte kommit på nåt bättre...)
Det intressanta är att toppfarten för cyklar beräknas varje gång
metoden anropas, till skillnad från motsvarande definition i klassen
Motorfordon
.
class Cykel extends Fordon { private Person cyklist; Cykel (Person p) { cyklist = p; } double getToppfart () { if (cyklist.ålder > 60) return 10; if (cyklist.ålder > 25) return 20; return 30; //Larvigt... } }
I många andra objektorienterade programspråk kan man skapa ett objekt men glömma att initiera det. Det är en ganska vanlig källa till buggar, så Javas designers försökte ta bort denna felkälla genom att införa konstruktorer,
Hur kan klasser kan relateras genom arv?
Som ni har sett betyder arv att en klass ärver implementation av en annan.
I ett välskrivet program bör arv endast användas när klasserna är konceptuellt relaterade.
I exemplet gäller det ju att
En god tumregel är att endast använda arv när
Det händer ofta att man har två klasser som verkar vara relaterade på nåt sätt men det passar ändå inte att låta en av dem ärva från den andra.
Exempel: Anta att du har två klasser, Bil
och Cykel
. Det är ju
uppenbart att klasserna har nåt gemensamt, men man kan inte säga att
bilar är en slags cyklar (eller tvärtom).
Lösningen är vanligtvis att introducera en ny klass som rymmer
gemensamma datastrukturer och metoder och som båda klasserna får ärva
ifrån. I exemplet har vi förstås en klass Fordon
.
När man resonerar om ett objektorienterat program måste man tänka på två sätt:
Notera: Klassen som beskriver bilar heter "Bil" eftersom varje instans av klassen beskriver en bil. Låt inte en klass ha ett namn i plural—till exempel "Bilar" eller "Cyklar".
För att besvara frågorna nedan blir du tvungen att gå utanför anteckningarna. Kolla i kursboken eller på nätet. Det kan också hjälpa att skriva enkla testprogram för att kontrollera att din förståelse är korrekt.
A
som ärver en klass B
, vad gäller om en metod
definieras både i superklass och i subklass?