Chapter 7
Nedarvning

Kurt Nørmark ©
Department of Computer Science, Aalborg University, Denmark


September 2001


Abstract
Previous lecture Next lecture
Index References Contents
Nedarvning er en de definerende egenskaber af objekt-orienteret programmering. Vi tager i denne lektion udgangspunkt i hhv. specialisering og udvidelse, som vi bl.a. karakteriserer ved brug af ekstension og intension, som allerede blev introduceret i den første lektion på kurset. Dernæst indfører vi nedarvning som en programmeringssproglig mekanisme, der kan bruges til at implementere specialisering og udvidelse. Vi ser i slutningen af lektionen på polymorfi-begrebet i objekt-orienteret programmering, og herunder dynamisk binding der også kendt under betegnelsen 'virtuelle operationer'


Specialisering og udvidelse

Specialisering
Slide Note Contents Index
References Speak
Specialisering er en form for abstraktion hvormed vi danner et nyt begreb fra et eksisterende begreb. Vi vil nu gå over til også at tale om specialisering af klasser. Specialisering benyttes i en objekt-orienteret modelleringsproces

Specialisering er central i forbindelse med begrebsmodellering i den tidlige fase af en programudviklingsproces

The concept specialisering: En specialisering af en klasse A betegner en ny klasse B hvor
  • Instanserne af B er en delmængde af instanserne af A
  • B-instanserne har alle specielle egenskaber i forhold til andre A-instanser
  • Operationerne fra klassen A er også anvendelige på instanser af klassen B
  • Nogle af A operationerne er tilpassset de særlige egenskaber, som karakteriser klassen B
Når vi specialiserer en klasse A danner vi en ny klasse som vi her vil kalde B. Mængden af B-objekter er en delmængde af A-objekterne. Ligeledes gælder at de operationer, som virker på objekter af typen A, også virker på objekter af typen B. Klassen B kan indeholde nye redefinerede operationer af A operationerne. Endvidere er det muligt og typisk, at klassen B definerer helt nye operationer i forhold til klassen A. De nye operationer i B relaterer sig til de specielle egenskaber, som B delmængden af A besidder.

Reference

Eksempel på specialiseringer af Konto
Slide Note Contents Index
References Speak
Vi har i en tidligere lektion set på klassen Konto og en række mulige specialiserede kontoklasser. Vi vender her tilbage til eksemplet, specielt med henblik på at forstå is-a relationen

Figure. Klassehierarkiet med Konto som den mest generelle klasse. Vi ser tre specialiserede kontoklasser: CheckKonto, AktionærKonto og GevinstKonto

Figure. Mulige ekstensioner af Konto klasserne. Vi ser her at en AktionærKonto er en (is a) Konto, og tilsvarende at en CheckKonto og en GevinstKonto er en (is a) Konto.

En CheckKonto er en Konto, en AktionærKonto er en Konto og en GevinstKonto er en Konto

Reference

Specialisering og ekstension
Slide Note Contents Index
References Speak
Specialisering vil nu blive karakteriseret i forhold til begrebet 'ekstension'

Ekstensionen af en specialiseret klasse B er en delmængde af ekstensionen af den generelle klasse A

Figure. Ekstensionen af et begreb A indsnævres når klassen A specialiseres til klassen B

  • Is-a relationen

    • Et B objekt er et A objekt

    • Der er en is-a relation mellem A og B

Is-a relationen mellem klasser kan måske lyde lidt abstrakt når vi taler om A og B klasser. De tilknyttede eksempler vil imidlertid gøre det mere klart, hvad det er vi mener

Is-a relationen udgør en kontrast til has-a relationen

Has-a relationen er karakteristisk for aggregering

Reference

Eksempel på specialiseringer af Figur
Slide Note Contents Index
References Speak
På denne side ser vi på et noget større hierarki, nemlig et hierarki af geometriske figurer

Figure. Et specialiseringshierarki af geometriske figurer. Bemærk at en LigebenetRevinketTrekant både er en LigebenetTrekant og en RetvinkletTrekant. Dette lægger op til, at LigebenetRevinkletTrekant både arver fra LigebenetTrekant og RevinketTrekant

Der er is-a relationer mellem sub- og superklasserne i ovenstående klassehierarki

Reference

Udvidelse
Slide Note Contents Index
References Speak
Udvidelse af en klasse A med nye egenskaber er centralt fra et genbrugsperspektiv. Vi ønsker at holde klassen A intakt. I en ny klasse B ønsker vi at have alle egenskaber i A plus et antal nye egenskaber, som kun eksisterer i B.

Udvidelse tager udgangspunkt i en klasse opfattet som et modul

Et modul er en syntaktisk ramme omkring et antal variabel- og metodedefinitioner

Udvidelse er en nyttig genbrugsmekanisme , som gør det muligt at undgå håndtering og vedligeholdelse af flere kopier af det samme stykke program

The concept udvidelse: En udvidelse af en klasse A betegner en ny klasse B som
  • Tilføjer nye egenskaber (variable og metoder) i forhold til A.
  • Egenskaberne af A kan også betragtes som egenskaber i klassen B.
Når vi udvider en klasse A tilføjer vi nye instansvariable og metoder. Lad os kalde den nye udvidede klasse B. B har således alle A's egenskaber plus de nye 'udvidede egenskaber'.

Udvidelse og intension
Slide Note Contents Index
References Speak
Parallelt med at vi ovenfor studerende specialisering i forhold til ekstensionsbegrebet vil vi nu se på udvidelse i forhold til intensionsbegrebet

Intensionen af en udvidet klasse B er en supermængde af intensionen af den oprindelige klasse A

Figure. Intensionen af et begreb A bliver større når klassen udvides

Reference

Det er generelt ikke muligt at karakterisere ekstensionen af B i forhold til ekstensionen af A

Ofte vil ekstensionen af B ikke overlappe ekstensionen af A

Eksempel på udvidelse: Pair til Triple
Slide Note Contents Index
References Speak
Vi ser her på et konkret eksempel på to klasser, Pair og Triple, hvor Triple udvider Pair.

