Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Migrated to Confluence 5.3
Expand
titleDel 1 - Innkapsling og Konstruktører (15%)
Oppgave a)

Hva er formålet med / begrunnelsen for å implementere en eller flere konstruktører for en klasse? 

Expand
titleLF

  En konstruktør har som formål å initialisere et objekt, slik at det fra starten av har en gyldig tilstand.

Oppgave b)

   I forhold til innkapsling, hva er hovedgrunnen til å la en konstruktør ta en eller flere parametre?

Expand
titleLF
 Parametrene til en konstruktør er informasjon som trengs for å gi objektet en gyldig starttilstand, som oftest verdien til felt som må være satt og det ikke finnes noen fornuftig default-verdi.
Oppgave c)

c) I hvilke situasjoner bør en konstruktør være henholdsvis public, protected og private og hvordan vil kallet til konstruktøren se ut?

Expand
titleLF

public brukes når andre klasser skal kunne instansiere klassen direkte med new <klasse>(…). protected-konstruktører brukes for å initialisere felt i en superklasse og kalles av subklassens konstruktør med super(…). private brukes på hjelpekonstruktører internt i en klasse og kalles med this(…).

Oppgave d)

Gitt følgende klasse:

 

Code Block
public class V {
      public int v0, v1 = v0++, v2;
      public V() {
            v2 = v0++;
      }
}

Hva vil verdien til feltene v0, v1 og v2 være etter at konstruktøren er utført?

Expand
titleLF
 v0 = 2, v1 = 0, v2 = 1. v0 blir til slutt 2 fordi den blir økt to ganger. v1 blir initialisert før konstruktøren blir utført og settes til v0 sin verdi før v0 økes (første gang). v2 blir initialisert etter v1 og settes til v0 sin verdi før v0 økes (for andre gang).
Oppgave e)

Java vil ved gitte forutsetninger automatisk opprette en konstruktør for en klasse, slik at instanser av klassen kan opprettes, selv om en ikke eksplisitt har definert en konstruktør for klassen. Hva er betingelsen(e) for at Java skal gjøre dette og hvordan ser denne konstruktøren ut?

Expand
titleLF
 Forutsetningen for å automatisk opprette en konstruktør er at den er instansierbare dvs. ikke abstrakt, og det ikke er definert noen andre konstruktører. En slik konstruktør vil være public og ha tom parameterliste.

Se også Innkapsling

Expand
titleDel 2 - Klasser (45%)

I denne oppgaven skal du implementere klasser og metoder for å håndtere en ordliste, f.eks. brukt til å gjøre tekstinput på en mobiltelefon mer effektivt. For alle oppgavene gjelder det at du kan bruke andre metoder deklarert i samme eller tidligere deloppgaver, selv om du ikke har implementert dem (riktig).

Oppgave a)

Du skal implementere en klasse WordList for å representere en ordliste. WordList skal inneholde metoder for å si om et ord finnes, finne alle ord som begynner med et bestemt prefiks og legge til og fjerne ord.

Implementer følgende metoder inkl. nødvendige deklarasjoner av felt og hjelpemetoder:

- boolean containsWord(String word): returnerer om word finnes i denne ordlista.

- Collection<String> getWordsStartingWith(String prefix): returnerer en samling av alle ordene i denne ordlista som begynner med bokstavene i prefix. Dersom prefix selv finnes i ordlista, så skal den også være med. Denne metoden er nyttig for å kunne foreslå ord som passer til det en har begynt å skrive. F.eks. kan den kalle metoden med ordet ”program” og få tilbake en liste som bl.a. inneholder ordene ”program”, ”programmer”, ”programmerer” og ”programmering” (forutsatt at disse faktisk er lagt inn i denne ordlista på forhånd).

- boolean addWord(String word): Legger word til denne ordlista, dersom word ikke finnes i ordlista fra før. Mellomrom foran og bak skal fjernes og tomme ord skal ikke legges inn. Returverdien skal angi om ordlista faktisk ble endret.

- boolean removeWord(String word): Fjerner word fra denne ordlista. Returverdien skal angi om ordlista faktisk ble endret.

