Distanskursen objektorienterad programmering med Java
Sven-Olof Nyström Uppsala Universitet
April 20, 2006
Tidigare exempel: en fordonshierarki
Fyra klasser:
Fordon (abstrakt)
Motorfordon (abstrakt) Bil
Cykel
Toppfarten för en cykel beräknas från cyklistens ålder.
Toppfarten för en bil beräknas från motorstykan.
Polymorfi
Ordet polymorfi betyder många former
i OO-sammanhang helt enkelt att objekt i en arvshierarki kan ha samma gränssnitt men att implementationen kan skilja sig.
Exempel på polymorfi:
Klassen GeometricShape med subklasser.
utan polymorfi vore begreppen interface och abstrakta klasser meningslösa...
Exempel: en hushierarki
• Vi har en klasshierarki med (bland annat) klasserna Hus, Bostadshus, Flerfamiljshus.
• Ett hus har diverse attribut, till exempel längd och bredd.
• Ett bostadshus är (förstås) ett slags hus. På samma sätt är ett flerfamiljshus ett slags bostadshus.
• Ett flerfamiljshus har ett attribut som anger antalet lägenheter.
public class Hus { double längd;
double bredd;
int antalVåningar;
int senasteRenovering; // årtal
public Hus( double längd, double bredd, int antalVåningar, int senasteRenovering ) {
this.längd = längd;
this.bredd = bredd;
this.antalVåningar = antalVåningar;
this.senasteRenovering = senasteRenovering;
}
public double yta() {
return längd * bredd * antalVåningar; }}
Filen Bostadshus.java
class Bostadshus extends Hus { boolean tilläggsisolerat = false;
public Bostadshus(double längd, double bredd, int antalVåningar, int senasteRenovering ) {
super(längd, bredd, antalVåningar, senasteRenovering);
}
public void isolera() { tilläggsisolerat = true;
} }
Flerfamiljshus.java
public class Flerfamiljshus extends Bostadshus { int antalLägenheter;
int senasteRenovering;
static double hyrePerM2 = 1000;
public Flerfamiljshus(double längd, double bredd, int antalVåningar,
int antalLägenheter,
int senasteYttreRenovering, int senasteInreRenovering ) {
super(längd,bredd,antalVåningar,senasteYttreRenovering);
this.antalLägenheter = antalLägenheter;
senasteRenovering=senasteInreRenovering;}
Flerfamiljshus.java (forts)
public int antalLgh() {
return antalLägenheter;
}
public double yta() {
return längd*bredd*antalVåningar*0.95;
} }
TestHus.java
public class TestHus {
public static void main( String[] args ) {
Bostadshus b = new Bostadshus( 15, 20, 3, 1945 );
Flerfamiljshus f=new Flerfamiljshus(15,20,3,4,1967,1989);
System.out.println( b.yta() );
System.out.println( f.yta() );
} }
Programmet skriver (som sig bör):
900.0 855.0
Dynamisk bindning public class TestHus1 {
public static void main( String[] args ) {
Bostadshus b = new Bostadshus( 15, 20, 3, 1945 );
Flerfamiljshus f=new Flerfamiljshus(15,20,3,4,1967,1989);
Hus h = new Flerfamiljshus(15,20,3,4,1967,1989);
System.out.println( b.yta() );
System.out.println( f.yta() );
System.out.println( h.yta() );
} }
Dynamisk bindning (forts)
Vi deklarerar en referens till Hus och låter denna referera ett Flerfamiljshus. Utskriften blir
900.0 855.0 855.0
Metoden yta() som definierats i klassen Flerfamiljshus används.
Dynamisk bindning
• När man använder överskuggade metoder är
objektets klass som avgör vilken metod som anropas.
• Detta kallas också dynamisk bindning, vilken metod som skall anropas bestäms först när programmet körs.
• Man kan säga: Mottagaren vet hur meddelandet ska tolkas.
Tilldelningsregler och substituerbarhet
Om klassen B är en subklass till klassen A gäller:
Överallt där det krävs ett objekt av klassen A kan man ge ett objekt av klassen B.
Man kan t.ex. skriva en metod som jämför ytan av två hus (fungerar för alla typer av hus):
public boolean större( Hus h ) { return yta() > h.yta();
}
Substituerbarhet, exempel
• En klass Person med subklass ShopKeeper
• Klassen Person lagrar namn och adress etc
• Klassen ShopKeeper lagrar information om affären, etc
• Ett objekt av typen ShopKeeper kan lagras i en
datastruktur där objekt av typen Person väntas (till exempel, en telefonkatalog)
Substituerbarhet, exempel (forts)
• En metod som väntar sig ett objekt Person (som skriver ut namn och adress, till exempel), kan lika gärna ta ett objekt Shopkeeper
• Det finns inget sätt att skilja på ett objekt av typ Person och ett objekt ShopKeeper om du bara får
använda metoder definierade för klassen Person (ok, det finns, men vi ska inte använda det...)
Dolda instansvariabler
Reglerna för överskuggade instansvariabler och metoder skiljer sig åt. Exempel:
public class TestHus2 {
public static void main( String[] args ) {
Flerfamiljshus f=new Flerfamiljshus(15,20,3,4,1996,1994);
Hus h = f;
System.out.println(f.senasteRenovering);
System.out.println(h.senasteRenovering); }}
Dolda, exempel (forts) vi försöker använda den dolda instansvariabeln senasteRenovering. Programmet skriver 1994
1996
För variablen h som är av typen Hus betyder fältet
senasteRenovering alltid det som deklareras i Hus trots att h ju egentligen refererar ett flerfamiljshus.
Funnes inte senasteRenovering i Hus skulle vi få kompileringsfel.
(Som vi tidigare sett är det tvärtom objektets
klasstillhörighet som avgör vilken metod som ska anropas.)
Dolda instansvariabler (forts)
För att komma åt Hus variant av senasteRenovering i klassen Flerfamiljshus skriver vi
super.senasteRenovering.
Om variablen b är deklarerad och initialiserad enligt Flerfamiljshus b = new Flerfamiljshus(...)
Utanför klassen kan man skriva är man hänvisad till att deklarera referenser av superklassen.
Om b är av typ Flerfamiljshus och man vill komma åt den instansvariabel som deklarerades i klassen Hus:
((Hus) b).senasteRenovering
Dolda instansvariabler (forts) Man kan på samma sätt komma åt metoden yta i Hus genom att inne i
Flerfamijshus anropa super.yta(), d.v.s. metoden yta() i Flerfamijshus skulle kunna skrivas:
public double yta() {
return super.yta() * 0.95;
}
Återanvändning av kod är alltid (?) bra!
Både abstrakta klasser och gränssnitt (interface) är exempel på arv genom specifikation.
Polymorfi
Anta att vi har en array av hus.
Anta vidare att bostadshus (som inte är flerfamiljs-) alltid har en lägenhet och att hus som inte är bostadshus har ingen.
En husarray
public class HusArray {
public static void main( String[] args ) { Hus[] husen = new Hus[4];
husen[0] = new Bostadshus( 15, 20, 3, 1945 );
husen[1] =
new Flerfamiljshus( 15, 20, 3, 4, 1967, 1989);
husen[2] = new Hus( 15, 20, 3, 1967);
husen[3] =
new Flerfamiljshus( 100, 30, 10, 12, 1990, 1990);
...
Beräkna det totala antalet lägenheter
static int totaltAntalLägenheter(Hus[] husen) { int lgh = 0;
for (int i=0; i < husen.length; i++) {
if (husen[i] instanceof Flerfamiljshus) {
Flerfamiljshus h = (Flerfamiljshus) husen[i];
lgh += h.antalLgh();
}
else if (husen[i] instanceof Bostadshus) lgh++;
}
return lgh;
}
Alternativ lösning
...
int lgh = 0;
for (int i=0; i < 4; i++) {
lgh = lgh + husena[i].antalLgh();
}
System.out.println("Totalt antal lägenheter: " + lgh );
} }
Förutsätter att alla hus definierar metoden antalLgh.
Alternativ (forts)
Vi säger att objekt av klassen Hus har inga lägenheter.
public class Hus {
public int antalLgh() { return 0; } ... }
... men ett objekt av klassen BostadsHus har exakt en lägenhet.
class Bostadshus extends Hus { public int antalLgh() { return 1; } ... }