Figure. Til venstre viser vi to klasser kaldet Pair og Triple. Triple udvider Pair med en ny part: part3. Vi har kun antydet operationerne på klasserne. Til højre ser vi to instanser af Pair og to instanser af Triple. Læg mærke til, at Triple instanser er hele objekter, på trods af klassen Triple er fremkommet ved at udvide klassen Pair

Instanser af Triple er hele objekter uden nogen som helst form for 'Pair del'

Ekstensionen af Triple har intet overlap med ekstensionen af Pair


Nedarvning

Nedarvning
Slide Note Contents Index
References Speak
Vi går nu over til at tale om 'nedarvning'. Som vi ser herunder, opfatter vi nedarvning som en udtryksform i programmeringssprog

The concept nedarvning: Nedarvning er en programmeringssproglig udtryksform som afleder en ny klasse fra en eksisterende klasseEn klasse kan arve fra en anden klasse når vi programmerer i et objekt-orienteret programmeringssprog. Klassen som arver kan enten være en specialisering, en udvidelse, eller både en specialisering og en udvidelse af den oprindelige klasse

  • Nedarvning i forhold til specialisering og udvidelse

    • Nedarvning kan anvendes til at specialisere en bestående abstrakt datatype

    • Nedarvning kan anvendes til at udvide egenskaberne af en eksisterende klasse

    • Når en klasse B arver fra A er B ofte både en specialisering og en udvidelse af A

Alternativ til nedarvning: Kopiering
Slide Note Contents Index
References Speak
På denne side ser vi på forskellen mellem at arve fra en klasse A og at udvide (en kopi) af klassen A.

Figure. Til venstre illustrerer vi at klassen B er frembragt ved at kopiere A's egenskaber ind i en ny klasse B. Til højre har vi arrangeret os ved at lade klassen B arve fra A

Klassen B er konstrueret ved at kopiere alle A's egenskaber ind i B

Klassen B er konstrueret ved at arve fra A

A og B er separate klasser uden spor af det oprindelige slægtskab

B er defineret med udgangspunkt i A, og således vævet sammen med A

Vanskelig at vedligeholde pga. dublering af egenskaber

Lettere at vedligeholde da egenskaber kun er defineret i én klasse

Alternativ til nedarvning: Delegering
Slide Note Contents Index
References Speak

The concept delegering: Ved delegering vil vi forstå at ét objekt videresender et givet arbejde til et andet objektEt objekt, som modtager en besked med et arbejde, det ikke selv kan/vil udføre, og som derfor videresender denne besked til et andet objekt, siges at delegere arbejdet.

I stedet for at lade en klasse B arve fra A, kan vi konsekvent delegere 'A relevante beskeder' fra B til et separat A objekt, som udfører arbejdet på B-objektet's vegne

Figure. Et B-objekt, aB delegerer til et A-objekt, anA.

Ved delegering fra B til A er der involveret to eller flere objekter

Når B arver fra A er der kun involveret et objekt, nemlig B objektet

Delegering giver således køb på modtagerobjektets identitet

Reference

Nedarvning i Java
Slide Note Contents Index
References Speak
Angivelse af en klasses superklasse er lige til i Java. Vi viser her syntaksen

Syntax: Syntaksen for definition af en klasse, som arver fra en anden klasse. Nøgleordet extends anvendes til at angive superklassen til en klasse i Java


class subKlasse extends superKlasse {    
  subklasseEgenskaber
}

Figure. Den grafiske notation som modsvarer ovenstående skabelon for definition af en subklasse. Læg mærke til at pilen går fra subklassen til superklassen

Eksempel på nedarvning: CheckKonto
Slide Note Contents Index
References Speak
Vi har i en tidligere lektion set på klassen Konto. Her vil vi studere en specialiseret konto, som vi kalder CheckKonto

Program: Java klassen Konto. Dnnne version er forberedt på at indgå i et klassehierarki idet de tre instansvariable navn og saldo er erklæret som protected. Med andre ord kan renteSats, navn og saldo ses i evt. subklasser af Konto. Vi vender tilbage til protectede egenskaber længere fremme i denne lektion.
/user/normark/courses/prog1/prog1-01/sources/java/noteEksempler/Bank2.java

Program: Java klassen CheckKonto som arver fra Konto. Vi vender senere tilbage til detaljerne i konstruktoren samt anvendelsen af super i de forskellige metoder i klassen - altså alle de blå aspekter i ovenstående.
class CheckKonto extends Konto {

   protected double ågerRenteSats;
   protected int checkNummer; 

   public CheckKonto(String nytNavn) {
     super(nytNavn);
     ågerRenteSats = 0.18;
     checkNummer = 0;
   }

   public void hævCheck(double beløb){
     this.hæv(beløb);
     checkNummer = checkNummer + 1;
   }
               
   public void tilskrivRente() {
     if (saldo > 0)  
        super.tilskrivRente();
     else
        saldo = saldo + saldo * ågerRenteSats;
   }

   public String toString() {
     return super.toString() +
            "Udskrevene checks: " + checkNummer + "\n";
   }

} // End Checkkonto

References

Checkkonto er både en udvidelse og en specialisering af Konto
Slide Note Contents Index
References Speak
Vi understreger her at nogle klasser både kan opfattes som en udvidelse og en specialisering af den oprindelige superklasse

Figure. Vi gentager her nedarvningshierarkiet for klassen Konto

Udvidelse

Specialisering

CheckKonto er en udvidelse af klassen Konto

CheckKonto er en specialisering af klassen Konto

Intensionen af CheckKonto vokser med variablen checkNummer og metoden hævCheck

Ekstensionen af CheckKonto skrumper i forhold til ekstensionen af Konto, idet nogle Konto objekter ikke er checkkonti

Exercise 7.1. Opgave om kvadrater i forhold til rektanglerDenne opgave skal ses i forlængelse af opgaven om rektangler fra en tidligere lektion.

Lav en klasse Square (dansk: Kvadrat). Det er åbentlyst for alle, at der er et nært forhold mellem kvadrater og rektangler, men hvilket? Bør Rectangle arve fra Square, eller Square fra Rectangle? Overvej også om Square kan opfattes som en specialisering af Rectangle (eller omvendt). Eller vil det være mere givende at opfatte Rectangle som en udvidelse af Kvadrat (eller omvendt)?

