Göm menyn

Labb 2: Intro till objektorientering i Java

Förberedelser

Labben baseras på FÖ 3 (objektorienteringens grunder). Om du börjar innan FÖ 3, var beredd på att uppgifterna kan baseras på sådant vi ännu inte har diskuterat.

Syfte och krav

I denna labb ser vi hur man skapar och använder objekt och klasser. Vi återkopplar också till labbar från Python-kursen för att kontrastera mot andra sätt att programmera.

Vi kommer att hjälpa till med flera handgrepp i programmeringen, men det är som tidigare viktigt att ni inte genomför dem "mekaniskt" utan reflekterar över varför ni gör som ni gör.

Labben examineras via demonstration utan kodinlämning senast vid deadline.

Övning 2.1: Ett första objekt + operatorn new

Syfte: Komma igång med objekt!

Nu ska ni snabbast möjligt komma igång och skapa egna objekt med "new". Vi skapar därför objekt av en redan existerande klass från Javas klassbibliotek.

Bakgrund 2.1.1: Skapa paket och klass

Vi fortsätter i samma IDEA-projekt, men skapar ett nytt paket för labb 2 för att hålla isär de olika övningarna bättre. Vi skapar oftast en ny klass för varje övning.

Uppgift 2.1.1: Skapa paket och klass

  1. Tidigare klasser låg i paketet se.liu.ida.dinadress.tddd78.lab1. Högerklicka src, välj New | Package och skapa se.liu.ida.dinadress.tddd78.lab2. Detta ska hamna på samma nivå som lab1 i klassträdet.

  2. Skapa klassen Slump i paketet lab2.

Bakgrund 2.1.2: Slumptal

Vi ska nu skapa slumptal med en (pseudo-)slumptalsgenerator.

En slumptalsgenerator initialiseras normalt med ett "frö" och ger sedan en sekvens av slumptal. Samma frö ger alltid samma sekvens av slumptal (som ska vara svår att förutse och därmed efterlikna "äkta slump"), medan olika frön förhoppningsvis ger olika sekvenser.

Slumptalsgeneratorn måste alltså hålla reda på (bland annat) sitt frö – den har ett eget tillstånd (state). Den har också ett beteende, en funktionalitet: Den kan lämna ut nästa slumptal. Med både tillstånd och beteende passar det att modellera den som ett objekt.

Så är det också i Java: Slumptalsgeneratorer är objekt av klassen Random. Vi ska nu testa denna klass.

Uppgift 2.1.2: Slumptal

  1. Skapa en main()-metod i Slump.

  2. Vi skall nu skapa ett första objekt. Objekt skapas med hjälp av operatorn new. Vi skriver därför

       Random rnd = new Random();

    Som alla klasser i Java ligger Random i ett paket. Flera paket kan ha klasser med samma namn. När du skriver in Random vill IDEA därför veta vilken Random du menar. Den ger då en popup som föreslår import av java.util.Random, den enda Random som hittas just nu. Tryck Alt+Enter för att acceptera detta.

    Koden deklarerar att vi vill ha ett objekt av typen Random som vi kommer att referera till med namnet rnd. Vi skickar inte med några initialiseringsparametrar till konstruktorn utan är nöjda med defaultbeteendet hos Random.

Bakgrund 2.1.3: Javadoc

Javadoc är Javas standardiserade dokumentationsformat. Det byggs av speciella kommentarer som skrivs omedelbart före en klass, metod eller fält. Javadoc-verktyget gör om detta till indexerade HTML-filer. Dokumentationen för Javas standardklasser finns så klart färdigindexerad på nätet.

Uppgift 2.1.3: Visa Javadoc

Prova att placera markören i t.ex. Random() och trycka Ctrl-Q. Detta visar javadoc i en liten popup-ruta så att du snabbt får fram information utan att behöva lämna utvecklingsmiljön och navigera genom HTML-filerna.

Så småningom ska du skriva Javadoc-kommentarer även för din egen kod. Även där fungerar Ctrl-Q för att snabbt få fram information.

Exempel från fältet "out" i System.out:

Bakgrund 2.1.4: Auto-komplettering