- boolean removeWordsStartingWith(String prefix): Fjerner alle ord som begynner på prefix fra denne ordlista. Dersom den kalles med med ordet ”program” så skal bl.a. ordene ”program”, ”programmer”, ”programmerer” og ”programmering” fjernes fra denne ordlista. Som for removeWord skal returverdien angi om noen ord i ordlista faktisk ble fjernet.

Expand
titleLF

Vi velger å bruke en Collection til å holde ordlista. add- og remove-metodene kan brukes direkte, siden de returnerer om lista ble endret. Skal en gjøre det selv, må en initialisere en lokal variabel til et uttrykk/løkke med med contains og returnere denne etter add/remove/removeAll-kallet.

Code Block
private Collection<String> wordList = new ArrayList<String>();

public boolean containsWord(String s) {
      return wordList.contains(s);
}
public Collection<String> getWordsStartingWith(String s) {
      Collection<String> matchingWords = new ArrayList<String>();
      for (String word : wordList) {
            if (word.startsWith(s)) {
                  matchingWords.add(word);           }
     }
     return matchingWords;
}

public boolean addWord(String s) {
      s = s.trim();
      return (s.length() > 0 && (! wordList.contains(s)) ? wordList.add(s) : false);
}

public boolean removeWord(String s) {
      return wordList.remove(s);
}

public boolean removeWordStartingWith(String s) {
      return wordList.removeAll(getWordsStartingWith(s));
}

Se også Collection-rammeverket

Oppgave b)

 

Det er noen ganger nyttig å kunne finne alle ord som har et gitt sett med endelser. Implementer følgende metoder, som er til hjelp for dette.

- String getPrefix(String word, String suffix): Dersom word ender på suffix så skal prefikset frem til suffix-endelsen returneres. Ellers skal null returneres. F.eks. skal getPrefix(”java-program”, ”program”) returnere ”java-”, mens getPrefix(”java-program”, ”programmering”) skal returnere null.

- boolean hasSuffixes(String prefix, List<String> suffixes): Returnerer true dersom prefix finnes i denne ordlista med alle endelsene i suffixes-lista. Dersom du legger ”tjue-en” og ”tjue-to” inn i ordlista og kaller metoden med argumentene ”tjue-” og en liste med ordene ”en” og ”to”, så skal metoden altså returnere true.

- List<String> findPrefixes(List<String> suffixes): Returnerer lista av alle prefiks som forekommer (i denne ordlista) med alle endelsene i suffixes. Merk at prefikset selv trenger ikke å være et ord i ordlista. Dersom du legger ”tjue-en”, ”tjue-to”, ”tretti-en”, ”tretti-to” og ”førti-en” (og ingen andre ord) inn i ordlista og kaller metoden med en liste med ordene ”en” og ”to” som argument, så skal det returneres en liste med ordene ”tjue-” og ”tretti-” (men ikke med ”førti-”).

Expand
titleLF
Code Block
public String getPrefix(String word, String suffix) {
            if (word.endsWith(suffix)) {
                  return word.substring(0, word.length() - suffix.length());
            }
            return null;
      }

      public boolean hasSuffixes(String prefix, List<String> suffixes) {
            for (String suffix : suffixes) {
                  if (! containsWord(prefix + suffix)) {
                        return false;
                  }
            }
            return true;
      }

      public List<String> findPrefixes(List<String> suffixes) {
            List<String> prefixes = new ArrayList<String>();
            for (String word : wordList) {
                  String prefix = getPrefix(word, suffixes.get(0));
                  if (prefix != null && hasSuffixes(prefix, suffixes)) {
                        prefixes.add(prefix);
                  }
            }
            return prefixes;
      }

 

 
Oppgave c)

 Hvilke(n) av de tre metodene i deloppgave b) kunne vært deklarert som static og hvorfor?

Expand
titleLF
 getPrefix kan være static siden den ikke bruker felt eller metoder som er ikke-static.
Expand
titleDel 3 - Grensesnitt og delegering (20%)
Oppgave a)

Det innføres et interface Words, som deklarerer de fire første metodene spesifisert i 2 a), altså containsWord, getWordsStartingWith, addWord og removeWord. WordList endres til å deklarere at dette grensesnittet implementeres, altså class WordList implements Words { … }.