Implementer ud fra resultatet af disse overvejelser klassen Square. (Jeg linker her til min udgave af Rectangle incl. hjælpeklasser hvis I ønsker at tage udgangspunkt i denne). Klassen skal have 'passende konstruktorer' ala konstruktorerne fra Rectangle. Konstruktorerne i Square og Rectangle bør anvende hinanden via aktivering af super(....).

Bortset fra konstruktorerne skal klassen Rectangle og Square ligne hinanden så meget som muligt.

Eksempel på nedarvning: Triple
Slide Note Contents Index
References Speak
Tidligere i denne lektion så vi Triple som en udvidelse af Pair. Vi studerer nu disse to klasser i en Java implementation, som udnytter nedarvning

Program: Java klassen Pair. En instans af klassen Pair er en aggregering af to dele, der hver især kan være generelle objekter. På mange måde ligner klassen Pair klassen Linkable, som vi studerede i en tidligere lektion.
/user/normark/courses/prog1/prog1-01/sources/java/noteEksempler/Pair.java

Program: Java klassen Triple. Som vi har set tidligere udvider Tripple klassen Pair. Vi ser her hvordan Triple arver fra Pair. Vi vender tilbage til nogle af detaljerne senere i lektionen.
class Triple extends Pair {

  protected Object part3;

  public Triple(Object part1, Object part2, Object part3){
    super(part1,part2);
    this.part3 = part3;
  }

  public Object part3(){
    return(part3);
  }

  public void setPart3(Object newPart){
   part3 = newPart;
  }

  public String toString(){
    return("Triple: " + part1.toString() + 
           ", " + part2.toString() +
           ", " + part3.toString());
  }

}


Reference

Eksempel på nedarvning: DoubleLinkable
Slide Note Contents Index
References Speak
Linkable er klassen som implementerer de skjulte linkobjekter i kædede lister. Her ser vi på en udvidelse af Linkable, som også har en hægte til det foregående element. Vi ser altså på grundlaget for at kunne lave dobbeltkædede lister

Program: Java klassen Linkable. Instanser af klassen Linkable repræsenterer kædeobjekterne i en sammenkædet liste.
/user/normark/courses/prog1/prog1-01/sources/java/lister/CircularList1.java

Program: Java klassen DoubleLinkable som arver fra Linkable. Vi har udvidet klassen Linkable med en instansvariabel prev, som repræsenterer en hægte til det foregående element i en dobbeltkædet liste
class DoubleLinkable extends Linkable {

   private DoubleLinkable prev;
   
   public DoubleLinkable(){
     super();
     prev = null;
   }

   public DoubleLinkable(Object data){
     super(data);
     this.prev = null;
   }


   public DoubleLinkable(Object data, Linkable next){
     super(data,next);
     this.prev = null;
   }

   public DoubleLinkable(Object data, Linkable next, DoubleLinkable prev){
     super(data,next);
     this.prev = prev;
   }

   public Linkable prev(){
     return(prev);
   }

   public void setPrev(DoubleLinkable prev){
     this.prev = prev;
   }

 } // end DoubleLinkable

Exercise 7.2. Dobbeltkædet cirkulær listeDenne opgave skal ses i forlængelse af opgaven om cirkulære lister fra en tidligere lektion .

I kan her vælge at fortsætte med løsning af opgaven om enkeltkædede cirkulære lister. Alternativt kan I læse videre og gennemføre denne opgave, som langt hen ad vejen kan opfattes som en designøvelse.

I denne opgave bedes du designe en udvidelse af klassen CircularList, som var resultatet af opgaven fra lektion 5. (Jeg linker her til min udgave af CircularList for de af jer, som ønsker at tage udgangspunkt i denne). Udvidelsen består i at de cirkulære lister nu skal være dobbeltkædede. Lav en klasse DoubleCircularList som arver fra CircularList. Antag at klassen Linkable (hvis instanser er kædeobjekter) er en indre klasse til CircularList. DoubleCircularList bør tilsvarende indeholde en indre klasse, DoubleLinkable, som skal arve fra Linkable.

Start med at lave den indre klasse DoubleLinkable i en tom klasse DoubleCircularList, og lav dernæst så meget af DoubleCircularList som tiden tillader. Læg vægt på følgende aspekter:

  • Konstruktoren i DoubleCircularList i forhold til konstruktoren i superklassen.
  • Protectede egenskaber i CircularList, som anvendes i DoubleCircularList.
  • Muligheden for at kalde operationerne i CircularList under implementationen af de tilsvarende operationer i DoubleCircularList: super.circularListOperation(...).
Der er ikke tid til at lave en færdig implementation af DoubleCircularList i denne øvelsesgang. Det væsentlige i denne opgave er at komme igennem design overvejelserne, idet dette fører jer igennem langt de fleste aspekter, som er berørt i lektionen om nedarvning.

Jeg erindrer om, at vi faktisk smugkiggede på DoubleLinkable i forelæsningen . I kan naturligvis vælge at tage udgangspunkt i denne version af DoubleLinkable, og dermed gå mere direkte igang med programmering af DoubleCircularList.

Reference

Instantiering og initialisering
Slide Note Contents Index
References Speak
På denne side studerer vi den generelle instantierings og initialiseringsproblematik når vi lader klasser arve fra hinanden. På næste side ser vi løsningen, som er valgt i Java

Hvordan instantieres og initialiseres en subklasse?

En klasse D, der arver fra C som arver fra A, instantieres som ét objekt, der har alle egenskaber fra A, C og D.

Figure. Et billede af et klassehierarki (til venstre) og en instans af klassen D til højre. Man skal bemærke at instansen af D er ét samlet objekt med A egenskaber, C egenskaber og D egenskaber. Tilsvarende er en instans af B ét samlet objekt med A egenskaber og B egenskaber.

Hvordan kombineres initialiseringsoperationerne (konstruktorerne) i subklasse og superklasser?