IDEAs autokomplettering visar vilka valmöjligheter som finns för fortsatt kodskrivning och kan vara mycket användbar.

Uppgift 2.1.4: Använda autokomplettering

Testa att skriva "rnd." på raden under new Random();. Beroende på dina inställningar kanske rutan med möjliga fortsättningar kommer automatiskt, eller så får du ta fram den själv med Ctrl-Space.

Eftersom IDEA vet vilken typ variabeln har vet den exakt vad som kan stå efter "rnd.", vilket är ett väldigt bra sätt att lära sig mer om tillgänglig funktionalitet.

Du kan välja ett alternativ i listan eller fortsätta skriva. Om du fortsätter skriva kommer IDEA att filtrera listan och försöka hitta rätt metod utifrån det som skrivs. Skriver du t.ex. "on" kommer IDEA att föreslå " nextLong()".

Man kan navigera i alternativlistan med pil upp/ner. Man kan också trycka Ctrl-Q vilket tar fram javadoc-information för den entitet man just har markerat.

Bakgrund 2.1.5: Generera slumptal

Vi ska nu skapa och skriva ut slumptal med en hjälp av Random.

Uppgift 2.1.5: Generera slumptal

  1. Låt main()-metoden skriva ut 25 slumptal på varsin rad. Använd valfri typ av loop för att iterera 25 gånger. Använd t.ex. nextInt(100) för att generera ett slumptal mellan 0 och 99.

  2. Testa programmet.

Sammanfattning

Vi kan nu skapa objekt och anropa deras metoder. I övningen har vi använt en klass som är implementerad i Javas util-paket. I nästa övning skall vi skriva vår egen första "fullständiga" klass.

Övning 2.2: En egen "fullständig" klass

Syfte: Skapa egna objekt!

Vi ska nu gå vidare med skillnaderna mellan objektorienterad och icke objektorienterad programmering. Detta görs genom att vi skapar vår första mycket enkla "fullständiga" klass, som har både internt tillstånd (som lagras i fält), beteende (som anges av metoder), och en egen konstruktor.

Vi går även vidare med utskrifter och ser hur vi ger en klass en egen anpassad utskriftsmetod.

Bakgrund 2.2.1: Personklassen

Vi tänker oss att vi skall arbeta med olika personer i en applikation. Då är det naturligt att samla information och funktionalitet som rör en person i en klass.

För att kunna konstruera olika objekt av typen Person behöver vi en konstruktor, en metod som initialiserar varje nytt objekt. Den heter alltid samma som klassen, har ingen returtyp, och tar noll eller flera parametrar. Den kan bland annat sätta värden på alla fält i det objekt som den skapar, t.ex. genom att "spara undan" värden som den har fått som parametrar.

Uppgift 2.2.1: Personklassen

  1. Skapa klassen Person.

  2. Addera ett privat fält med namn name av typen String som skall användas för att lagra personens namn. Eftersom varje person har sitt eget namn (som lagras i personobjektet) ska fältet inte vara static.

  3. Addera ett privat fält birthDay av typen LocalDate som skall användas för att lagra personens födelsedatum. När LocalDate matas in kommer IDEA inte att känna till denna klass, men föreslå att den importeras av personklassen så att den känns igen och kan användas. Skulle man trycka bort möjligheten att låta IDEA importera LocalDate kommer IDEA att rödmarkera namnet. Ställer man markören i namnet kommer en röd lampa att tändas där man åter kan välja att IDEA skall importera klassen LocalDate.

    Här finns mer information om LocalDate för den som är intresserad.

  4. Lägg till en konstruktor som tar en String och en LocalDate som inparametrar och lagrar dessa värde i objektets "privata" fält. IDEA kan snabbt skapa en konstruktor via Alt+Insert->Constructor.

  5. Ett LocalDate-objekt som representerar ett datum kan t.ex. skapas med hjälp av anropet LocalDate.of(1990, 6, 1) om man vill skapa ett datum som representerar den 1:a juni 1990. Skapa en main()-metod (snabbast genom att skriva psvm och därefter trycka CTRL+Space eller CTRL-J och välja att ni vill ha en main() metod). I main()-metoden kan du skapa en person som motsvarar dig själv.

    Kör ditt testprogram och se att det inte kraschar!