Anta så at en deklarerer to variabler som følger:
WordList wordList1 = new WordList();
Words wordList2 = new WordList();
wordList1.??? <= hva kan stå her? …

wordList2.??? <= hva kan stå her? …

 Hvordan bestemmer deklarasjonene hvordan wordList1 og wordList2 kan brukes lenger ned i koden?

Expand
titleLF

Den deklarerte typen bestemmer hvilke metoder en kan kalle, uavhengig av om objektet som variabelen refererer til har andre/flere metoder. Altså vil en for wordList1 kunne kalle alle metodene i WordList, mens en for wordList2 kun kan kalle metodene i Words.

Oppgave b)

Hva blir verdiene av de fire uttrykkene wordList1 instanceof Word, wordList1 instanceof WordList , wordList2 instanceof Word og wordList2 instanceof WordList

Expand
titleLF
 Alle gir true, fordi instanceof tar utgangspunkt i den faktiske (dynamiske) typen til objektet det refereres til.
Oppgave c)

  Du skal lage en ny Words-implementasjon kalt DelegatingWordList, som kombinerer to andre (interne) ordlister med delegeringsteknikken. DelegatingWordList skal altså oppføre seg om den inneholder alle ordene i de to interne ordlistene. Ta utgangspunkt i følgende felt-deklarasjoner og konstruktør:

 

Code Block
public class DelegatingWordList implements Words {
      private Words words1, words2;
      public DelegatingWordList(Words words1, Words words2) {
            this.words1 = words1;
            this.words2 = words2;
      }
      ... implementer Words-metodene her ...}

 

Forklar med tekst og (pseudo)kode hvordan du vil implementere Words-metodene med logikken som angitt i deloppgave 2 a) og basert på de interne ordlistene. Prøv å unngå å endre de interne ordlistene mer enn nødvendig.

Expand
titleLF

Poenget er å forstå hva som er riktig logikk og hvordan det må delegeres til hver av de to listene.

 

     

Code Block
 public boolean containsWord(String s) {
            return words1.containsWord(s) || words2.containsWord(s);
      }
      public Collection<String> getWordsStartingWith(String s) {
            List<String> matchingWords = new ArrayList<String>();
            matchingWords.addAll(words1.getWordsStartingWith(s));
            matchingWords.addAll(words2.getWordsStartingWith(s));
            return matchingWords;
      }
      public boolean addWord(String s) {
            if (! containsWord(s)) {
                  words1.addWord(s);
                  return true;
            }
            return false;
      }

      public boolean removeWord(String s) {
            return words1.removeWord(s) | words2.removeWord(s);
      }

Se også Delegeringsteknikken

20%)

Gitt følgende klasse, som representerer et tidspunkt på dagen: 

Code Block
public class DayTime {

      public final int hours, minutes;

      public DayTime(int hours, int minutes) {
            this.hours = hours;
            this.minutes = minutes;
      }

      public String toString() {
            return hours + ":" + minutes;
      }
}

Oppgave a)

Hva betyr final-nøkkelordet slik det er brukt her? På hva slags måte(r) ivaretar denne klassen formålet med innkapsling og på hva slags måte(r) ikke?

 

Expand
titleLF

final betyr her at feltet ikke kan endres etter at det er satt i konstruktøren. Selv om feltene er public så sikres innkapsling ved at felt-verdiene forblir korrekte, siden kode utenfor klassen ikke kan sette feltene til ugyldige verdier Imidlertid er det ikke i tråd med innkapsling at kode gjøres avhengig av at data er lagret i spesifikke felt. Ved bruk av get-metoder får implementasjonsklassen større frihet til å endre interne detaljer, uten at annen kode blir påvirket.

Oppgave b)

Beskriv den generelle teknikken og navnekonvensjonen(e) for å representere og kapsle inn en enkel verdi, f.eks. tall eller objektreferanse, som skal kunne endres etter at objektet er opprettet.

Expand
titleLF

Vi ber om både representasjon (felt) og innkapsling (metoder). Gitt verdi med (logisk) navn ”value” og type X, så vil en ha felt, get- og set-metoder som følger:

       private X value;

       public X getValue() { … }

(Dersom X er boolean/Boolean, så brukes gjerne ”is” som prefiks istedenfor ”get”).

       public void setValue(X value) { … }

 

