Exercise index of this lecture   Alphabetic index   Course home   

Exercises and solutions
Nedarvning


7.1   Opgave om kvadrater i forhold til rektangler  

Denne 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.

Solution
Et kvadrat er en specialisering af Rectangle. Hvis vi bruger 'is a testen' kan vi spørge om et kvadrat er et rektangel? Det er det, naturligvis. Mængden af kvadrater er en delmængde af mængden af rektangler. Altså bør Square arve fra Rectangle.

Man kunne forsøge sig med et argument om at et rektangel udvider klassen kvadrat med en ny sidelængde, og derved at Rectangle skal arve fra Square. Jeg mener dog helt klart, at dette er et vildspor, som ikke bør forfølges nærmere. Hvis der er et godt argument for specialisering komme udvidelses og programgenbrugs ideer i anden række.

Her viser vi klassen Square:

public class Square extends Rectangle {
  
  // no new instance variables

  /** Construct a square from the upper left corner and a side length.*/
  public Square(Point upperLeft, int sideLength){
    super(upperLeft, sideLength, sideLength);
  }

  // All other methods are inherited

}

Bemærk at vi kun har behov for at lave en konstruktor i klassen, som udtrykker hvilken super konstruktor i Rectangle, som skal anvendes.

Vi afprøver programmet via SquareTest:

public class SquareTest{

  public static void main(String[] args){
 
    Square r1 = new Square(new Point(5,10), 4);
    Square r2 = new Square(new Point(6,10), 5);
    Square r3 = new Square(new Point(0,0), 0);

    System.out.println("r1: " + r1);
    System.out.println("r2: " + r2);
    System.out.println("r3: " + r3);
  
    System.out.println("Width of r1: " + r1.width() + "  Height of r1: " + r1.height());
    System.out.println("Width of r2: " + r2.width() + "  Height of r2: " + r2.height());
  
//  r2.moveRelative(3,0);

    System.out.println("r1: " + r1);
    System.out.println("r2: " + r2);

    System.out.println("Overlap: " + r1.overlappingRectangle(r2));

  }

}  

  


7.2   Dobbeltkædet cirkulær liste  

Denne 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.

Solution
Her følger en løsningsskitse til en dobbeltkædet, cirkulær liste:

// UKOMPLET SKITSE. AFHÆNGER AF CircularList mv.

class DoubleCircularList extends CircularList {

 class DoubleLinkable extends Linkable {

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

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


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

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

   Linkable prev(){
     return(prev);
   }

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

 } // end DoubleLinkable

 public DoubleCircularList(){
   super();
 }

 // private firstLinkable OK as inherited
 
 // simpler because of double linking:
 protected Linkable butLastLinkable (){
   if (length == 0)
     return(null);
   else return(((DoubleLinkable)last).prev());
 }

 // emptyMe OK as inherited
 
 // size OK as inherited

 // redefined from CircularList
 private DoubleLinkable makeLinkable(Object el, DoubleLinkable next){
  if (next == null)
      return (new DoubleLinkable(el));
  else return (new DoubleLinkable(el,next));
 }

 protected void makeMeSingular(Object el){
   super.makeMeSingular(el);
   ((DoubleLinkable)last).setPrev((DoubleLinkable)last);
 }

 /** Insert el as a new first element */
 public void insertFirst(Object el){
  super.insertFirst(el);
  DoubleLinkable inserted = (DoubleLinkable)last.next();
  inserted.setPrev((DoubleLinkable)last);
 }


 /** Insert el as a new last element */
  public void insertLast(Object el){
    // reimplemented from scratch:
    DoubleLinkable newLink;
    if (length == 0) {
      makeMeSingular(el);
    }
    else{
      DoubleLinkable rememberOldLast = (DoubleLinkable)last;
      newLink = makeLinkable(el,(DoubleLinkable)firstLinkable());
      last = newLink;
      rememberOldLast.setNext(last);
      ((DoubleLinkable)last).setPrev(rememberOldLast);
      length = length + 1;
    }
 }

 /** Delete my first element */
 public void deleteFirst(){
   super.deleteFirst();
   DoubleLinkable first = (DoubleLinkable)((DoubleLinkable)last).next();
   first.setPrev((DoubleLinkable)last);
 }

 /** Delete my last element */
 public void deleteLast(){
   super.deleteLast();
   DoubleLinkable first = (DoubleLinkable)((DoubleLinkable)last).next();
   first.setPrev((DoubleLinkable)last);
 }   

 // retrieveFirst inherited and OK

 // retrieveLast inherited and OK

 public String toString(){
     return("Double: " + super.toString());
 }

}

Her er endvidere et link til det rene Java program .


7.3   Opgave om covarians  

Forklar hvorfor der ikke opstår problemer i forbindelse med covarians i det Java program, som blev omtalt på den tilknyttede slide

Solution
Beskeden meth(sref) sendt til aref, som refererer til et B-objekt, afstedkommer et kald af meth i klassen A. Vi får altså slet ikke kaldt meth i klassen B, som vi måske ville have forventet. Årsagen er, at metoderne meth(S) i A og meth(T) kan skelnes fra hinanden via deres formelle parameter. Vi siger som bekendt, at meth er overloadet, jf noteside om procedurer og funktioner i Java fra en tidligere lektion.


Generated: Monday March 31, 2008, 12:08:47
on the system cs-unix