Bakgrund 2.2.2: Tidsperioder

Vi skall nu utöka vår personklass med en metod för att beräkna hans/hennes ålder. Till vår hjälp har vi redan sett att LocalDate kan användas för att representera ett datum. För att få nuvarande tid använder vi LocalDate.now() som returnerar ett LocalDate objekt som representerar dagens datum. Om vi vill räkna ut en persons ålder behöver vi vet hur lång tid det är mellan dess födelse och dagens datum. Vi kan räkna ut hur lång tidsperiod det är mellan två datum med hjälp av klassen Period. Om man anropar Period.between(start,slut) där start och slut är av typen LocalDate får man ett Period-objekt. Man kan ta reda på hur många år som perioden består av genom metoden getYears().

Uppgift 2.2.2: Tidsperioder

  1. Implementera metoden getAge() som returnerar personens ålder. Eftersom varje person har sin egen ålder (som lagras i personobjektet) ska metoden inte vara static.

  2. Testa att metoden fungerar genom att skriva ut åldern på den person med dina data som skapats i main()-metoden.

Bakgrund 2.2.3: Utskrifter

Vi vill kunna skriva ut våra personer på ett läsligt format. Först provar vi vad som händer när vi skriver ut ett Person-objekt.

Uppgift 2.2.3: Utskrifter

Se till att main() skriver ut minst ett Person-objekt med System.out.println(). Testkör. Får du ett underligt resultat, i stil med "Person@28cd724"? Då är allt rätt.

Bakgrund 2.2.4: Utskrifter och toString()

Till skillnad från t.ex. listor finns det inget bra standardiserat sätt att skriva ut objekt. Att bara visa värdet på alla fält t.ex. är ofta inte det bästa sättet, även om det hade fungerat för personklassen som den ser ut just nu. Som standard skriver Java därför ut objekt på formen "klassnamn@objektID", där objektID är olika för varje objekt.

För att ändra detta implementerar man metoden public String toString() i sin klass. Vid ett anrop såsom System.out.println(mittObjekt) anropas då (indirekt och automatiskt) mittObjekt.toString(), och det är den returnerade strängen som skrivs ut istället för "klassnamn@objektID". Nu ska vi implementera en sådan metod.

IDEA kan själv skapa en toString() via Alt+Insert / toString(). Detta kan vara användbart när man snabbt vill generera en toString() för användning i debuggning (debuggern visar varje objekts toString() för att identifiera det). Den skulle dock generera ett resultat av typen "Person{name='namn', birthDay=1990-06-01}". I vår applikation är det viktigt att snabbt kunna se åldern på personer och därför gör vi på ett annat sätt.

(Avancerat: Alla klasser har egentligen redan en toString()-metod. Implementerar man ingen egen ärvs en ned från superklassen. Superklassen till Person är Object, och dess toString() har detta standardbeteende. Detta kommer att diskuteras när vi har gått genom arv.)

Uppgift 2.2.4: Utskrifter och toString()

  1. Tryck Alt+Insert och välj Override. Välj sedan toString().

  2. Vi sätter nu ihop en sträng som får representera vår person. Man kan sätta ihop två strängar med hjälp av "+"-operatorn. Om man sätter ihop en sträng med något som inte är en sträng kommer Java att konvertera det andra objektet till en sträng. Låt toString() returnera värdet name+" "+getAge().

  3. Skapa några personer till och skriv ut var och en på sin egen rad.

Funktionsargument och Smart Completion

IDEA har flera hjälpsamma funktioner. T.ex. så kommer en funktion som inte får inparametrar av rätt typ att rödmarkeras av IDEA, något som inte skulle fungera i Python där parametrar inte har typer. Om man sätter markören över får man reda på varför anropet inte fungerar. Testa att anropa konstruktorn till Person men med ett heltal istället för en sträng på namnpositionen. IDEA kommer då visa att man skickar med ett heltal när konstruktorn kräver en sträng. Det sparar mycket tid att man får reda på fel innan kompilering. Dessutom får man reda på vad som är rätt också!