Oppgave c)

I hva slags metode(r) brukes unntak ifm. innkapsling, og hvordan? Vis gjerne med et eksempel!

Expand
titleLF

I metoder som endrer (en verdi i) et objekt, så bør argumenter valideres. Dette må skje før selve endringen og i tilfelle ugyldig(e) verdier så kastes et unntak av typen IllegalArgumentException(…). Eksempel:

Code Block
public void setValue(int value) {
    if (value < 0) {
       throw new IllegalArgumentException("Value shouldn't be negative, but was " + value);
   }
  this.value = value;
 }
Oppgave d)

Mange klasser inneholder et List<X>-felt og definerer addX, removeX og andre metoder som tilsvarer og bruker metodene i List-grensesnittet. En alternativ løsning kunne vært å arve fra en List<X>-implementasjon, f.eks. ArrayList<X>. Hvorfor brukes aldri denne løsningen i praksis?

Expand
titleLF

Det handler ikke om bruk av List<X> vs. ArrayList<X>, men om hvorfor vi f.eks. gjør slik

Code Block
public class Person {
    List<Person> children = ... // contains list of children
    public void addChild(Person p) { ... }
   public void removeChild(Person p) { ... }
 }

og ikke slik

Code Block
public class Person extends ArrayList<Person> {
    // trenger ikke add- og remove-metoder, fordi vi arver dem fra ArrayList
 }
  • Når en arver så kan en ikke kun arve de metodene en ønsker, men får alle med på kjøpet. Da vil en være nødt til å redefinere alle en ikke trenger/ønsker at skal være tilgjengelig.
  • Det vil ikke alltid være logisk riktig at klassen skal være instanceof List<X>.
  • (Teknikken kan bare brukes for én slik liste, siden en bare kan arve fra én implementasjonsklasse).
Se også Innkapsling
Expand
titleDel 2 - Klasser (45%)

I denne oppgaven skal du implementere klasser og metoder for å håndtere en dagsplan med tidsrom, f.eks. avtaler. Klassen oppgitt i oppgave 1 a) kan brukes som en del av implementasjonen din. Husk at alle metoder kan brukes i implementasjonen av andre.

Oppgave a)

Du skal implementere en klasse TimeSlot for å representere et tidsrom innenfor en dag, f.eks. en avtale. Et TimeSlot-objekt har en beskrivelse (tekst), et start- og sluttidspunkt (time og minutt) og en varighet (minutter). Ingen av disse dataene skal kunne endres etter at TimeSlot-objektet er opprettet. Du velger selv hvordan disse dataene skal representeres og hvilke hjelpemetoder som evt. trengs. Merk at varighet kan beregnes fra starttidspunkt og sluttidspunkt, evt. at sluttidspunkt kan beregnes fra starttidspunkt og varighet.

Implementer følgende konstruktører og metoder:

  • TimeSlot(String description, int hours, int minutes, int duration): initialiserer med oppgitt beskrivelse og starttidspunkt (timer og minutter) og varighet (minutter). F.eks. vil new TimeSlot(”TDT4100-forelesning”, 10, 15, 105) representere en TDT4100-forelesning i tidsrommet 10:15-12:00.
  • String toString(): returnerer en String på formen beskrivelse@start-slutt hvor start og slutt er på formen tt:mm (altså to siffer pr. tall). Dersom toString()-metoden kalles på TimeSlot-objektet fra forrige punkt skal det gi ”TDT4100-forelesning@10:15-12:00”.
  • DayTime getStartTime(): returnerer starttidspunktet som et DayTime-objekt (se oppgave 1).
  • DayTime getEndTime(): returnerer sluttidspunktet som et DayTime-objekt (se oppgave 1).
  • int getDuration(): returnerer varighet i minutter
Expand
titleLF

Det er ikke viktig å ta hensyn til overgangen mellom dager, dvs. interval som går over midnatt.

Vi velger å representere start- og sluttidspunktene som antall minutter siden midnatt. Dette gjør mange av beregningene siden lettere. Et alternativ er å ha to DayTime-felt i stedet eller int-felt for start/slutt-time og –minutter.