Nedarvning og konstruktorer i Java
Slide Note Contents Index
References Speak
I direkte forlængelse problematikken identificeret herover ser vi nu på Java's løsning af initialiseringsproblemet. Vi ser også på en række Java eksempler på konstruktorer i de klasser vi introducerede ovenfor.

Konstruktorer nedarves ikke i Java

En Java subklasse's konstruktor er ansvarlig for initialisering af både 'direkte' og nedarvede instansvariable

I forbindelse med initialisering af en subklasse påkalder man sig enten eksplicit eller implicit en superklassekonstruktor

En superklasse konstruktor er ikke direkte meningsfuld for subklassen. Derfor arves den ikke. Men en konstruktor i superklassen løser dog typisk en del af problemet med at initialisere en instans af en subklasse. Derfor vil det altid være tilfældet, at der aktiveres en superklasse konstruktor i forbindelse med initialisering af en subklasse instans. Som det ses nedenfor har vi via super(...) mulighed for at angive hvilken af superklassens konstruktorer vi ønsker anvendt.

Reglen om, at konstruktorer ikke nedarves er typisk for objekt-orienterede sprog. Der er altså ikke blot tale om en 'særhed' i Java

Program: En skitse der viser hvordan en Java konstruktor i B kalder en konstruktor i superklassen A
Class B extends A {
  
  instansvariable i B

  public B (parameterListe1){
    super(parameterListe2);
    initialisering af B instansvariable
  }

  ...

}

Program: Konstruktor i Java klassen CheckKonto som arver fra Konto.
/user/normark/courses/prog1/prog1-01/sources/java/noteEksempler/Bank2.java

Program: Konstruktor i Java klassen Triple.
/user/normark/courses/prog1/prog1-01/sources/java/noteEksempler/Triple.java

Program: Konststruktorer i Java klassen DoubleLinkable som arver fra Linkable.
/user/normark/courses/prog1/prog1-01/sources/java/lister/DoubleCircularList.java

Grænseflader til klienter og subklasser i forbindelse med nedarvning
Slide Note Contents Index
References Speak
På denne side studerer vi den generelle grænsefladeproblematik ved nedarvning. Vi har ligesom i den simple situation (uden nedarvning) at gøre med grænsefladen til klienter. Men med introduktion af nedarvning kan vi nu også tale om grænsefladen mellem en klasse og dens subklasser

Figure. De forskellige grænseflader for klasserne A og B:
  1. De egenskaber i A, som klienter af A kan anvende.
  2. De egenskaber i A, som klassen B kan anvende
  3. De egenskaber i B, som klienter af B kan anvende.
  4. De egenskaber i B, som subklasser af B kan anvende

Nedarvning og synlighed i Java
Slide Note Contents Index
References Speak
Som allerede nævnt i tidligere lektioner har java en 'visibility modifier' som hedder protected. Det er denne modifier som er central i forbindelse med nedarvning

Der er behov for at offentliggøre egenskaber fra superklasse til subklasse i Java

Det er for snævert hvis disse er 'private' i superklassen

Det er for bredt hvis disse gøres 'public' i superklassen

The concept protected egenskab: En protected variabel eller metode i en klasse A kan ses i subklasser af A

Protectede egenskaber er også synlige fra klasser i samme pakke som A.

Som en mellemting mellem 'private' og 'public' indfører vi nu protectede egenskaber af en Java klasse. Grundtanken er, at protectede egenskaber i en klasse kan ses direkte subklasser af klassen. I Java gælder endvidere, at protectede egenskaber også kan ses direkte fra alle andre klasser i samme pakke. Protectede egenskaber har altså også pakke synlighed

Uden for A's pakke kan protectede egenskaber ses i objekter, der statisk er kvalificerede med mindst subklassen af A. Denne regel er omtalt nærmere i Java Tutorial samt i appendix F af Lewis and Loftus

  • Ændringer af synlighed under nedarvning:

    • Java tillader at gøre egenskaber 'mere offentlig' under nedarvning

    • Java forbyder at gøre egenskaber 'mere privat' under nedarvning

    • Superklassens erklæringer om synlighed kan ikke strammes i en subklasse

At Java tillader at gøre egenskaber mere offentlige betyder, at private egenskaber kan erklæres protected eller public i subklassen, og at protectede egenskaber kan erklæres public i subklassen

At Java forbyder at gøre egenskaber mere private betyder i praksis, at protectede egenskaber ikke må gøres private, og at offentlige egenskaber ikke må gøres protectede eller private i en subklasse

Program: Protectede instansvariable i Java klassen Konto.
/user/normark/courses/prog1/prog1-01/sources/java/noteEksempler/Bank2.java

Program: Protectede instansvariable i Java klassen Pair.
/user/normark/courses/prog1/prog1-01/sources/java/noteEksempler/Pair.java

Program: Java klassen Linkable uden protectede instansvariable. Instansvariablene tilgås i dette eksempel udelukkende via de direkte definerede metoder i Linkable. Bemærk, at disse metoder kan kaldes fra metoder i DoubleLinkable såfremt metoderne ikke er private.
/user/normark/courses/prog1/prog1-01/sources/java/lister/CircularList1.java

References

Forhindring af nedarvning i Java
Slide Note Contents Index
References Speak

Det kan undertiden være ønskværdigt at forhindre, at en klasse udvides eller specialiseres

Hvis en klasse angives som final kan vi forhindre at en anden klasse arver fra den

Det er også muligt at angive en metode som final, og derfor forhindre at en enkelt metode redefineres


Klassehierarkier

Nedarvning og dannelse af klassehierarkier
Slide Note Contents Index
References Speak

Ved at lade en række klasser arve fra hinanden kan der dannes et eller flere klassehierarkier

  • Muligheder og begrænsninger når en række klasser arver fra hinanden

    • Der må ikke være cykler i nedarvningsrelationen

    • Alle objekt-orienterede sprog tillader dannelse af nedarvningstræer

    • I nogle sprog er det muligt for en klasse at arve fra to eller flere klasser (multipel nedarvning)

    • I nogle sprog er det muligt for en klasse at arve to eller flere gange fra samme klasse (gentagen nedarvning)