Om du deklarerar String namn = "Anders Andersson"; och sedan någonstans längre ner i programmet skall skapa en person med detta namn kan IDEA hjälpa till genom "Smart Completion". Man kommer åt denna hjälp genom att trycka CTRL-Shift-Space. Skulle man stå på ett ställe där en String förväntas kommer IDEA att föreslå variabler som matchar. Om du gör ett anrop till Person-konstruktorn och trycker CTRL-Shift-Space när det är dags att ange name-parametern kommer IDEA föreslå att du skickar med namn-variabeln.

Sammanfattning

Vi kan nu skapa egna objekt och vet hur man konsturerar en strängrepresentation av dessa. Vi har sett hur man definierar klasser och initialiserar objekt då de skapas. Detta skiljer sig från programmering i icke objektorienterade språk.

Övning 2.3: Kalender

Syfte

Nu ska vi prova på att skapa en större sammanhängande uppsättning klasser för att lösa en specifik uppgift. Detta görs genom en almanacksuppgift som låter oss kontrastera de olika stegen mot den icke objektorienterade och "icke-typade" Python-programmering ni gjort i tidigare kurs. Vi går dock inte lika långt som tidigare – bara tillräckligt för att se kontrasterna i modellering.

Bakgrund 2.3.1: Typad OO-programmering

Vi skall nu skapa en klass Month som har motsvarande funktionalitet som i Python-almanackan.

I Python-almanackan behövde många funktioner programmeras explicit för varje typ, t.ex. för månader. Detta orsakades delvis av att programmet inte använde klasser, där man får mycket av detta "gratis" eller i alla fall "billigare".

  • is_month: objekt -> sanningsvärde testade om en lista var en månad med hjälp av get_tag(). Nu ska vi istället skapa klassen Month. Kräver en metod ett månadsobjekt behöver vi inte testa i efterhand om det var en månad som skickades – vi deklarerar helt enkelt en parameter av typen Month. Detta tar också bort många anrop till ensure() från implementationen.

  • new_month: sträng -> month, som skapar en månad av en textsträng som "january", blir en konstruktor. Detta använde attach_tag(), som inte längre behövs.

Funktioner som month_name(), month_number() och number_of_days() blir nu metoder i Month.

Uppgift 2.3.1: Objektorientering

  1. Skapa klassen Month med fälten:

    • name - månadens namn ("January")
    • number - månadens nummer (1)
    • days - antal dagar i månaden (31)
    Eftersom vi inte vill att någon skall ändra dessa värden utifrån lägger vi attributet "private" framför deklarationerna.

  2. Använd IDEA för att skapa en konstruktor som initialiserar Month åt dig. Tryck Alt+Insert och välj Constructor. I detta fall ska alla fält anges som parametrar till konstruktorn, så markera alla fält innan du trycker OK.

  3. Info: Getters/Setters

    Eftersom man oftast vill förhindra andra klasser att direkt använda fält i en klass är det vanligt att man skapar metoder för att läsa och ändra fält. Dessa kallas Getters och Setters och kan genereras automatiskt av IDEA. Fördelen med ett metodgränssnitt mot fälten är bland annat att den interna representationen kan ändras utan att någon utanför behöver modifiera sin kod.

    Tryck Alt+Insert och välj Getter, klicka i alla tre fälten och sedan Ok. Nu genererar IDEA funktioner så att andra klasser kan få ut informationen men inte ändra den.

Vi har nu alla funktioner som fanns i Python-month.

Bakgrund 2.3.2: Månadsdata

Enligt ovan associeras en månad med ett namn, ett månadsnummer, samt antal dagar. I nuläget kan man skapa en månad med vilka parametrar som helst. Detta är inte vad vi vill uppnå. Vi vill ha en klart definierad månad som beter sig så som vi förväntar oss. Därför behövs mer information kring månader. Vi måste kunna testa om en sträng motsvarar ett månadsnamn samt kunna fråga hur många dagar det finns i en månad med ett visst namn. Detta är information som är specifik för månader och den bör alltså ligga i klassen Month.

Vi kommer att bortse från skottår då det inte tillför något av värde ur objektorienteringssynpunkt!

