1
| Først laver vi klassen Forsendelse:
|
|
package Post;
public abstract class Forsendelse {
protected double vægt; // i gram
public Forsendelse( double v ) {
vægt = v;
}
public abstract double porto();
} |
|
| Vi vælger her at lade datakernen være en angivelse af vægten
i gram. Det er ikke nødvendigt at anvende en double,
en int ville have været
tilstrækkelig, men det er alligevel valgt, da en vægt traditionelt
angives med et komma-tal.
|
| Metoden porto, er den
metode der gør klassen abstrakt.
|
| Dernæst laver vi klassen Pakke:
|
|
package Post;
public class Pakke extends Forsendelse {
public Pakke( double v ) {
super( v );
}
public double porto() {
if ( vægt <= 1000 )
return 30.25;
else if ( vægt <= 5000 )
return 32.25;
else if ( vægt <= 10000 )
return 41.00;
else if ( vægt <= 15000 )
return 61.00;
else if ( vægt <= 20000 )
return 67.00;
else
return -1;
}
public String toString() {
return "[Pakke: " + vægt + " gram]";
}
} |
|
| Pakke er den første konkrete klasse, og vi implementerer derfor
porto. Dette gøres
vha. en række if-sætninger.
|
| Dernæst laver vi klassen Brev:
|
|
package Post;
public abstract class Brev extends Forsendelse {
public Brev( double v ) {
super( v );
}
} |
|
| Denne klasse er abstrakt idet vi ikke implmenterer porto.
Vi laver en set-konstruktor afht. de to subklasser.
|
| Dernæst laver vi klassen ABrev:
|
|
package Post;
public class ABrev extends Brev {
public ABrev( double v ) {
super( v );
}
public double porto() {
if ( vægt <= 20 )
return 4.00;
else if ( vægt <= 100 )
return 5.25;
else if ( vægt <= 250 )
return 9.25;
else if ( vægt <= 500 )
return 16.00;
else if ( vægt <= 1000 )
return 20.00;
else if ( vægt <= 2000 )
return 28.00;
else
return -1;
}
public String toString() {
return "[ABrev: " + vægt + " gram]";
}
} |
|
| Her implementerer vi porto
på samme måde som det er gjort i klassen Pakke.
|
| Dernæst laver vi klassen BBrev:
|
|
package Post;
public class BBrev extends Brev {
public BBrev( double v ) {
super( v );
}
public double porto() {
if ( vægt <= 20 )
return 3.75;
else if ( vægt <= 100 )
return 5.00;
else if ( vægt <= 250 )
return 8.75;
else if ( vægt <= 500 )
return 15.00;
else if ( vægt <= 1000 )
return 19.00;
else if ( vægt <= 2000 )
return 27.00;
else
return -1;
}
public String toString() {
return "[BBrev: " + vægt + " gram]";
}
} |
|
| porto implementeres
analogt til de andre implementationer af denne metode.
|
|
Endelig har vi en test-anvendelse.
|
|
import Post.*;
class TestPost {
public static void main( String[] argv ) {
ABrev a = new ABrev( 140 );
System.out.println( a + " porto=" + a.porto() );
BBrev b = new BBrev( 140 );
System.out.println( b + " porto=" + b.porto() );
Pakke p = new Pakke( 1400 );
System.out.println( p + " porto=" + p.porto() );
}
} |
[ABrev: 140.0 gram] porto=9.25
[BBrev: 140.0 gram] porto=8.75
[Pakke: 1400.0 gram] porto=32.25
|
|
|
Kildetekster:
Bemærk at de fem første filer skal placeres i et
subdirectory kaldet Post.
Forsendelse.java
Pakke.java
Brev.java
ABrev.java
BBrev.java
TestPost.java
|
2
| Først laver vi den abstrakte superklasse:
|
|
package Bolig;
public abstract class Bolig {
protected double m2;
public Bolig( double m2 ) {
this.m2 = m2;
}
public Bolig( Bolig bolig ) {
m2 = bolig.m2;
}
public abstract double årligHusleje();
public String toString() {
return "[Bolig: m2=" + m2 + "]";
}
} |
|
| Her har vi erklæret metoden årlighusleje
abstrakt, idet det er op til subklasserne at levere implementationen.
Metoden er erklæret i Bolig,
da vi ønsker kunne kalde den polymorft via en Bolig-reference.
|
| Vi har gjort klassen public,
da vi ellers ikke vil kunne anvende den uden for pakken. Filen med denne
klasse, og de to næste, skal placeret i et subdirectory, der bærer
samme navn som pakken: Bolig.
|
| Bemærk at datakernen er protected,
selv om det strengt taget ikke er nødvendigt, da den ikke i denne
løsning anvendes i nogen af subklasserne.
|
| Dernæst laver vi klassen Villa,
der nedarver fra Bolig:
|
|
package Bolig;
public class Villa extends Bolig {
private double pris, grund;
private boolean byZone;
public Villa( double m2, double p, double g, boolean bz ) {
super( m2 );
pris = p;
grund = g;
byZone = bz;
}
public Villa( Villa bolig ) {
super( bolig.m2 );
pris = bolig.pris;
grund = bolig.grund;
byZone = bolig.byZone;
}
public double årligHusleje() {
double normalLeje = pris * 0.10;
if ( byZone )
return 1.20 * normalLeje;
else
return normalLeje;
}
public String toString() {
return "[Villa: pris=" + pris +
", grund=" + grund +
", byZone=" + byZone +
" " + super.toString() + "]";
}
} |
|
| Bemærk at toString
anvender superklassens toString
og derved undgår at tilgå m2
direkte. Det samme gør sig gældende for konstruktorerne,
der bruger superklassens konstruktorer.
|
| Datakernen i Villa,
og Lejlighed, er naturligvis
private, da de ikke her
subklasser.
|
| Dernæst kommer den anden subklasse, Lejlighed,
som ikke adskiller sig fra Villa
i nævneværdig grad.
|
|
package Bolig;
public class Lejlighed extends Bolig {
private double månedligHusleje;
public Lejlighed( double m2, double mLeje ) {
super( m2 );
månedligHusleje = mLeje;
}
public Lejlighed( Lejlighed bolig ) {
super( bolig.m2 );
månedligHusleje = bolig.månedligHusleje;
}
public double årligHusleje() {
return 12.0 * månedligHusleje;
}
public String toString() {
return "[Lejlighed: månedligHusleje=" + månedligHusleje +
" " + super.toString() + "]";
}
} |
|
| Endelig har vi en testanvendelse, der illustrerer polymorfien ved at
bruge abstrakte referencer. Bemærk, at det derfor er nødvendigt
at caste referencerne i forbindelse med copy-konstruktorerne.
|
|
import Bolig.*;
class TestBolig {
public static void main( String[] argv ) {
Bolig b1 = new Lejlighed( 70, 3000 );
System.out.println( b1 + " årlig husleje: " + b1.årligHusleje() );
Bolig b2 = new Villa( 110, 650000, 800, false );
System.out.println( b2 + " årlig husleje: " + b2.årligHusleje() );
Bolig b3 = new Villa( 140, 850000, 400, true );
System.out.println( b3 + " årlig husleje: " + b3.årligHusleje() );
Bolig b4 = new Lejlighed( (Lejlighed) b1 );
System.out.println( b4 + " årlig husleje: " + b4.årligHusleje() );
Bolig b5 = new Villa( (Villa) b3 );
System.out.println( b5 + " årlig husleje: " + b5.årligHusleje() );
}
} |
[Lejlighed: månedligHusleje=3000.0 [Bolig: m2=70.0]] årlig husleje: 36000.0
[Villa: pris=650000.0, grund=800.0, byZone=false [Bolig: m2=110.0]] årlig husleje: 65000.0
[Villa: pris=850000.0, grund=400.0, byZone=true [Bolig: m2=140.0]] årlig husleje: 102000.0
[Lejlighed: månedligHusleje=3000.0 [Bolig: m2=70.0]] årlig husleje: 36000.0
[Villa: pris=850000.0, grund=400.0, byZone=true [Bolig: m2=140.0]] årlig husleje: 102000.0
|
|
|
Kildetekster:
Bemærk at de tre første filer skal placeres i et
subdirectory kaldet Bolig.
Bolig.java
Villa.java
Lejlighed.java
TestBolig.java
|
3
| Først laver vi den abstrakte klasse Bil:
|
|
package Bil;
public abstract class Bil {
private double vægt;
public Bil( double v ) {
vægt = v;
}
public Bil( Bil bil ) {
this( bil.vægt );
}
public abstract double afgift();
protected double vægtAfgift( double ører ) {
return vægt/100.0*ører;
}
public String toString() {
return "[Bil: vægt=" + vægt + "]";
}
} |
|
| Vi har her gjort metoden afgift
abstrakt. Selv om vægt
indgår i beregningen af afgift
for både LastBil
og PersonBil, sker
det på en måde, der ikke giver anledning til en fælles
implementation i afgift.
I stedet er det nyttigt med en service-metode til sub-klasserne, der beregner
den del af afgiften som beror på Bil'ens
vægt. Metoden
vægtAfgift
tager afgiften i ører
pr. kg. Den er protected
for at subklasserne kan anvende den, og samtidig giver den, den eneste
adgang til vægt
som subklasserne behøver - derved kan vi styrke indkapslingen ved
at gøre vægt
private.
|
|
|
| Lad os se den første af subklasserne LastBil:
|
|
package Bil;
public class LastBil extends Bil {
private double last;
public LastBil( double v, double lt ) {
super( v );
last = lt;
}
public LastBil( LastBil bil ) {
super( bil );
last = bil.last;
}
public double afgift() {
return vægtAfgift( 35.0 ) + last/100.0*20.0;
}
public String toString() {
return "[LastBil: last=" + last + " " + super.toString() + "]";
}
} |
|
| Bemærk, at samtlige metoder i denne klasse anvender metoder fra
super-klassen. Konstruktorerne anvender super-klassens konstruktorer til
at initialisere vægt,
og metoderne afgift
og toString anvender
de get-agtige metoder i superklassen, ligeledes vedrørende vægt.
|
| Det er også værd at bemærk, at copy-konstruktoren
kalder Bil's copy-konstruktor
med en LastBil som
aktuel parameter. Da den formelle parameter af typen Bil
også kan referere til en LastBil,
er dette ikke noget problem.
|
|
|
| Dernæst følger den anden subklasse PersonBil:
|
|
package Bil;
public class PersonBil extends Bil {
private int passagerer;
public PersonBil( double v, int p ) {
super( v );
passagerer = p;
}
public PersonBil( PersonBil bil ) {
super( bil );
passagerer = bil.passagerer;
}
public double afgift() {
return vægtAfgift( 50.0 ) + passagerer*800.0;
}
public String toString() {
return "[PersonBil: passagerer=" + passagerer + " " + super.toString() + "]";
}
} |
|
| Man bemærker at denne subklasser grundlæggende ikke adskiller
sig fra LastBil
- den er opbygget på samme måde.
|
|
|
| Endelig har vi en testanvendelse:
|
|
import Bil.*;
class TestBil {
public static void main( String[] argv ) {
Bil last1 = new LastBil( 2000, 400 );
System.out.println( last1 + " afgift: " + last1.afgift() );
Bil person1 = new PersonBil( 1100, 5 );
System.out.println( person1 + " afgift: " + person1.afgift() );
Bil last2 = new LastBil( (LastBil) last1 );
System.out.println( last2 + " afgift: " + last2.afgift() );
Bil person2 = new PersonBil( (PersonBil) person1 );
System.out.println( person2 + " afgift: " + person2.afgift() );
}
} |
[LastBil: last=400.0 [Bil: vægt=2000.0]] afgift: 780.0
[PersonBil: passagerer=5 [Bil: vægt=1100.0]] afgift: 4550.0
[LastBil: last=400.0 [Bil: vægt=2000.0]] afgift: 780.0
[PersonBil: passagerer=5 [Bil: vægt=1100.0]] afgift: 4550.0 |
|
|
Kildetekster:
Bemærk at de tre første filer skal placeres i et
subdirectory kaldet Bil.
Bil.java
LastBil.java
PersonBil.java
TestBil.java
|
4
| Først laver vi den abstrakte superklasse Medlem:
|
|
package Idraet;
public abstract class Medlem {
protected String navn;
protected int alder;
public Medlem( String n, int a ) {
navn = n;
alder = a;
}
public abstract int kontingent();
public String toString() {
return "[navn=" + navn + ", alder=" + alder + "]";
}
} |
|
| Bemærk at metoden kontingent
er erklæret abstrakt.
|
| Dernæst laver vi klassen Junior,
der implementerer kontingent:
|
|
package Idraet;
public class Junior extends Medlem {
private String parent;
public Junior( String n, int a, String p ) {
super( n, a );
parent = p;
}
public int kontingent() {
if ( alder < 15 )
return 150;
else
return 250;
}
public String toString() {
return "[Junior:" + super.toString() + ", forældre: " + parent + "]";
}
} |
|
| Bemærk at anvendelsen af super-kald i realiteten gør det
overflødigt at vi har en protected
datakerne i Medlem, den
kunne godt have været private.
|
| Dernæst følger klassen Senior:
|
|
package Idraet;
public class Senior extends Medlem {
public Senior( String n, int a ) {
super( n, a );
}
public int kontingent() {
if ( alder < 40 )
return 400;
else if ( alder < 60 )
return 350;
else
return 100;
}
public String toString() {
return "[Senior:" + super.toString() + "]";
}
} |
|
| Endelig har vi testanvendelsen, der illustrerer polymorfien, ved at
anvende abstrakte referencer til de tre instanser:
|
|
import Idraet.*;
class TestIdraet {
public static void main( String[] argv ) {
Medlem m1 = new Senior( "Erling", 75 );
System.out.println( m1 + ", kontingent: " + m1.kontingent() );
Medlem m2 = new Junior( "Hans", 9 , "Valdemar" );
System.out.println( m2 + ", kontingent: " + m2.kontingent() );
Medlem m3 = new Junior( "Peter", 16, "Vera" );
System.out.println( m3 + ", kontingent: " + m3.kontingent() );
}
} |
[Senior:[navn=Erling, alder=75]], kontingent: 100
[Junior:[navn=Hans, alder=9], forældre: Valdemar], kontingent: 150
[Junior:[navn=Peter, alder=16], forældre: Vera], kontingent: 250 |
|
|
Kildetekster:
Bemærk at de tre første filer skal placeres i et
subdirectory kaldet Idraet.
Medlem.java
Junior.java
Senior.java
TestIdraet.java
|
|
|
5
| Først laver vi den abstrakte superklasse Lokale:
|
|
package Lokaler;
public abstract class Lokale {
private double areal;
public Lokale( double areal ) {
this.areal = areal;
}
public Lokale( Lokale lok ) {
this( lok.areal );
}
public abstract int kapacitet();
protected int arealKapacitet( double prM2 ) {
return (int) (areal/prM2);
}
public String toString() {
return "[Lokale: areal=" + areal + "]";
}
} |
|
| Bemærk at metoden kapacitet
er erklæret abstrakt.
|
| Dernæst laver vi klassen KlasseLokale,
der implementerer kapacitet:
|
|
package Lokaler;
public class KlasseLokale extends Lokale {
public KlasseLokale( double areal ) {
super( areal );
}
public KlasseLokale( KlasseLokale lok ) {
super( lok );
}
public int kapacitet() {
return arealKapacitet( 1.5 );
}
public String toString() {
return "[KlasseLokale: " + super.toString() + "]";
}
} |
|
| Bemærk at anvendelsen af arealKapacitet
gør det muligt at bibeholde areal
som værende private
i super-klassen Lokale.
|
| Dernæst følger klassen EDB_Lokale:
|
|
package Lokaler;
public class EDB_Lokale extends Lokale {
private int strøm;
public EDB_Lokale( double areal, int strøm ) {
super( areal );
this.strøm = strøm;
}
public EDB_Lokale( EDB_Lokale lok ) {
super( lok );
this.strøm = lok.strøm;
}
public int kapacitet() {
int kap = arealKapacitet( 2.5 );
if ( kap > strøm/3 )
return strøm/3;
else
return kap;
}
public String toString() {
return "[EDB_Lokale: " + super.toString() + ", strøm=" + strøm + "]";
}
} |
|
| Endelig har vi testanvendelsen, der illustrerer polymorfien, ved at
anvende abstrakte referencer til de fem instanser. Bemærk at anvendelse
af et array gør det bekvemt at kalde de polymorfe metoder i en
løkke:
|
|
import Lokaler.*;
public class Test {
public static void main( String[] argv ) {
Lokale[] lokaler = new Lokale[5];
lokaler[0] = new KlasseLokale( 40 );
lokaler[1] = new KlasseLokale( 30 );
lokaler[2] = new KlasseLokale( (KlasseLokale) lokaler[0] );
lokaler[3] = new EDB_Lokale( 20, 25 );
lokaler[4] = new EDB_Lokale( 60, 70 );
for ( int i=0; i<lokaler.length; i++ )
System.out.println( lokaler[i] + ": " + lokaler[i].kapacitet() );
}
} |
[KlasseLokale: [Lokale: areal=40.0]]: 26
[KlasseLokale: [Lokale: areal=30.0]]: 20
[KlasseLokale: [Lokale: areal=40.0]]: 26
[EDB_Lokale: [Lokale: areal=20.0], strøm=25]: 8
[EDB_Lokale: [Lokale: areal=60.0], strøm=70]: 23 |
|
|
Kildetekster:
Bemærk at de tre første filer skal placeres i et
subdirectory kaldet Lokaler.
Lokale.java
KlasseLokale.java
EDB_Lokale.java
Test.java
|
|
|
6
| Først laver vi den abstrakte superklasse Tog:
|
|
package Tog;
public abstract class Tog {
private int standardPladser;
public Tog( int standardPladser ) {
this.standardPladser = standardPladser;
}
public Tog( Tog tog ) {
this( tog.standardPladser );
}
public abstract int billetIndtægt();
protected int standardIndtægt() {
return 20*standardPladser;
}
public String toString() {
return "[Tog: standardPladser=" + standardPladser + "]";
}
} |
|
| Bemærk at metoden billetIndtægt
er erklæret abstrakt.
|
| Dernæst laver vi klassen ReginalTog,
der implementerer billetIndtægt:
|
|
package Tog;
public class RegionalTog extends Tog {
public RegionalTog( int standardPladser ) {
super( standardPladser );
}
public RegionalTog( RegionalTog tog ) {
super( tog );
}
public int billetIndtægt() {
return standardIndtægt();
}
public String toString() {
return "[RegionalTog: " + super.toString() + "]";
}
} |
|
| Bemærk at anvendelsen af standardIndtægt
gør det muligt at bibeholde standardPladser
som værende private
i super-klassen Tog.
|
| Dernæst følger klassen LynTog:
|
|
package Tog;
public class LynTog extends Tog {
private int businessPladser;
public LynTog( int standardPladser, int businessPladser ) {
super( standardPladser );
this.businessPladser = businessPladser;
}
public LynTog( LynTog tog ) {
super( tog );
this.businessPladser = tog.businessPladser;
}
public int billetIndtægt() {
return standardIndtægt() + businessPladser * 50;
}
public String toString() {
return "[LynTog: " + super.toString() + ", businessPladser=" + businessPladser + "]";
}
}
|
|
| Endelig har vi testanvendelsen, der illustrerer polymorfien, ved at
anvende abstrakte referencer til de tre instanser. Bemærk at anvendelse
af et array gør det bekvemt at kalde de polymorfe metoder i en
løkke:
|
|
import Tog.*;
public class TestTog {
public static void main( String[] argv ) {
Tog[] tog = new Tog[3];
tog[0] = new RegionalTog( 120 );
tog[1] = new RegionalTog( (RegionalTog) tog[0] );
tog[2] = new LynTog( 80, 30 );
for ( int i=0; i<tog.length; i++ )
System.out.println( tog[i] + " indtægt: " + tog[i].billetIndtægt() );
}
} |
[RegionalTog: [Tog: standardPladser=120]] indtægt: 2400
[RegionalTog: [Tog: standardPladser=120]] indtægt: 2400
[LynTog: [Tog: standardPladser=80], businessPladser=30] indtægt: 3100 |
|
|
Kildetekster:
Bemærk at de tre første filer skal placeres i et
subdirectory kaldet Tog.
Tog.java
RegionalTog.java
LynTog.java
TestTog.java
|
|
|
| |