Figure. Billeder svarende til de forskellige muligheder og begrænsninger mellem klasser, der arver fra hinanden. Situation (a) afspejler, at der er cykler, idet Z indirekte arver fra X, og X arver fra Z. Situation (b) er den normale og simple, hvor der dannes et træ. Situation (c) viser en acyklisk graf hvor V arver fra både Y og Z. I situation (d) arver Y to gange fra X.

Dannelse af klassehierarkier i Java
Slide Note Contents Index
References Speak
Vi ser nu på, hvordan et antal klassedefinitioner i Java kan definere et klassehierarki med klassen Object som rod

Klassen Object er rod i Java's klassehierarki

  • Implicit eller eksplict angivelse af superklasse

    • Hvis en klasse C ikke angiver en eksplicit superklasse arver C direkte fra klassen Object

    • Hvis en klasse C angiver en eksplicit superklasse D arver C direkte fra D, som direkte eller indirekte arver fra Object

Figure. En grov skitse af fire klasser i Java, der tilsammen danner et nedarvningstræ. Klassen A angiver ikke en superklasse, og den arver derfor implicit, men direkte fra Object. Klasserne B, C og D angiver alle en eksplicit, direkte superklasse (hhv. A, A, og C). Klasserne B, C og D arver således indirekte fra Object gennem A

Reference


Polymorfi og dynamisk binding

Statiske og dynamiske typer
Slide Note Contents Index
References Speak
Vi indfører her et par definitioner på forskellige former for typer af variable og parametre mv.

The concept statisk type: Den statiske type af en variabel eller parameter er den type, hvoraf variablen eller parameteren er erklæretDen statiske type af en variabel er den type vi finder i variabelerklæringen
The concept dynamisk type: Den dynamiske type af en variabel eller parameter er typen af det objekt, variablen eller parameteren refererer tilDen dynamiske type er typen af det objekt, som variablen eller parameteren peger på via en reference

Figure. To klasser A og B, hvor B arver fra A

Program: Erklæring af to variable x og y samt efterfølgende instantieringer og assignments
A x;          // x har statisk type A.
B y;          // y har statisk type B.

x = new A();  // x har dynamisk type A. 
y = new B();  // y har dynamisk type B.
 
x = y;        // x har dynamisk type B. Lovligt?
y = x;        // y har dynamisk type B. Lovligt?
              

Program: Et tilsvarende komplet Java program. Programmet illustrerer samme situation som skitseret generelt ovenfor
/user/normark/courses/prog1/prog1-01/sources/java/noteEksempler/StaticDynamicTypes.java

Skelnen mellem statisk og dynamisk type af en variabel eller parameter er vigtig for at kunne forstå det nært beslægtede emne: statiske kontra dynamisk binding og virtuelle operationer. Vi vender tilbage til dette herunder. Det er også muligt og nyttigt at tale om den statiske hhv. den dynamiske type af et udtryk. Via typeerklæringer af variable, parametre og funktioners resultat kan man udtale sig om et udtryks statiske type. På næste side vil vi definere under hvilke omstændigheder de to assignments herover er lovlige.

Polymorfi og typesammenlignelighed
Slide Note Contents Index
References Speak
Polymorfi er et nøglebegreb i mange programmeringssprog - objekt-orienterede såvel som sprog i andre paradigmer. Vi introducerer her polymorfibegrebet på det generelle plan.

The concept polymorfi: Polymorfi betyder mangeformethed.

Med polymorfi kan variable og parametre referere til objekter af mere end én type

Polymorfi hentyder til situationen hvor variable og parametre kan 'have eller antage forskellige former'. I programmeringssprog ser vi ofte, at polymorfe parametre afleder polymorfe procedurerer eller funktioner. Vi taler således om, at det er procedurerne eller funktionerne, der er polymorfe. Det simple polymorfibegreb på variable (og parametre) ses altså at kunne inducere et mere avanceret polymorfibegreb på procedurer og funktioner (og metoder).

Figure. En scene hvor klassen B som sædvanlig arver fra klassen A. Vi ser endvidere en variabel v erklæret af typen A og en metode (i en ikke angivet kontekst) med en parameter af typen A. Det centrale for vores interesse er imidlertid lovligheden af assignmentet v = e og kaldet m(e), specielt hvad angår typen af udtrykket e i forhold til klassen A.

  • Assignmentet v = e er lovligt i typemæssig forstand hvis den statiske type af e er A eller en subklasse af A

  • Kaldet m(e) er lovligt i typemæssig forstand hvis den statiske type af e er A eller en subklasse af A

Dette er de væsentlige regler for typesammenlignelighed i Java, og en del andre objekt-orienterede programmeringssprog.

De viste regler udmærker sig ved, at de kan checkes på det tidspunkt programmet oversættes. Årsagen til dette er, at der kun indgår statiske typer i reglerne. Dette er en stor fordel, idet man så undgår relativt kostbare checks på programmets udførelsestidspunkt.

Lad os vende tilbage til de to assignments x = y og y= x fra forrige slide.

Ifølge reglen indført på denne slide er x = y lovlig, idet den statiske type af y (nemlig B) er en subklasse af den statiske type af x (nemlig A).

Igen ifølge reglen er y = x ulovlig. Årsagen er, at x´s statiske type (A) ikke er en subklasse af y´s statiske type (B). Når de to assignments forekommer i den viste rækkefølge, kan det føles restriktivt ikke at tillade y = x. Forskellige sprog har forskellige konventioner på dette punkt. Nogle sprog tillader y = x, og disse sprog følger således ikke de regler, vi har formuleret på denne slides. Sådanne sprog må ty til typecheck på programmets udførelsestidspunkt.

Reglerne for typesammenlignelighed er lavet for kunne sikre statisk typecheck efter en konservativ model

Statisk typecheck er typecheck for programudførelsen starter; Det vil i praksis sige under compileringen. Når vi siger 'konservativ' mener vi her 'forsigtig' og 'garanterende'. Garantien skal sikre mod, at der ikke kan forekomme køretidsfejl som følge af typeproblemer. Når vi på køretidspunktet anvender en operation op på et objekt o gennem en variabel v (v.op(...)) ønsker vi at være sikre på, at op giver mening på det objekt (o), som v refererer. Statisk typecheck garanterer dette.