Uppgift 2.3.2: Månadsdata

  1. Skapa i klassen Month metoderna getMonthNumber(String name) och getMonthDays(String name) som med hjälp av switch-satser (se labb 1) rapporterar månadsnummer och antal dagar i en månad utifrån det givna månadsnamnet. Låt default: returnera -1. Då kan vi använda någon av funktionerna för att testa om en sträng faktiskt representerar en existerande månad.

    Detta ska motsvara MONTH_NUMBERS och MONTH_DAYS i Python-almanackan, men vi implementerar det som en metod istället för en dictionary.

    I just detta fall vill vi använda statiska metoder (deklarerade static). Då behöver vi inte skapa ett Month-objekt för att kunna ta reda på antal dagar i en månad, utan anropar metoderna direkt i klassen istället. Metoderna anropas därmed som Month.getMonthNumber(...). Mer om detta kommer på senare föreläsningar.

Bakgrund 2.3.3: Tid och Datum

Vi kommer inte att skapa speciella typer för timmar, minuter och dagar. Eftersom vi inte ska gå vidare och ge dessa tidsenheter mer funktionalitet än att just innehålla ett heltal, använder vi i denna labb helt enkelt int för att representera dessa. Däremot behöver vi representera datum som är sammansatta av år, dag och månad. Vi kommer också att behöva representera tidpunkter samt tidsintervall.

Uppgift 2.3.3: Tid och Datum

  1. Skapa klassen Date med fälten year (int), month (Month) och day (int). Lägg till en konstruktor som tar in dessa som parametrar. Lägg också till getters för dem. Skriv en toString()-metod presenterar ett datum i ett format som du tycker är lämpligt.

  2. Skapa klassen TimePoint som innehåller ett klockslag för att representera start/slut på en aktivitet. Klassen skall ha fälten time (String), hour och minute. Generera getters för hour och minute. Låt IDEA generera en konstruktor som endast tar time som inparameter.

    I konstruktorn skall hour och minute tilldelas värden. Liksom i Python-almanackan kräver att en TimePoint skapas med en sträng i formatet "hh:mm". Detta bryter vi upp i konstruktorn med hjälp av anropet

     String[] parts = time.split(":");

    som skapar en substräng för varje del som delas av med ett kolon. Nu är parts[0] timmen (som sträng) och parts[1] minuterna. Detta kan sedan konverteras till heltal med Integer.parseInt(...), precis som i labb 1.

    De fält och metoder vi skapar nu ska anropas i ett objekt, inte i en klass. De ska alltså inte vara statiska! Vi frågar till exempel ett specifikt Date-objekt, inte datumklassen, vilket år det har.

    Extra information för den intresserade: Typen String[] är en array av strängar. Arrayer liknar listor, men kan aldrig ändra storlek efter att de har skapats. Vi kan få ut antalet element i arrayen med parts.length. Mer om arrayer kommer senare under kursen.

  3. Skriv en toString()-metod som endast returnerar time. Klassen har hour och minute som kan användas för att beräkna varaktigheter på aktiviteter samt att sortera dessa medan time ger oss en färdig sträng att skriva ut.

    Överkurs: Skapa en int compareTo(TimePoint other)-metod som returnerar -1 om den aktuella TimePoint kommer före other, 0 om de representerar samma tid och 1 om other kommer före.

  4. Skapa till sist klassen TimeSpan. Den behöver två fält av typen TimePoint, start och end. Gör getters och en konstruktor som tar in dessa. Skriv sedan en toString()-metod som skapar utskrifter av typen "12:15 - 13:15". Detta görs bland annat genom anrop till toString() i start och end. På detta sätt bygger man rekursivt upp en textrepresentation av ett sammansatt objekt.

Info: Listor

Java har ingen egen syntax för listor. Istället är de klasser som alla andra, och det finns flera olika listklasser med olika egenskaper. Den vanligaste och mest använda är ArrayList.

Listklasserna är generiska typer. Detta innebär helt enkelt att man kan ange en parameter som talar om vilken typ av element de innehåller, så att kompilatorn kan utföra bättre typkontroller. Om vi vill ha en lista som bara innehåller strängar:

   ArrayList<String> myList = new ArrayList<String>();

