You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 2 Next »

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

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?

 

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.

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!

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:

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?

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

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

og ikke slik

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

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

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.

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-”).

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?

 getPrefix kan være static siden den ikke bruker felt eller metoder som er ikke-static.
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?

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

 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:

 

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.

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

 

     

 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

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

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

 

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?

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?

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);}

  • No labels