Statisk og dynamisk binding
Slide Note Contents Index
References Speak
Dynamisk binding er en attraktiv og meget nyttig egenskab ved en objekt-orienteret programudførelse. Vi lader her op til til sondringen mellem statisk og dynamisk binding ved at studere to klasser, samt referencer til instanser af disse

Polymorfi er en forudsætning for dynamisk binding

Figure. I denne scene, hvor B igen arver fra A, ser vi en operation op som er redefineret i B. Til venstre arrangerer vi os sådan, at variablen x med statisk type A får dynamisk type B. Spørgsmålet er om x.op(...) referer til op i A eller op i B

The concept dynamisk binding: Dynamisk binding: Den dynamiske type af x er bestemmende for bindingen af opNår vi anvender dynamisk binding er det objektets type der afgør hvilken metode vi aktiverer
The concept statisk binding: Statisk binding: Den statiske type af x er bestemmende for bindingen af opNår vi anvender statisk binding er det den statiske type den variabel, hvorigennem vi tilgår objektet der bestemmer metoden, som aktiveres

Program: Et tilsvarende Java program. Programmet illustrerer samme situation i Java, som skitseret generelt ovenfor
/user/normark/courses/prog1/prog1-01/sources/java/noteEksempler/StaticDynamicBinding.java

Hvis der anvendes statisk binding, kaldes op fra klassen A i X.op(...). Omvendt, hvis der anvendes dynamisk binding, kaldes op fra den specialiserede klasse B i X.op(...).

I sprog med statiske typer (i programteksten) er det vigtigt, at anvendelse af dynamisk binding ikke svækker typesikkerheden. Sproget må derfor gennem de sproglige regler sikre, at uanset den dynamiske type af X, findes der en operation op, der kan anvendes.

Statisk og dynamisk binding i Java
Slide Note Contents Index
References Speak
Betegnelsen 'virtuelle' operationer benyttes ofte i forbindelse med objekt-orienteret programmering. Vi sætter her denne betegnelse på plads i forhold til statisk og dynamisk binding

Java anvender konsekvent dynamisk binding af operationer

Variable bindes derimod statisk i Java

  • Virtuelle operationer i forhold til dynamisk binding

    • Dynamisk bundne operationer siges at være virtuelle

    • I Java er alle operationer således virtuelle

    • I nogle objekt-orienterede programmeringssprog kan programmøren bestemme, om de enkelte operationer skal bindes statisk eller dynamisk

Reference

Polymorfi og dynamisk binding i Banken
Slide Note Contents Index
References Speak
Vi viser her et eksempel på udnyttelse af klasserne Konto og CheckKonto, som illustrerer polymorfi

Program: Superklassen Konto.
/user/normark/courses/prog1/prog1-01/sources/java/noteEksempler/Bank3.java

Program: Subklassen CheckKonto.
/user/normark/courses/prog1/prog1-01/sources/java/noteEksempler/Bank3.java

Program: Et hovedprogram som illustrerer dynamisk binding. Vi laver 5 konti, som lagres i et array. Vi udnytter polymorfien derved at arrayet kan referere både instanser af Konto og instanser af CheckKonto. Læg særligt mærke til rentetilskrivningen, som foregår i en for-løkke. Carsten's checkkonto får tilskrevet ubehagelig ågerrente, idet der er negativ saldo på kontoen. Den dynamiske binding søger for, at den 'rigtige' rentetilskrivningsoperation bliver kaldt på de enkelte konti. Udskrivningen af kontiene via toString metoden illustrerer også den dynamiske binding. Kør selv programmet, og verificer at der er tale om dynamisk binding af rentetilskrivningsoperationen.
class Bank3 {
 
   public static void main( String[] args ) {

     Konto[] konti = new Konto[5];

     konti[0] = new Konto("Peter");
     konti[1] = new CheckKonto("Jens");
     konti[2] = new CheckKonto("Børge");
     konti[3] = new CheckKonto("Carsten");
     konti[4] = new Konto("Kristen");

     konti[0].hæv(50);                      // Peter:    -50 (Konto)
     konti[1].indsæt(100.0);                // Jens:     100 (CheckKonto)
     konti[2].indsæt(200.0);                // Børge:    200 (CheckKonto)
     konti[3].indsæt(300.0);                // Carsten
     ((CheckKonto)konti[3]).hævCheck(500);  // Carsten: -300 (CheckKonto)
     konti[4].indsæt(400.0);                // Kristen:  400 (Konto)

     for(int i = 0; i < 5; i = i + 1){
       konti[i].tilskrivRente();
     }

     for(int i = 0; i < 5; i = i + 1){
        System.out.println(konti[i]);
     }

   }
} // end Bank3

Eksemplet uden dynamisk binding
Slide Note Contents Index
References Speak
Her viser vi eksemplet fra forrige side uden anvendelse af dynamisk binding

Alternativet til at anvende dynamisk binding er eksplicit diskrimination af objekter baseret på deres type

Det er generelt et sygdomstegn hvis man anvender instanceof operatoren i Java (for meget)

Diskrimination af objekter betyder at gøre forskel på objekter. Forskellen består i i hvilken operation der bliver kaldt når der sendes en bestemt besked.

Program: Bank programmet uden anvendelse af fordelene ved dynamisk binding.
class Bank4 {
 
   public static void main( String[] args ) {

     Konto[] konti = new Konto[5];

     konti[0] = new Konto("Peter");
     konti[1] = new CheckKonto("Jens");
     konti[2] = new CheckKonto("Børge");
     konti[3] = new CheckKonto("Carsten");
     konti[4] = new Konto("Kristen");

     konti[0].hæv(50);                      // Peter
     konti[1].indsæt(100.0);                // Jens
     konti[2].indsæt(200.0);                // Børge
     konti[3].indsæt(300.0);                // Carsten
     ((CheckKonto)konti[3]).hævCheck(500);  // Carsten
     konti[4].indsæt(400.0);                // Kristen

     for(int i = 0; i < 5; i = i + 1){
       if (konti[i] instanceof CheckKonto)
          ((CheckKonto)konti[i]).tilskrivRenteCheckKonto();
       else if (konti[i] instanceof Konto)
          konti[i].tilskrivRenteKonto();
     }

     for(int i = 0; i < 5; i = i + 1){
        System.out.println(konti[i]);
     }

   }
} // end Bank4