Code Block
private static int asHours  (int minutes) { return minutes / 60;}
private static int asMinutes(int minutes) { return minutes % 60;}

private String description;
private int startTime, endTime;

public TimeSlot(String description, int hours, int minutes, int duration) {
   this.description = description;
   this.startTime = hours * 60 + minutes;
   this.endTime = this.startTime + duration;
}

private String twoDigits(int i) {
   return (i < 10 ? "0" : "") + i;
}

public String toString() {
   return description + "@" + twoDigits(asHours(startTime)) + ":" + twoDigits(asMinutes(startTime)) + "-" + twoDigits(asHours(endTime)) + ":" + twoDigits(asMinutes(endTime));
}

public DayTime getStartTime() {
   return new DayTime(asHours(startTime), asMinutes(startTime));
}


public DayTime getEndTime() {
   return new DayTime(asHours(endTime), asMinutes(endTime));
}


public int getDuration() {
   return endTime - startTime;
}


public String getDescription() {
   return description;
}
Oppgave b)

Implementer følgende metoder:

  • boolean contains(int hours, int minutes): returnerer om dette TimeSlot-objektet inneholder tidspunktet angitt med hours og minutes. Merk at sluttidspunktet regnes ikke som å være inneholdt i tidsrommet. Dette betyr at new TimeSlot(”…”, 8, 0, 30).contains(8, 0) skal gi true, mens new TimeSlot(”…”, 8, 0, 30).contains(8, 30) skal gi false.
  • boolean overlaps(TimeSlot timeSlot): returnerer om dette TimeSlot-objektet overlapper med det angitte TimeSlot-objektet, dvs. om det finnes et tidspunkt som begge inneholder.
Expand
titleLF
Code Block
public boolean contains(int hours, int minutes) {
   minutes = hours * 60 + minutes;
   return startTime <= minutes && endTime > minutes;
}

public boolean overlaps(TimeSlot other) {
   return startTime < other.endTime && endTime > other.startTime;
}

En alternativ strategi er å sjekke om start- eller sluttidspunktet ligger inni den andre eller omvendt. 

Oppgave c)

  TimeSlot-klassen skal støtte sortering. TimeSlot-objektet med tidligst starttidspunkt sorteres først, og dersom starttidspunktene er like, så skal det med tidligst sluttidspunkt sorteres først. Forklar og implementer nødvendig kode.

Expand
titleLF

Dersom klassen implementerer Comparable spesialisert til samme klasse, så kan instansene sorteres vha. Java sin innebygde Collections.sort-metode.

Code Block
public class TimeSlot implements Comparable<TimeSlot> {
 
       ...
 
       public int compareTo(TimeSlot other) {
               // negativ diff betyr mindre enn
              int diff = startTime - other.startTime;
              if (diff == 0) {
                      diff = endTime - other.endTime;
              }
              return diff;
       }
}
Oppgave d)

Du skal implementere en klasse DayPlan, for å holde oversikt over alle avtalene (altså TimeSlot-objekter) for en dag, bl.a. gi muligheten til å legge til og fjerne TimeSlot-objekter. Velg selv hvilke felt og evt. hjelpemetoder som trengs.

Implementer følgende metoder:

  • void addTimeSlot(TimeSlot timeSlot): legger det angitte tidsrommet til denne dagsplanen
  • void removeTimeSlot(TimeSlot timeSlot): fjerner det angitte tidsrommet fra denne dagsplanen
  • TimeSlot getTimeSlotAt(int hours, int minutes): returnerer det tidligste tidsrommet som inneholder tidspunktet angitt med hours og minutes, ellers null.
Expand
titleLF
Code Block
private List<TimeSlot> timeSlots = new ArrayList<TimeSlot>();

public void addTimeSlot(TimeSlot timeSlot) {
   timeSlots.add(timeSlot);
   Collections.sort(timeSlots);
}

public void removeTimeSlot(TimeSlot timeSlot) {
   timeSlots.remove(timeSlot);
}


public TimeSlot getTimeSlotAt(int hours, int minutes) {
   for (TimeSlot timeSlot : timeSlots) {
      if (timeSlot.contains(hours, minutes)) {
         return timeSlot;
      }
   }
   return null;
}
Oppgave e)