Vi kan därefter stoppa in och plocka ut element:

   myList.add("foo");  // Lägg till sist i listan
   String x = myList.get(0);    // Hämta första elementet
   String y = myList.get(3);    // Hämta fjärde elementet
   int antal = myList.size();   // Antal element i listan

Överkurs: Skriver ni som ovan kan det hända att IDEA klagar lite. ArrayList är nämligen ett specialfall av den mer generella typen List, och IDEA tycker att man ska skriva skriva så här istället. Detta förklaras senare i kursen.

   List<String> myList = new ArrayList<String>();

Bakgrund 2.3.4: Möten och kalendern

Allt som återstår nu är att lägga till en klass för möten och att skapa själva kalendern. Vi skapar en simpel kalender där alla möten lagras i en lista. En stor del av koden i metoden book() som används för att lägga möten i kalendern utgörs av parameterkontroll. Att kontrollera användarens inparametrar är ofta största delen av den kod som ligger i ett API. Vi kommer att generera ett undantag om man försöker skapa ett otillåtet möte.

Uppgift 2.3.4: Möten och kalendern

  1. Skapa klassen Appointment med tre fält, subject (String), date (typ Date) och timeSpan (typ TimeSpan). Skapa getters och en konstruktor som tar samtliga som parametrar. Skapa även en toString()-metod som använder sig av Date.toString() och TimeSpan.toString() för att formattera en fin utskrift.

  2. Skapa klassen Calendar. Den skall ha en privat lista av appointments som realiseras av en ArrayList, vilket är en collection som implementerar list-gränssnittet, i en konstruktor utan inparametrar. Detta sker genom raderna

       private List<Appointment> appointments;
    i klassen och

       appointments = new ArrayList<>();
    i konstruktorn.

  3. Implementera metoden show() som går igenom listan och skriver ut alla appointments. Detta kan med fördel göras genom att använda Live-Template (Ctrl+J) iter som leder till en så kallad for-each-loop där varje element i t.ex. en lista besöks. Denna loop har formatet "for (element : behållare)".
  4. Implementera metoden

       public void book(int year, String month, int day, 
            String start, String end, String subject)

    Notera att vi här tar in ett månadsnamn som en sträng. Att skapa ett Date-objekt kräver däremot att vi har ett månadsobjekt. Det är upp till book() att anropa lämpliga statiska metoder i Month för att ta reda på bl.a. vilket nummer månaden har och sedan skapa ett lämpligt månadsobjekt.

    Metoden skall skall kontrollera alla inparametrar. Detta betyder att

    • year > 2013
    • start/end skall konverteras och kontrolleras så att 0 <= hour <= 23 och 0 <= minute <= 59
    • month skall vara ett namn som motsvarar en existerande månad
    • månaden skall ha tillräckligt många dagar för att day skall vara tillåten
    • Överkurs: Använd compareTo() i TimePoint för att kontrollera att start är före end.

    Om någon av parametrarna är fel ska detta signaleras till anroparen. Detta gör man normalt med exceptions, "undantag". Exakt hur detta fungerar kommer vi att gå genom i en senare del av kursen. För tillfället räcker det med att ni vet att ett IllegalArgumentException ska kastas, och att detta görs med:

       throw new IllegalArgumentException("felmeddelande");

    När parametrarna är kontrollerade skall ett Date objekt skapas utifrån datuminformationen och två TimePoint objekt skapas utifrån start/end. Dessa två används sedan för att skapa ett TimeSpan och slutligen ett Appointment som läggs till i listan.

  5. Kalendern är nu färdig och du skall skriva ett testprogram som skapar en kalender, bokar 5-10 appointments och sedan skriver ut kalendern.

    Överkurs: Skriv ut appointments i sorterad ordning.

Sammanfattning

Du har nu byggt ett objektorienterat program med flera klasser. Du är bekväm med Setters/Getters, har testat for-each och listor i Java, inklusive generics, samt har kastat Exceptions.

Övning 2.4: Delegering

Syfte

Ett vanligt sätt att återanvända funktionalitet i existerande klasser i OO-programmering är ärvning, vilket vi inte har diskuterat än. Det finns dock andra sätt att åstadkomma något liknande – t.ex. komposition med delegering. Vi ska nu prova dessa tekniker.

