Programmering 1999 Forelæsning 12, fredag 8. oktober 1999 Oversigt Abstrakte klasser. Grænseflader. Programmering 1999 KVL Side 12-1 Tree klassen fra sidste forelæsning class Tree { int age; // in years public Tree(int age) { this.age = age; public void onemoreyear() { age++; public double height() { return 0; // arbitrary value public boolean dies() { return false; // arbitrary value Programmering 1999 KVL Side 12-2 Beech klassen fra sidste forelæsning class Beech extends Tree { public Beech(int age) { super(age); public double height() { return age * 40.0 / 280.0; public boolean dies() { double mortality = Math.pow((age-250.0)/31, 4); return age >= 280 (age >= 250 && (Math.random() < mortality)); Programmering 1999 KVL Side 12-3 Abstrakte træer Det er ikke tilfredsstillende at skulle definere vilkårlige metoder height() og dies() i klassen Tree. Det er måske heller ikke så godt at man kan sige new Tree(). Et ikke nærmere specificeret træ er i virkeligheden et abstrakt begreb. Derfor skal Tree defineres som abstrakt klasse. Programmering 1999 KVL Side 12-4
Abstrakte klasser En klasse kan erklæres abstract. En abstrakt klasse kan have abstrakte metoder. abstract class Tree { int age; // in years public Tree(int age) { this.age = age; public void onemoreyear() { age++; abstract public double height(); abstract public boolean dies(); Programmering 1999 KVL Side 12-5 Hvad betyder abstract? En abstrakt klasse kan ikke instantieres (dvs. man kan ikke lave objekter direkte fra den). En abstrakt klasse kan udvides af ikke-abstrakte subklasser. En abstrakt metode repræsenterer en egenskab som alle den abstrakte klasses subklasser skal have. En abstrakt metode defineres uden krop. En subklasse af en abstrakt klasse skal overskrive alle abstrakte metoder (eller selv være abstrakt). En metode der overskriver en abstrakt metode giver en konkret implementation af den abstrakte egenskab. Programmering 1999 KVL Side 12-6 klassen Vessel fra sidste forelæsning class Vessel { private double contents; public double getcontents() { return contents; public double volume() { return 0; 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 12-7 Tank klassen fra sidste forelæsning 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 12-8
klassen Vessel gjort abstrakt abstract class Vessel { private double contents; public double getcontents() { return contents; abstract public double volume(); 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 12-9 Mere om abstrakte metoder og klasser En klasse der indeholder en abstrakt metode skal erklæres abstract. En abstrakt klasse kan kun instantieres gennem sine ikke-abstrakte subklasser. For at bruge en abstrakt klasse må man altså lave subklasser fra den. En klasse kan erklæres abstract selv om den ikke indeholder abstrakte metoder. En abstrakt metode kan ikke erklæres static. Konstruktorer kan ikke være abstrakte. Undlad at erklære en abstrakt metode private... Programmering 1999 KVL Side 12-10 Eksempel: Aftalekalender Vi vil nu lave en aftalekalender der kan håndtere såvel tidsbegrænsede aftaler (Appointment) som husk-at-få-gjort notater (ToDo). Til at repræsentere tidspunkter benytter vi en Time klasse som understøtter følgende metoder: Time(int hours, int min) Konstruktor Time(int hours) Konstruktor (hele timer) Time(Time time) Kopierende konstruktor String tostring() Giver tidspuntet i læsbart format void increment(int min) Læg et antal minutter til (ændrer tidspunktet) Time plus(int min) Giver et nyt tidspunkt et antal minutter senere int to(time t) Giver antallet af minutter fra et tidspunkt til et andet boolean before(time t) Sandt hvis tidspunktet er mindre end eller lig med et andet (Klassen findes i filen u:\dat\prog\eksempler\time5.java) Programmering 1999 KVL Side 12-11 Eksempel: Aftalekalender (CalendarItem) abstract class CalendarItem { public String text; public CalendarItem(String text) { this.text = text; abstract public String tostring(); Programmering 1999 KVL Side 12-12
Eksempel: Aftalekalender (ToDo) class ToDo extends CalendarItem { public boolean done; public ToDo(String text) { super(text); done = false; public String tostring() { if (done) return "Done: " + text; else return "To do: " + text; Programmering 1999 KVL Side 12-13 Eksempel: Aftalekalender (Starten af Appointment) class Appointment extends CalendarItem { private Time starttime, endtime; public Appointment(Time starttime, Time endtime, String text) { super(text); this.starttime = new Time(starttime); this.endtime = new Time(endtime); public Appointment(Time starttime, String text) { this(starttime, starttime.plus(60), text); // One-hour appointment public String tostring() { return "from " + starttime + " to " + endtime + ": " + text; Programmering 1999 KVL Side 12-14 Eksempel: Aftalekalender (resten af Appointment) public void prolong(int min) { endtime.increment(min); public void postpone(int min) { starttime.increment(min); endtime.increment(min); public void move(time newstart) { postpone(starttime.to(newstart)); Programmering 1999 KVL Side 12-15 Eksempel: Aftalekalender (TestAppointment2b, hjælpemetoden print calendar) class TestAppointment2b { static void print_calendar(calendaritem [] calendar) { for (int i = 0; i < calendar.length; i++) if ( calendar[i]!= null ) System.out.println(calendar[i].toString()); System.out.println(); // main method here... Programmering 1999 KVL Side 12-16
Eksempel: Aftalekalender (TestAppointment2b, metoden main) public static void main(string[] args) { ToDo t1 = new ToDo("tag backup"); Appointment a1 = new Appointment(new Time(8), new Time(9, 20), "forelæsning"); Appointment a2 = new Appointment(new Time(13), "møde"); Appointment a3 = new Appointment(new Time(14), new Time(14, 15), "kaffe"); CalendarItem [] friday = {t1, a1, a2, a3; print_calendar(friday); t1.done = true; a2.prolong(60); a3.postpone(60); print_calendar(friday); Programmering 1999 KVL Side 12-17 Multipel nedarving I mange objektorienterede sprog kan en klasse have flere superklasser. Dette kaldes multipel nedarvning (engelsk: multiple inheritace). Multipel nedarvning er nyttig når en klasse modellerer noget som er en kombination af to forskellige ting. Eksempel: Et krystalglas er både en beholder og et musikinstrument. En tank er kun en beholder. En trompet er kun et musikinstrument. Programmering 1999 KVL Side 12-18 Java har ikke multipel nedarvning(!) Java tillader kun udvidelse af en enkelt superklasse. Dette kaldes enkelt nedarvning (engelsk: single inheritance). Programmering 1999 KVL Side 12-19 Grænseflader Java understøtter en erstatning for multipel nedarvning. Den hedder grænseflader (engelsk: interfaces). En grænseflade er en slags rent abstrakt klasse der kun indeholder abstrakte metoder. En klasse kan implementere flere grænseflader (og stadig udvide en superklasse). For at implementere en grænseflade må klassen implementere alle grænsefladens metoder. Programmering 1999 KVL Side 12-20
Eksempel: interface MusicalInstrument { public void play(note note); class CrystalGlass extends Vessel implements MusicalInstrument { public double volume() {... // from Vessel public void play(note note) {... // from MusicalInstrument Programmering 1999 KVL Side 12-21 Noget om grænseflader En grænseflade kan udvide en eller flere andre grænseflader. interface MusicalInstrument extends Playable, Tuneable { En grænseflade kan aldrig udvide en klasse eller implementere en grænseflade. Metoder i grænseflader kan ikke erklæres static, private, protected, final eller synchronized. Alle metoder i en grænseflade er abstrakte, så man behøver ikke at skrive abstract (men man kan). Programmering 1999 KVL Side 12-22 Eksempel: Grænseflader i aftalekalenderen Forskellen på Appointment og ToDo er at en aftale er begrænset af et tidsinterval. En aftale kan være begrænset enten ved hvornår den starter, hvornår den slutter eller begge dele. Der er dog noget fælles ved alle begrænsninger: Man kan se om aftalerne overlapper. Vi vil nu definere det ultimative aftalehierarki: Appointment2000. Programmering 1999 KVL Side 12-23 : Appointment2000 Grænseflader interface Schedulable { public void postpone(int min); public boolean collides(schedulable s); interface StartBounded extends Schedulable { public Time getstart(); public void move(time newstart); interface EndBounded extends Schedulable { public Time getend(); interface Bounded extends StartBounded, EndBounded { public void prolong(int min); Programmering 1999 KVL Side 12-24
: Appointment2000 Ændringer i Appointment class Appointment extends CalendarItem implements Bounded {... public Time getstart() { return starttime; public Time getend() { return endtime; public boolean collides(schedulable s) { if (s instanceof StartBounded && endtime.before(((startbounded)s).getstart())) return false; if (s instanceof EndBounded && ((EndBounded)s).getEnd().before(starttime)) return false; return true; Programmering 1999 KVL Side 12-25 Hov! Hvad var nu det nu at instanceof og (klasse) betød? Man kan undersøge om et givent objekt er af en bestemt klasse eller implementerer en bestemt grænseflade. Det gøres ved udtrykket: objekt instanceof klasse-eller-grænseflade Man kan eksplicit typekonvertere (eller omtype, på engelsk: type cast) et udtryk med (typenavn)udtryk For klasser eller grænseflader går det kun godt hvis udtrykket rent faktisk giver et objekt der kunne tildeles til en variabel af typen. Programmering 1999 KVL Side 12-26 : Appointment2000 En aftale uden sluttidspunkt class StartAppointment extends CalendarItem implements StartBounded { private Time starttime; public StartAppointment(Time starttime, String text) { super(text); this.starttime = new Time(starttime); public String tostring() { return "from " + starttime + ": " + text; public Time getstart() { return starttime; public boolean collides(schedulable s) { if (s instanceof EndBounded && ((EndBounded)s).getEnd().before(starttime)) return false; return true; public void postpone(int min) { starttime.increment(min); public void move(time newstart) { postpone(starttime.to(newstart)); Programmering 1999 KVL Side 12-27 Appointment2000: Automatisk flytning af aftaler (Virker kun hvis aftaler er ordnet efter starttidspunkt.) static void reschedule(calendaritem [] calendar) { for (int i = 1; i < calendar.length; i++) if (calendar[i] instanceof StartBounded) for (int j = 0; j < i; j++) if (calendar[j] instanceof EndBounded && ((Schedulable)calendar[i]). collides((schedulable)calendar[j])) { // Move the later appointment ((StartBounded)calendar[i]). move(((endbounded)calendar[j]).getend()); Programmering 1999 KVL Side 12-28
Appointment2000: main i TestAppointment2000 public static void main(string[] args) { ToDo t1 = new ToDo("tag backup"); Appointment a1 = new Appointment(new Time(8), new Time(9, 20), "forelæsning"); Appointment a2 = new Appointment(new Time(13), "møde"); Appointment a3 = new Appointment(new Time(14), new Time(14, 15), "kaffe"); StartAppointment a4 = new StartAppointment(new Time(15), "biblioteket"); CalendarItem [] friday = {t1, a1, null, a2, a3, a4; print_calendar(friday); t1.done = true; friday[2] = new Appointment(new Time(13), new Time(13, 10), "ring til mor"); a2.prolong(60); reschedule(friday); print_calendar(friday); Programmering 1999 KVL Side 12-29 Sammenfatning Abstrakte klasser kan bruges til at modellere abstrakte begreber. Abstrakte metoder definerer minimumskrav for en abstrakt klasses subklasser. En klasse kan kun udvide en enkelt superklasse (enkelt nedarvning). En klasse kan implementere en eller flere grænseflader (multipel pseudo-nedarvning). En grænseflade består udelukkende af abstrakte metoder. Variable, parametre og tabeller kan erklæres med en abstrakt klasse eller grænseflade som type. Man kan bruge instanceof til at udersøge medlemsskab af en klasse eller grænseflade. Man kan eksplicit typekonvertere ( omtype ) et udtryk. Læs: L&L afsnit 9.1 9.2. Programmering 1999 KVL Side 12-30