Implementer følgende to metoder for tidsplanlegging:

  • boolean containsOverlapping(): returnerer om det finnes overlappende tidsrom i denne dagsplanen.
  • Collection<TimeSlot> getFreeTime(): returnerer en samling TimeSlot-objekter som representerer fritiden en har i løpet av en dag, dvs. tidsrommene som denne dagsplanen ikke dekker.

For begge disse metodene kan det være lurt å definere hjelpemetoder for å gjøre løsningen ryddigere.

Expand
titleLF

Disse to metodene er litt fiklete, så vi er ikke så nøye på detaljene. Vi kan utnytte at lista er sortert. Dersom duplikater ikke er lov, så trenger en strengt tatt kun en enkel løkke som sjekker for overlapp med nabo-elementet. Med duplikater må en sjekke mot første element etter/før som ikke er samme element:

Code Block
public boolean containsOverlapping() {
   for (int i = 0; i < timeSlots.size(); i++) {
      TimeSlot timeSlot = timeSlots.get(i);
      for (int j = i + 1; j < timeSlots.size(); j++) {
         TimeSlot otherTimeSlot = timeSlots.get(j);
         if (timeSlot != otherTimeSlot) {
            return timeSlot.overlaps(otherTimeSlot);
         }
         // alternativt, dersom lista ikke er sortert
         if (timeSlot != otherTimeSlot && timeSlot.overlaps(otherTimeSlot)) {
            return true;
         }
      }
   }
   return false;
}

private static void addTimeSlotIfNonEmpty(Collection<TimeSlot> timeSlots, DayTime startTime, DayTime endTime) {
   int duration = (endTime.hours * 60 + endTime.minutes) - (startTime.hours * 60 + startTime.minutes);
   if (duration > 0) {
      timeSlots.add(new TimeSlot(null, startTime.hours, startTime.minutes, duration));
   }
}


public Collection<TimeSlot> getFreeTime() {
   Collection<TimeSlot> freeTime = new ArrayList<TimeSlot>();
   TimeSlot previous = new TimeSlot(null, 0, 0, 0);
   for (TimeSlot timeSlot : getAllTimeSlots()) {
      DayTime startTime = timeSlot.getStartTime();
      addTimeSlotIfNonEmpty(freeTime, previous.getEndTime(), startTime);
      DayTime endTime = timeSlot.getEndTime();
      if (! previous.contains(endTime.hours, endTime.minutes)) {
         previous = timeSlot;
      }
   }
   addTimeSlotIfNonEmpty(freeTime, previous.getEndTime(), new DayTime(24, 0));
   return freeTime;
}

Et alternative er å gå gjennom alle tidspunkt i døgnet og “samle” minuttene som ikke er inneholdt i noe tidsintervall i nye tidsintervall som legges til resultatlista.

 

Expand
titleDel 3 - Arv og delegering (20%)
Oppgave a)

Du skal implementere støtte for TDT4100-forelesninger, som et spesielt tidsrom som alltid er fra 10:15 til 12:00. Vis hvordan en klasse TDT4100Lecture kan implementere dette vha. arv fra TimeSlot.

Expand
titleLF

Velger å bruke konstruktøren for å sette riktige verdier. Alternativt kan get-metoden redefineres til å returnere riktige (konstant)verdier.

Code Block
public class TDT4100Lecture extends TimeSlot {
   public TDT4100Lecture() {
      super("TDT4100lecture", 10, 15, 105);
   }
}
Oppgave b)

Du skal implementere støtte for en dagsplan som alltid inneholder et TDT4100Lecture-objekt, dvs. en TDT4100-forelesning fra 10:15-12:00. Det skal ikke være mulig å fjerne TDT4100-forelesningen eller legge inn andre tidsrom som overlapper med den. Følgende kode illustrerer hvordan det skal virke:

Code Block
DayPlan tuesday = new TDT4100DayPlan();
System.out.println(tuesday.getTimeSlotAt(10, 30)); 
// prints "TDT4100 lecture@10:15-12:00"
tuesday.addTimeSlot(new TimeSlot("Coffee break", 11, 30, 60));
// throws appropriate exception, since it overlaps with the TDT4100 lecture

Vis hvordan dette kan implementeres i en TDT4100DayPlan-klasse vha. arv fra DayPlan.