Bakgrund 2.4.1: Stack och Kö

Vi skall nu skapa två enkla datastrukturer: kö och stack. För enkelhets skull går vi inte in närmare på generiska typer, utan håller oss till Person som element.

I en kan man bara lägga till element längst bak (enqueue) och ta bort längst fram (dequeue). Detta är enligt mottot "först in, först ut" (FIFO):

I en stack ("trave") däremot påverkar man alltid det översta elementet, med metoderna push och pop. Mottot är här "sist in, först ut" (LIFO):

Vi kommer att behöva någonstans att lagra de element som skall ligga i datastrukturerna. För att göra det enkelt för oss använder vi en redan existerande datastruktur, ArrayList från förra övningen. På så vis lämnar vi t.ex. över problemet att allokera lagom mycket lagringsutrymme till denna färdiga klass.

Men hur skall vi använda en ArrayList för att implementera detta?

Ett sätt är genom arv, något som ska diskuteras ingående under en senare del av kursen. Som vi ska se under en föreläsning är detta inte det bästa valet i denna situation. Istället vill vi använda sammansättning (composition). Det betyder helt enkelt att vi låter vår Stack ha och använda en ArrayList istället för att vara en.

Uppgift 2.4.1: Stack och kö

  1. Skapa klassen Queue.

  2. Skapa ett privat fält List<Person> elements och initialisera detta till en ny ArrayList<Person>. Eftersom varje Queue har sina egna element får detta fält inte vara statiskt.

  3. Det finns metoder i ArrayList som vi vill göra tillgängliga för användaren av Queue. size() är en sådan metod. Eftersom elements håller koll på antalet element den innehåller, skriver vi helt enkelt bara en size()-metod i Queue som returnerar resultatet av elements.size(). Vi delegerar alltså det egentliga arbetet till elements-listan. (IDEA kan hjälpa till – läs vidare!)

    Vi delegerar arbetet till elements även för metoderna isEmpty(), clear() och contains() på precis samma sätt, genom att ha en metod som direkt anropar och returnerar svaret från motsvarande metod i elements.

    Delegering är så pass vanligt att IDEA kan automatisera det åt oss. Genom att välja Code | Delegate Methods och sedan välja elements kan man därefter välja precis vilka metoder man vill delegera.

  4. Vi behöver även några "egna" metoder. Lägg till:

    • enqueue(), som tar en person som parameter och lägger till den sist i kön, och
    • dequeue(), som hämtar personen som är först i kön, tar bort den ur kön (listan), och returnerar den.

    Dessa skall använda sig av metoderna elements.add() och elements.remove(int index) för att ta ut första elementet i elements och stoppa in ett element sist i listan.

    Tänk på att t.ex. enqueue ska lägga till ett element i en specifik kö (ett specifikt Queue-objekt), inte i kö-klassen i sin helhet. Metoderna du skriver här ska alltså inte vara statiska.

  5. Nu är Queue färdig och det är dags att skapa Stack. Klasserna är lika på alla punkter utom var objekt läggs till/tas ut. Därför kan man använda IDEAs funktion clone class som man kommer åt genom att trycka F5 när markören står i klassnamnet. Ange bara namnet Stack så skapas en ny klass som ser precis ut som Queue. Byt namn på enqueue/dequeue till push/pop och ändra koden i dem så att Stack beter sig som en stack.

  6. Slutligen, skriv ett testprogram.

    Skapa en stack, lägg i tur och ordning in 5 olika personer i denna, och plocka sedan ut och skriv ut element i den ordning de kommer. Vi kunde använda en for-loop, men vi har egentligen inget behov av att veta vilken position vi är på – bara om det finns fler element kvar eller inte. Därför använder vi istället en while-loop som itererar så länge stacken inte är tom.

    Gör även samma sak med en kö.

Avslutning

Här slutar andra laborationen. Visa slutresultatet för din handledare och passa på att fråga om det är något du undrar över. Skulle handledaren vara upptagen går det bra att börja med laboration 3.

Labb av Mikael Nilsson, Jonas Kvarnström 2014–2016.


Sidansvarig: Jonas Kvarnström
Senast uppdaterad: 2017-01-09