Programmering 1999 Forelæsning 11, tirsdag 5. oktober 1999 Oversigt Klasse-hierarkier: superklasser og subklasser Nedarvning Polymorfi Programmering 1999 KVL Side 11-1 Hierarkier En klasse repræsenterer et begreb, f.eks. tidspunkt, ko eller bøgetræ. Nogle begreber kan naturligt ordnes i hierarkier. Eksempler: Dyr kan opdeles i pattedyr, fugle, fisk... Et træ kan være en bøg, en ask, en eg,... Objektorienterede programmeringssprog skulle netop være gode til at modellere ting fra den virkelige verden...... så man kan naturligvis også modellere hierarkier i Java. Programmering 1999 KVL Side 11-2 Klasse-hierarkier En klasse kan udvide en anden klasse. En klasse der udvider en anden kaldes en subklasse af den anden. Den klasse der udvides kaldes for subklassens superklasse. En klasse der udvider en anden erklæres som følger: class subklasse-navn extends superklasse-navn { variable og metoder Man skal kunne sige subklasse er en superklasse. Superklassen er det generelle, subklassen mere specifik. Programmering 1999 KVL Side 11-3 Et eksempel på et klasse-hierarki Et træ(arts)-hierarki, hvor <- går fra subklasse til superklasse: Tree <- Beech <- Ash <- Spruce <- NorwaySpruce <- SitkaSpruce Her er klasserne Beech, Ash og Spruce subklasser af Tree. SitkaSpruce er en subsubklasse af Tree. NorwaySpruce har superklassen Spruce, som igen har superklassen Tree. Man kan sige en bøg er et træ eller en rødgran er en gran og en gran er et træ. Programmering 1999 KVL Side 11-4
Alternative betegnelser superklasse subklasse underklasse superclass subclass parent class child class Programmering 1999 KVL Side 11-5 Nedarvning En subklasse arver ikke-private metoder og felter fra sin superklasse. De kan altså bruges i subklassen som var de defineret der. En subklasse kan derudover definere sine egne felter og metoder. Eksempel: Ash og Beech som subklasser af Tree Vi vil nu definere følgende hierarki: Tree <- Beech <- Ash Klassen Tree skal repræsentere hvad der er fælles for alle (slags) træer. Klassen Beech skal repræsentere bøgetræer og Ash asketræer. Beech og Ash skal arve egenskaber der er fælles fra Tree og hver definere egenskaber der er specielle for netop bøg hhv. ask. Programmering 1999 KVL Side 11-6 Træarts-hierarki: Tree class Tree { int age; // in years public Tree(int age) { this.age = age; public void onemoreyear() { age++; Et træ har en alder (age). Et træ kan blive et år ældre (onemoreyear). Programmering 1999 KVL Side 11-7 T ræarts-hierarki: Ash class Ash extends Tree {... // constructor here return age * 40.0 / 150.0; double mortality = Math.pow((age-80.0)/71, 4); return age >= 150 (age >= 80 && Math.random() < mortality); En ask har en højde der afhænger af alderen (height). Det kan undersøges om asken dør ved en given alder (dies). Programmering 1999 KVL Side 11-8
T ræarts-hierarki: Beech class Beech extends Tree {... // constructor here return age * 40.0 / 280.0; double mortality = Math.pow((age-250.0)/31, 4); return age >= 280 (age >= 250 && (Math.random() < mortality)); En bøg har en højde der afhænger af alderen (height). Det kan undersøges om bøgen dør ved en given alder (dies). Programmering 1999 KVL Side 11-9 Kunne age erklæres private? Hvis feltet age i Tree erklæres private kan det ikke bruges af metoder i subklasserne. Hvis age ikke erklæres private kan det på den anden side ses og ændres af alle. En løsning: derfiner getage i Tree: class Tree { private int age; public int getage() { return age;... Programmering 1999 KVL Side 11-10 Ændrede metoder i Ash: Nu kan subklasserne (og andre) læse alderen men ikke ændre den: return getage() * 40.0 / 150.0; int age = getage(); double mortality = Math.pow((age-80.0)/71, 4); return age >= 150 (age >= 80 && Math.random() < mortality); Programmering 1999 KVL Side 11-11 Hvad med konstruktorerne? Konstruktorer arves ikke. En subklasses konstruktor kan som det første den gør kalde en konstruktor for superklassen. Dette gøres med kaldet super(... ). Subklasse-konstruktorer der ikke kalder en anden konstruktor har et implicit kald super() som første ordre, og superklassen skal derfor have en kontruktor uden argumenter. Programmering 1999 KVL Side 11-12
Konstruktorer for Ash og Beech class Beech extends Tree { public Beech(int age) { super(age);... // methods class Ash extends Tree { public Ash(int age) { super(age);... // methods Programmering 1999 KVL Side 11-13 Overskrivning og polymorfi En subklasse kan overskrive (redefinere) metoder fra superklassen. En variabel der kan referere til et objekt af en bestemt klasse kan også referere til et objekt af en af klassens subklasser. Eksempel: En variabel af typen Tree kan referere til et objekt af en af klasserne Tree, Ash eller Beech. Hvilken version af en overskreven metode der kaldes afhænger af objektets klasse, ikke af variablens type. Dette kaldes polymorfi (flere former). Spørgsmål: Hvilket af følgende er tilladt med klasserne Tree og Ash defineret som hidtil? Tree t = new Ash(12); System.out.println("Træets alder: " + t.getage()); System.out.println("Træets højde: " + t.height()); Programmering 1999 KVL Side 11-14 Overskrivning i Tree hierarkiet Vi definerer følgende metoder i Tree klassen: return 0; // Arbitrary value return false; // Arbitrary value Metoderne height() og dies() i klasserne Ash og Beech overskriver disse metoder fra Tree. Dvs. at alle træer har en højde og alle træer kan dø, men den konkrete model afhænger af træarten (mere om dette i forelæsning 12 hvor vi når til abstrakte klasser). Programmering 1999 KVL Side 11-15 Polymorfi i Tree hierarkiet Lad os antage at vi har en variabel t defineret som Tree t; og at vi senere laver tildelingen t = new Ash(12); Vi kan nu skrive noget som System.out.println(t.height()); for at udskrive højden på træet. Her kaldes metoden height() fra klassen Ash.A Dvs. at ved at definere metoden height() i Tree kan vi nu bede om højden på et træ uden at vide om det er en bøg eller en ask. Men metoden som defineret i Tree bliver aldrig kaldt! Programmering 1999 KVL Side 11-16
Et sted i skoven: Klassen Plot // A plot is a location in the forest. It may hold a number of trees class Plot { Tree [] trees; Plot(int max_trees) { trees = new Tree[max_trees]; void dies() { for (int i = 0; i < trees.length; i++) if (trees[i]!= null && trees[i].dies()) trees[i] = null; // Remove dead tree void onemoreyear() { for (int i = 0; i < trees.length; i++) if (trees[i]!= null ) trees[i].onemoreyear(); Programmering 1999 KVL Side 11-17 Noget om typekonvertering Ved en erklæring som Tree t = new Ash(12) bliver (referencen) til det dannede Ash objekt implicit typekonverteret til (en reference til) Tree. En sådan generaliserende typekonvertering fra subklasse-reference til superklasse-reference sker ganske automatisk og implicit hvor der er brug for den og den er altid lovlig. Den modsatte konvertering fra superklasse-reference til subklasse-reference er en specialiserende typekonvertering og skal altid skrives eksplicit. En konvertering fra superklasse til subklasse kan også gå galt, da det jo ikke er sikkert at objektet rent faktisk tilhører den pågældende subklasse. Eksempel: Vi kan altid skrive: Tree t = new Ash(12); Men kun een af følgende konverteringer vil gå godt: Ash a = (Ash)t; Beech b = (Beech)t; Man kan undersøge objekters klasse med instanceof operatoren: if (t instanceof Ash) a = (Ash)t;... men hvis man har for meget brug for dette er der nok snarere noget galt med ens klassehierarki. Programmering 1999 KVL Side 11-18 Kort om klassen Object Alle klasser i java nedstammer fra klassen Object. Da det gælder alle klasser skriver man aldrig eksplicit extends Object nogen steder. Alle klasser arver metoderne fra Object, herunder en tostring() metode (som dog ikke nødvendigvis producerer noget fornuftigt). De tostring metoder vi har defineret overskriver altså alle sammen tostring fra Object. Det er defor at for eksempel følgende tegnstregns-sammensætning er tilladt: System.out.println("Middag: " + new Time(12,0)); Her kaldes tostring() implicit. Hvis den er defineret i Time bruges denne definition, ellers tages tostring fra Object. Alle objekter (eller rettere: referencer til objekter) kan typekonverteres til Object, hvilket vi benyttede os af i strukturen ObjectList som blev defineret til forelæsning 9. Programmering 1999 KVL Side 11-19 Noget om brugen af super Man kan i en subklasses metoder og konstruktorer bruge super referencer for at få fat i felter, metoder og konstruktorer for superklassen. Vi har allerede brugt super(... ) på første linie i en konstruktor for at få fat i superklassens konstruktor. Man kan også bruger super for at få fat i en metode fra superklassen som bliver overskrevet i subklassen. Eksempel: Hvis der af en eller anden grund var behov for at overskrive metoden onemoreyear fra Tree i klassen Ash kunne det se sådan ud i Ash: public void onemoreyear() { super.onemoreyear(); // First call onemoreyear from Tree... // Here some special code needed for Ash trees Her behøver super.onemoreyear() kaldet ikke nødvendigvis at være det allerførste i metoden, men sådan vil det ofte være. Ofte bruger man også en værdi fra en af superklassens metoder for at udregne en værdi i den tilsvarende metode i subklassen: public String tostring() { return super.tostring()+" ekstra information"; Programmering 1999 KVL Side 11-20
Eksempel: Beholdere class Vessel { private double contents; public double getcontents() { return contents; public double volume() { return 0; // Arbitrary value public void fill(double amount) { contents += amount; contents = Math.min(contents, volume()); public void tap(double amount) { contents -= amount; contents = Math.max(contents, 0); Programmering 1999 KVL Side 11-21 Eksempel: Beholdere (kasseformet tank) class Tank extends Vessel { // A box-shaped tank private double length, width, height; public Tank(double length, double width, double height) { this.length = length; this.width = width; this.height = height; public double volume() { return length * width * height; Programmering 1999 KVL Side 11-22 Eksempel: Beholdere (tønde) class Barrel extends Vessel { private double radius, height; public Barrel(double radius, double height) { this.radius = radius; this.height = height; public double volume() { return height * Math.PI * radius * radius; Programmering 1999 KVL Side 11-23 Eksempel: Beholdere (trug) class Trough extends Vessel { private double length, width; public Trough(double length, double width) { this.length = length; this.width = width; public double volume() { return length * Math.PI * (width/2) * (width/2) / 2; Programmering 1999 KVL Side 11-24
Eksempel: Test af beholdere Et par hjælpemetoder der håndterer tabeller af vilkårlige beholdere: static double volume(vessel[] vs) { double sum = 0; for (int i=0; i<vs.length; i++) sum += vs[i].volume(); return sum; static double contents(vessel[] vs) { double sum = 0; for (int i=0; i<vs.length; i++) sum += vs[i].getcontents(); return sum; Programmering 1999 KVL Side 11-25 Eksempel: Test af beholdere Endnu et par metoder til at håndtere vilkårlige tabeller af beholdere: static double fill(vessel[] vs, double amount) { double remains = amount; int i = 0; while (remains > 0 && i < vs.length) { double room = vs[i].volume() - vs[i].getcontents(); double tofill = Math.min(remains, room); vs[i].fill(tofill); remains -= tofill; i++; return remains; static void print(vessel[] vs) { for (int i=0; i<vs.length; i++) System.out.println("Vessel " + i + ": volume " + vs[i].volume() + " and contents " + vs[i].getcontents()); Programmering 1999 KVL Side 11-26 Eksempel: Test af beholdere class TestVessel1 { public static void main(string[] args) { Vessel[] vs = {new Tank(1, 1.5, 2), new Barrel(.5, 3), new Trough(1, 2); print(vs); System.out.println("Total volume: " + volume(vs)); fill(vs, 6.0); print(vs); System.out.println("Total contents: " + contents(vs));... // methods from previous slides here Programmering 1999 KVL Side 11-27 Sammenfatning Klasser kan ordnes i hierarkier. En subklasse udvider (extends) en superklasse. En subklasse arver superklassens ikke-private felter og metoder. Konstruktorer nedarves ikke. En subklasse kan overskrive metoder defineret i superklassen. Polymorfi: En variabel der kan referere til et objekt af en bestemt klasse må også referere til objekter af dens subklasser. Hvilken overskrevet udgave af en metode der kaldes afhænger af objektet, ikke variablen. Typekonvertering fra subklasse-reference til superklasse-reference sker implicit mens konvertering den anden vej skal ske eksplicit. Læs: L&L afsnit 8.1 8.4. Programmering 1999 KVL Side 11-28