Expand
titleLF
Code Block
public class TDT4100DayPlan extends DayPlan {


   private TDT4100Lecture tdt4100Lecture;


   public TDT4100DayPlan() {
      super();
      tdt4100Lecture = new TDT4100Lecture();
      super.addTimeSlot(tdt4100Lecture);
   }

   @Override
   public void addTimeSlot(TimeSlot timeSlot) {
      if (timeSlot.overlaps(tdt4100Lecture)) {
         throw new IllegalArgumentException("Cannot overlap TDT4100 lecture!");
      }
      super.addTimeSlot(timeSlot);
   }

   @Override
   public void removeTimeSlot(TimeSlot timeSlot) {
      if (timeSlot == tdt4100Lecture) {
         throw new IllegalArgumentException("Cannot remove TDT4100 lecture!");
      }
      super.removeTimeSlot(timeSlot);
   }
}
Oppgave c)

Det er ofte nyttig å kunne la én dagsplan bygge på eller inkludere en eller flere andre, f.eks. la dagsplanen for en bestemt tirsdag inkludere tirsdagsplanen som gjelder for hele semesteret (hvor bl.a. TDT4100-forelesningen ligger). Følgende kode illustrerer hvordan det skal virke:

Code Block
DayPlan repeatingTuesday = new TDT4100DayPlan();
DelegatingDayPlan tuesday = new DelegatingDayPlan(repeatingTuesday);
System.out.println(tuesday.getTimeSlotAt(10, 30)); 
// prints "TDT4100 lecture@10:15-12:00" since tuesday logically includes the TDT4100Lecture in repeatingTuesday
tuesday.addTimeSlot(new TimeSlot("Coffee break", 11, 30, 60));
System.out.println(tuesday.containsOverlapping());
// prints "true" since timeSlot in tuesday overlaps with TDT4100Lecture in repeatingTuesday

Skisser med tekst og kode hvordan delegeringsteknikken og arv fra DayPlan kan brukes for å implementere denne oppførselen, inkludert hvordan du evt. vil modifisere DayPlan for å gjøre løsningen ryddigere.

Expand
titleLF

Delegering er en teknikk hvor et objekt, videreformidler kall til en ”delegat” når det er behov for delegatens ferdigheter. I dette tilfellet er det viktig at TimeSlot-objektene som ligger i delegaten regnes med i logikken. F.eks. må getTimeSlotAt(…)-metoden sjekke egne TimeSlot-objekter og delegatens og returnere det tidligste av de to.

I løsningen innføres en getAllTimeSlots()-metode i DayPlan, som brukes istedenfor this.timeSlots  i metodene containsOverlapping() og getFreeTime() i DayPlan som trenger å behandle alle TimeSlot-objektene på en gang. Dermed trenger vi bare å redefinere getAllTimeSlots().     

Code Block
public class DelegatingDayPlan extends DayPlan {
   private DayPlan delegate;

   public DelegatingDayPlan(DayPlan delegate) {
      super();
      this.delegate = delegate;
   }

   @Override
   public TimeSlot getTimeSlotAt(int hours, int minutes) {
      TimeSlot timeSlot1 = super.getTimeSlotAt(hours, minutes);
      TimeSlot timeSlot2 = delegate.getTimeSlotAt(hours, minutes);
      if (timeSlot1 != null && timeSlot2 != null) {
         return timeSlot1.compareTo(timeSlot2) < 0 ? timeSlot1 : timeSlot2;
      } else if (timeSlot1 != null) {
         return timeSlot1;
      } else {
         return timeSlot2;
      }
   }

   @Override
   protected Collection<TimeSlot> getAllTimeSlots() {
      Collection<TimeSlot> timeSlots = super.getAllTimeSlots();
      if (delegate != null) {
         List<TimeSlot> timeSlotList = new ArrayList<TimeSlot>(timeSlots);
         timeSlotList.addAll(delegate.getAllTimeSlots());
         Collections.sort(timeSlotList);
         timeSlots = timeSlotList;
      }
      return timeSlots;
   }
}

Se også Delegeringsteknikken

Expand
titleDel 4 - Input/output (IO) (10%)
Oppgave a)