Program: Superklassen Konto med operationen tilskrivRenteKonto
/user/normark/courses/prog1/prog1-01/sources/java/noteEksempler/Bank4.java

Program: Subklassen CheckKonto med operationen tilskrivRenteCheckKonto
/user/normark/courses/prog1/prog1-01/sources/java/noteEksempler/Bank4.java

Fordele ved dynamisk binding
Slide Note Contents Index
References Speak
Vi ser her på fordelene ved at have dynamisk binding. Som en kontrast så vi allerede på forrige side et eksempel på, hvordan man begår sig uden dynamisk binding

Dynamisk binding i kombination med polymorfi muliggør at Java køretidssystemet kan diskriminere på et objekts type forud for bestemmelse af den metode, som skal anvendes når der sendes en besked til objektet

Efter at et objekt har modtaget en besked taler vi om metodeopslag i objektets klasse

Uden denne mekanisme skulle programmøren selv programmere diskriminationen ud fra objektets type


Metode redefinition og -kombination

Redefinition af metoder og variable
Slide Note Contents Index
References Speak
Når man laver en subklasse er der ofte behov for at redefinere egenskaber fra superklassen. I Java gøres dette simpelthen ved at definere egenskaber i subklassen, som har samme navne eller signaturer, som egenskaberne i superklassen

En subklasse har ofte behov for at definere en variant af en metode, som arves fra superklassen

The concept redefinition: En metode eller variabel i en klasse B siges at være redefineret hvis der findes en metode eller variabel af samme navn i B's superklasseI subklassen af en klasse kan vi overskrive superklassens egenskaber. På denne måde kan vi - i forbindelse med nedarvning og dannelsen af en subklasse - redefinere allerede eksiterende egenskaber

  • Redefinition i Java, hvor klassen B arver fra A:

    • En redefineret variabel i B er til stede i både en B version og en A version i instanser af klassen B

      • A versionen kan tilgås internt fra B objektet gennem brug af super

    • En redefineret metode i B er stadig til stede i klassen A

      • A versionen af metoden kan tilgås internt fra B gennem brug af super

Program: Et kunstigt program som illustrerer redefinition af variable og metoder, og dynamisk binding af metoder. Vi skaber et B objekt, aB, hvortil der sendes beskeden m(7). På grund af dynamisk binding kaldes B's m metode, som assigner v i B til 7. Metoden m i A kaldes dernæst med 7-3=4 som parameter. Som følge af dette bliver v i A assignet til 4. Objektet aB printes, hvilket forårsager at this.v (i B) og super.v (i A) udskrives. Programmet's output bliver:
  B: this.v = 7, super.v = 4

class A {
 protected int v = 5;

 protected void m(int p){
   v = p;
 }

}

class B extends A{

 protected int v = 6;

 protected void m(int p){
   v = p;
   super.m(p-3);
 }

 public String toString(){
   return("B: " + "this.v = " + this.v + ", super.v = " + super.v  );
 }

}


class RedefTry {

 public static void main (String[] args){

   A aB = new B();
   aB.m(7);
   System.out.println(aB);
 }

}

Program: En let ændring af ovenstående som illustrerer at redefinerede variable bindes statisk. Variablen v findes i både A og B klassen. Vi aflæser v gennem aB1 og aB2 variablene. Dette kan vi gøre da v variablene i A og B er protectede, og dermed også klasse- synlige. Den første er erklæret af typen A, den anden af typen B. Udskriften er 'Variablen v gennem A kvalificeret variabel: 5. Variablen v gennem B kvalificeret variabel: 6'. Vi ser altså at der ikke udnyttes dynamisk binding ved tilgang af variable, men derimod statisk binding.
class A {
 protected int v = 5;

 protected void m(int p){
   v = p;
 }

}

class B extends A{

 protected int v = 6;

 protected void m(int p){
   v = p;
   super.m(p-3);
 }

 public String toString(){
   return("B: " + "this.v = " + this.v + ", super.v = " + super.v  );
 }

}


class RedefTry2 {

 public static void main (String[] args){

   A aB1 = new B();
   B aB2 = new B();
   System.out.println("Variablen v gennem A kvalificeret variabel: " + aB1.v);
   System.out.println("Variablen v gennem B kvalificeret variabel: " + aB2.v);

 }

}

Findes der regler for hvordan returværdi og metodeparametre må variere under redefinition?

Vi ser på denne generelle problemstilling på næste side

Forhinding af redefinition
Slide Note Contents Index
References Speak

Egenskaber som er final kan ikke redefineres i en subklasse

Det giver meget dårlig mening at redefinere private egenskaber i en subklasse

Vi har tidligere omtalt final metoder, og disse kan selvsagt ikke redefineres i subklasser. Private egenskaber kan ikke ses uden for klassen, og man kan derfor ikke med god mening tale om, de kan redefineres.

Program: Metoden m, som er privat i A, forsøges redefineret som en offentlig metode. Dette giver ikke god mening, idet m i A ikke kan ses fra B. Når vi således i B's m metode kalder super.m(...) får vi en fejl fra compileren.
class A {
 private int v = 5;

 private void m(int p){
   v = p;
 }

}

class B extends A{

 private int v = 6;

 public void m(int p){
   v = p;
   super.m(p-3);  // giver fejl
 }

}


class RedefTry4 {

 public static void main (String[] args){

   B aB = new B();
   aB.m(10);

 }

}

Varians af parametre og returværdi under redefinition
Slide Note Contents Index
References Speak

Hvordan kan vi tillade at parametre og returværdi af en metode varierer under redefinition?