Hva er den grunnleggende forskjellen på input/output-klassene InputStream/OutputStream og deres subklasser ift. Reader/Writer og deres subklasser?

Expand
titleLF

InputStream-/OutputStream-klassene håndterer byte-verdier, mens Reader-/Writer-klassene håndterer char-verdier (dvs. implementerer koding av tegn til/fra bytes iht. Unicode-regler).

 

Oppgave b)

Ifm. input/output brukes en egen type unntak, hvilken? På hva slags måte påvirker det kode som driver med input/output?

Expand
titleLF

Input/output-metoder kaster ofte IOException, som er en såkalt “checked exception”. Slike brukes gjerne for feil som er utenfor vår kontroll. Disse krever at kode må fange dem opp med try/catch eller deklarere med throws at de kastes videre.

Oppgave c)

Hvorfor må vi lukke input- og output-“strømmer” med close()-metoden når vi er ferdige med dem? Hvordan sikrer man at det skjer også i tilfelle unntak?

Expand
titleLF

Strømmer bruker gjerne ressurser utenfor Java og close()-metoden sikrer at Java samhandler riktig med disse, f.eks. frigjør dem. For å sikre at dette alltid skjer, er det vanlig å ha close()-kallet i en try/finally-blokk.

Expand
titleDel 4 - Input/output og Unntak (20%)
Oppgave a)

Lag en metode void read(InputStream input) i WordList som fyller ordlista med ord lest fra den angitte input-strømmen. Du kan anta at input-strømmen er fra en tekstfil eller tekstlig nettressurs. Hver tekstlinje består enten av et enkeltord eller et prefiks etterfulgt av bindestrek (‘-‘) og så en liste med endelser med komma (‘,’) mellom. Merk at ekstra mellomrom rundt skilletegnene ‘-‘ og ‘,’ må utelates fra prefiks og endelser. Du trenger ikke sjekke om ordene inneholder rare tegn. I tillegg kan en linje inneholde en ‘#’, som betyr at alt fra og med ‘#’-tegnet regnes som en kommentar som skal ignoreres. Alle unntak skal overlates til kalleren av metoden. 

Eksempler:

java # enkeltordformat: legger “java” inn i lista

# kommentarlinje, ingen ord

2-1,2,3 # prefiks og liste med endelser, legger “21”, “22” og “23” inn i lista

tretti- # prefiks med tom liste av endelser: legger “tretti” inn i lista

Expand
titleLF

Unntaket må deklareres med throws. Input må omsluttes med en Reader for å håndtere tekstkoding riktig. De ulike formatene må håndteres (kommentar som hele eller deler av linja, enkeltord, prefiks med endelse).

 

Code Block
public void read(InputStream input) throws IOException {
      BufferedReader reader = new BufferedReader(new InputStreamReader(input));
      String line = null;
      while ((line = reader.readLine()) != null) {
            int pos = line.indexOf('#');
            if (pos >= 0) {
                  line = line.substring(0, pos);
            }
            pos = line.indexOf('-');
      if (pos < 0) {
                 addWord(line);
            } else {
                  String prefix = line.substring(0, pos).trim();
                  String[] suffixes = line.substring(pos + 1).split(",");
                  for (int i = 0; i < suffixes.length; i++) {
                        addWord(prefix + suffixes[i].trim());
                  }
            }
      }
}
Oppgave b)

Hva er en checked exception? Anta at metoden m2 bruker metoden m1 og m1 (muligens) utløser en checked exception. Da er det to måter å kode m2 på som gjør at den kompilerer, hvilke?

Expand
titleLF

En Exception som ikke er en RuntimeException er en checked exception. En slik unntakstype krever enten try/catch eller en throws-deklarasjon for å unngå kompileringsfeil.

Oppgave c)

  Anta at metoden m2 bruker metoden m1 og m1 (muligens) utløser en checked exception. Hvordan kan en kode m2 slik at den utløser en unchecked exception når m1 utløser en checked exception?

Expand
titleLF

En må fange unntaket med en try/catch og så utløse en ny unchecked exception, altså RuntimeException eller en egnet subklasse:

try { ... kode som kaster checked exception ...}

catch (Exception e) { throw new RuntimeException(e);}