Figure. En situation hvor klassen B arver A, og hvor T arver fra S. Typerne S og T anvendes som typer i parametre på metoder i hhv. A og B klasserne

Program: En praktisk anvendelse af klasserne A, B og S som typisk skaber problemer i objekt-orienteret programmering, men ikke i Java
   A aref;
   B bref = new B();
   S sref = new S();

   aref = bref;

   aref.meth(sref);

Det er problematisk at overføre et S-objekt som parameter til op i B

Vi kalder operationen op på et objekt af typen B. Dette objekt er refereret gennem en variabel, der er erklæret af (har statisk type A). Ud fra et statisk synspunkt er det derfor OK at overføre et S-objekt til operationskaldet. På grund af dynamisk binding vil vi her antage, at operationen op i klassen B kaldes. Dette er en tidsindstillet bombe, idet op fra B kan kalde en T-operation på sin parameter. Kald af en T-operation på et S-objekt er klart meningsløst. Hvad skal der til for at 'demontere' den tidsindstillede bombe? Et run-time check på, at (i vort tilfælde) x faktisk refererer til et T-objekt. For mange run-time checks gør programmer langsomme. Derfor strør compiler-skrivere ikke om sig med sådanne.

Covarians og contravarians
Slide Note Contents Index
References Speak
Her vil vi introducere to navne for de to grundliggende måder parametre kan variere i redefinerede metoder

The concept covarians: Covarians: Typer af operationsparametre varierer på samme måde, som de klasser, hvori operationerne befinder sig.Covarians betyder, at parametre af metoder varierer på samme måde (i samme retning) som de omkringliggende klasser
The concept contravarians: Contravarians: Typer af operationsparametre varierer på modsat måde, som de klasser, hvori operationerne befinder sig.Contravarians er det modsatte af covarians

Program: Et samlet program som illustrerer covarians problemstillingen i Java. Programmet viser klasserne A, B, S og T, hvor B arver fra A, og T arver fra S. T og S bruges som typer af parametre for metoder i A og B.
/user/normark/courses/prog1/prog1-01/sources/java/noteEksempler/RedefTry1.java

Exercise 7.3. Opgave om covariansForklar hvorfor der ikke opstår problemer i forbindelse med covarians i det Java program, som blev omtalt på den tilknyttede slide

Metodekombination
Slide Note Contents Index
References Speak
Når en redefineret metode samarbejder med den 'overskrevne' metoder kalder vi det undertiden for metodekombination. Vi vil her se på forskellige måder at styre 'kombinationen'

Figure. En klasse B arver fra klassen A. B redefinerer metoden 'op' fra A.

  • Mulige kombinationsteknikker mellem metoderne 'op' i A og B:

    • Der er programmatisk (imperativ) styring af metodekombinationen

      • Metoden op i A styrer den evt. aktivering af 'op' i B

      • Metoden op i B styrer den evt. aktivering af 'op' i A

      • Imperativ metodekombination

    • Der er en overordnet (deklarativ) styring af aktiveringen af 'op' i A og B

      • 'op' i A skal ikke kalde 'op' i B - eller omvendt

      • Deklarativ metodekombination

Java understøtter imperative metodekombination, hvor metoden i subklassen er styrende

Vi tænker her konkret på, at en metode i subklassen B kan aktivere metoden af samme navn i superklassen A ved super.op(...)


Terminologi

Nedarvningsterminologi
Slide Note Contents Index
References Speak
Vi runder af med en oversigt over den forskelligartede nedarvningsterminologi, der findes i udvalgte objekt-orienterede programmeringssprog

Der er meget forskelligartet terminologi omkring nedarvning i forskellige objekt-orienterede programmeringssprog

Figure. To klasser A og B, hvor B arver fra A

Table. Forskellig terminologi for nedarvning i forskellige objekt-orienterede programmeringssprog
SuperklasseSubklasseSprog
A er superklasse af BB er subklasse af ASimula, Smalltalk
A er basisklasse af BB er afledt (derived) klasse af AC++
A er forgænger (ancestor) af BB er efterkommer (descendent) af AEiffel
A er forældre (parent) til BB er arving (heir) af AEiffel
-B er en udvidelse af AJava
 


Collected references
Contents Index
Specialisering af begreber fra introduktions lektionen
The reference above goes to a lecture note pageThe reference above points to an earlier page in the lecture notes
Hierarkiet af bankkonto klasser fra introduktions lektionen
The reference above goes to a lecture note pageThe reference above points to an earlier page in the lecture notes
Ekstension af en klasse fra introduktions lektionen
The reference above goes to a lecture note pageThe reference above points to an earlier page in the lecture notes
Sub- og superklasser
The reference above goes to a lecture note pageThe reference above points to an succeding page in the lecture notes
Intensionen af en klasse fra introduktions lektionen
The reference above goes to a lecture note pageThe reference above points to an earlier page in the lecture notes
Mere om objekter i forhold til nedarvning
The reference above goes to a lecture note pageThe reference above points to an succeding page in the lecture notes
Protectede egenskaber
The reference above goes to a lecture note pageThe reference above points to an succeding page in the lecture notes
Vores første møde med Konto
The reference above goes to a lecture note pageThe reference above points to an earlier page in the lecture notes
Klassen Linkable
The reference above goes to a lecture note pageThe reference above points to an earlier page in the lecture notes
Om kæde-objekter fra lektionen om arrays og lister
The reference above goes to a lecture note pageThe reference above points to an earlier page in the lecture notes
Java modifiers: Appendix F i Lewis and Loftus (1ed og 2ed) giver en god oversigt over Java modifiers
The reference above goes to paper material
Controlling access to members
The reference above goes to a Java Tutorial pageThe references above goes to material on the Internet
Klassen Object i Java Core Library
The reference from above goes to Java API pageThe references above goes to material on the Internet
Illustration af statisk binding af variable
The reference above goes to a lecture note pageThe reference above points to an succeding page in the lecture notes

 

Chapter 7: Nedarvning
Course home     Author home     About producing this web     Previous lecture (top)     Next lecture (top)     Previous lecture (bund)     Next lecture (bund)     
Generated: March 31, 2008, 12:08:47