Tämä osa on ohjelmoinnin jatkokurssin kertausosa. Kertaustehtävillä voi paikata väliin jääneitä tehtäviä. Kertausosan tehtävät tulee tehdä ennen kurssin toista koetta.
Tässä tehtäväsarjassa tehdään luokat Tavara
, Matkalaukku
ja Lastiruuma
, joiden avulla harjoitellaan olioita, jotka sisältävät toisia olioita.
Tavara-luokka
Tee luokka Tavara
, josta muodostetut oliot vastaavat erilaisia tavaroita. Tallennettavat tiedot ovat tavaran nimi ja paino (kg).
Lisää luokkaan seuraavat metodit:
- Konstruktori, jolle annetaan parametrina tavaran nimi ja paino
-
Metodi
public String getNimi()
, joka palauttaa tavaran nimen -
Metodi
public int getPaino()
, joka palauttaa tavaran painon -
Metodi
public String toString()
, joka palauttaa merkkijonon muotoa "nimi (paino kg)"
Seuraavassa on luokan käyttöesimerkki:
public class Main {
public static void main(String[] args) {
Tavara kirja = new Tavara("Aapiskukko", 2);
Tavara puhelin = new Tavara("Nokia 3210", 1);
System.out.println("Kirjan nimi: " + kirja.getNimi());
System.out.println("Kirjan paino: " + kirja.getPaino());
System.out.println("Kirja: " + kirja);
System.out.println("Puhelin: " + puhelin);
}
}
Ohjelman tulostuksen tulisi olla seuraava:
Kirjan nimi: Aapiskukko Kirjan paino: 2 Kirja: Aapiskukko (2 kg) Puhelin: Nokia 3210 (1 kg)
Matkalaukku-luokka
Tee luokka Matkalaukku
. Matkalaukkuun liittyy tavaroita ja maksimipaino, joka määrittelee tavaroiden suurimman mahdollisen yhteispainon.
Lisää luokkaan seuraavat metodit:
- Konstruktori, jolle annetaan maksimipaino
-
Metodi
public void lisaaTavara(Tavara tavara)
, joka lisää parametrina annettavan tavaran matkalaukkuun. Metodi ei palauta mitään arvoa. -
Metodi
public String toString()
, joka palauttaa merkkijonon muotoa "x tavaraa (y kg)"
Tavarat kannattaa tallentaa ArrayList
-olioon:
ArrayList<Tavara> tavarat = new ArrayList<Tavara>();
Luokan Matkalaukku
tulee valvoa, että sen sisältämien tavaroiden yhteispaino ei ylitä maksimipainoa. Jos maksimipaino ylittyisi lisättävän tavaran vuoksi, metodi lisaaTavara
ei saa lisätä uutta tavaraa laukkuun.
Seuraavassa on luokan käyttöesimerkki:
public class Main {
public static void main(String[] args) {
Tavara kirja = new Tavara("Aapiskukko", 2);
Tavara puhelin = new Tavara("Nokia 3210", 1);
Tavara tiiliskivi = new Tavara("tiiliskivi", 4);
Matkalaukku matkalaukku = new Matkalaukku(5);
System.out.println(matkalaukku);
matkalaukku.lisaaTavara(kirja);
System.out.println(matkalaukku);
matkalaukku.lisaaTavara(puhelin);
System.out.println(matkalaukku);
matkalaukku.lisaaTavara(tiiliskivi);
System.out.println(matkalaukku);
}
}
Ohjelman tulostuksen tulisi olla seuraava:
0 tavaraa (0 kg) 1 tavaraa (2 kg) 2 tavaraa (3 kg) 2 tavaraa (3 kg)
Kielenhuoltoa
Ilmoitukset "0 tavaraa" ja "1 tavaraa" eivät ole kovin hyvää suomea – paremmat muodot olisivat "ei tavaroita" ja "1 tavara". Tee tämä muutos luokkaan Matkalaukku
.
Nyt edellisen ohjelman tulostuksen tulisi olla seuraava:
ei tavaroita (0 kg) 1 tavara (2 kg) 2 tavaraa (3 kg) 2 tavaraa (3 kg)
Kaikki tavarat
Lisää luokkaan Matkalaukku
seuraavat metodit:
-
Metodi
tulostaTavarat
, joka tulostaa kaikki matkalaukussa olevat tavarat -
Metodi
yhteispaino
, joka palauttaa tavaroiden yhteispainon
Seuraavassa on luokan käyttöesimerkki:
public class Main {
public static void main(String[] args) {
Tavara kirja = new Tavara("Aapiskukko", 2);
Tavara puhelin = new Tavara("Nokia 3210", 1);
Tavara tiiliskivi = new Tavara("tiiliskivi", 4);
Matkalaukku matkalaukku = new Matkalaukku(10);
matkalaukku.lisaaTavara(kirja);
matkalaukku.lisaaTavara(puhelin);
matkalaukku.lisaaTavara(tiiliskivi);
System.out.println("Matkalaukussa on seuraavat tavarat:");
matkalaukku.tulostaTavarat();
System.out.println("Yhteispaino: " + matkalaukku.yhteispaino() + " kg");
}
}
Ohjelman tulostuksen tulisi olla seuraava:
Matkalaukussa on seuraavat tavarat: Aapiskukko (2 kg) Nokia 3210 (1 kg) Tiiliskivi (4 kg) Yhteispaino: 7 kg
Muokkaa myös luokkaasi siten, että käytät vain kahta oliomuuttujaa. Toinen sisältää maksimipainon, toinen on lista laukussa olevista tavaroista.
Raskain tavara
Lisää vielä luokkaan Matkalaukku
metodi raskainTavara
, joka palauttaa painoltaan suurimman tavaran. Jos yhtä raskaita tavaroita on useita, metodi voi palauttaa minkä tahansa niistä. Metodin tulee palauttaa olioviite. Jos laukku on tyhjä, palauta arvo null.
Seuraavassa on luokan käyttöesimerkki:
public class Main {
public static void main(String[] args) {
Tavara kirja = new Tavara("Aapiskukko", 2);
Tavara puhelin = new Tavara("Nokia 3210", 1);
Tavara tiiliskivi = new Tavara("Tiiliskivi", 4);
Matkalaukku matkalaukku = new Matkalaukku(10);
matkalaukku.lisaaTavara(kirja);
matkalaukku.lisaaTavara(puhelin);
matkalaukku.lisaaTavara(tiiliskivi);
Tavara raskain = matkalaukku.raskainTavara();
System.out.println("Raskain tavara: " + raskain);
}
}
Ohjelman tulostuksen tulisi olla seuraava:
Raskain tavara: Tiiliskivi (4 kg)
Lastiruuma-luokka
Tee luokka Lastiruuma
, johon liittyvät seuraavat metodit:
- Konstruktori, jolle annetaan maksimipaino
-
Metodi
public void lisaaMatkalaukku(Matkalaukku laukku)
, joka lisää parametrina annetun matkalaukun lastiruumaan -
Metodi
public String toString()
, joka palauttaa merkkijonon muotoa "x matkalaukkua (y kg)"
Tallenna matkalaukut sopivaan ArrayList
-rakenteeseen.
Luokan Lastiruuma
tulee valvoa, että sen sisältämien matkalaukkujen yhteispaino ei ylitä maksimipainoa. Jos maksimipaino ylittyisi uuden matkalaukun vuoksi, metodi lisaaMatkalaukku
ei saa lisätä uutta matkalaukkua.
Seuraavassa on luokan käyttöesimerkki:
public class Main {
public static void main(String[] args) {
Tavara kirja = new Tavara("Aapiskukko", 2);
Tavara puhelin = new Tavara("Nokia 3210", 1);
Tavara tiiliskivi = new Tavara("tiiliskivi", 4);
Matkalaukku matinLaukku = new Matkalaukku(10);
matinLaukku.lisaaTavara(kirja);
matinLaukku.lisaaTavara(puhelin);
Matkalaukku pekanLaukku = new Matkalaukku(10);
pekanLaukku.lisaaTavara(tiiliskivi);
Lastiruuma lastiruuma = new Lastiruuma(1000);
lastiruuma.lisaaMatkalaukku(matinLaukku);
lastiruuma.lisaaMatkalaukku(pekanLaukku);
System.out.println(lastiruuma);
}
}
Ohjelman tulostuksen tulisi olla seuraava:
2 matkalaukkua (7 kg)
Lastiruuman sisältö
Lisää luokkaan Lastiruuma
metodi public void tulostaTavarat()
, joka tulostaa kaikki lastiruuman matkalaukuissa olevat tavarat.
Seuraavassa on luokan käyttöesimerkki:
public class Main {
public static void main(String[] args) {
Tavara kirja = new Tavara("Aapiskukko", 2);
Tavara puhelin = new Tavara("Nokia 3210", 1);
Tavara tiiliskivi = new Tavara("tiiliskivi", 4);
Matkalaukku matinLaukku = new Matkalaukku(10);
matinLaukku.lisaaTavara(kirja);
matinLaukku.lisaaTavara(puhelin);
Matkalaukku pekanLaukku = new Matkalaukku(10);
pekanLaukku.lisaaTavara(tiiliskivi);
Lastiruuma lastiruuma = new Lastiruuma(1000);
lastiruuma.lisaaMatkalaukku(matinLaukku);
lastiruuma.lisaaMatkalaukku(pekanLaukku);
System.out.println("Ruuman matkalaukuissa on seuraavat tavarat:");
lastiruuma.tulostaTavarat();
}
}
Ohjelman tulostuksen tulisi olla seuraava:
Ruuman matkalaukuissa on seuraavat tavarat: Aapiskukko (2 kg) Nokia 3210 (1 kg) tiiliskivi (4 kg)
Paljon tiiliskiviä
Testataan vielä, että lastiruuman toiminta on oikea eikä maksimipaino pääse ylittymään. Tee Main-luokkaan metodi public static void lisaaMatkalaukutTiiliskivilla(Lastiruuma lastiruuma)
, joka lisää parametrina annettuun lastiruumaan 100 matkalaukkua, joissa jokaisessa on yksi tiiliskivi. Tiiliskivien painot ovat 1, 2, 3, ..., 100 kg.
Ohjelman runko on seuraava:
public class Main {
public static void main(String[] args) {
Lastiruuma lastiruuma = new Lastiruuma(1000);
lisaaMatkalaukutTiiliskivilla(lastiruuma);
System.out.println(lastiruuma);
}
public static void lisaaMatkalaukutTiiliskivilla(Lastiruuma lastiruuma) {
// 100 matkalaukun lisääminen, jokaiseen tulee tiiliskivi
}
}
Ohjelman tulostus on seuraava:
44 matkalaukkua (990 kg)
Luo main
-metodissa uusi HashMap<String,String>
-olio. Tallenna tähän HashMappiin seuraavien henkilöiden nimet ja lempinimet niin, että nimi on avain ja lempinimi on arvo. Käytä pelkkiä pieniä kirjaimia.
- matin lempinimi on mage
- mikaelin lempinimi on mixu
- arton lempinimi on arppa
Tämän jälkeen hae HashMapistä mikaelin lempinimi ja tulosta se.
Tehtäväpohjassa ei ole testejä.
Talletettavia
Muuton yhteydessa tarvitaan muuttolaatikoita. Laatikoihin talletetaan erilaisia esineitä. Kaikkien laatikoihin talletettavien esineiden on toteutettava seuraava rajapinta:
public interface Talletettava {
double paino();
}
Lisää rajapinta ohjelmaasi. Rajapinta lisätään melkein samalla tavalla kuin luokka, new Java class sijaan valitaan new Java interface.
Tee rajapinnan toteuttavat luokat Kirja
ja CDLevy
. Kirja saa konstruktorin parametreina kirjan kirjoittajan (String), kirjan nimen (String), ja kirjan painon (double). CD-Levyn konstruktorin parametreina annetaan artisti (String), levyn nimi (String), ja julkaisuvuosi (int). Kaikkien CD-levyjen paino on 0.1 kg.
Muista toteuttaa luokilla myös rajapinta Talletettava
. Luokkien tulee toimia seuraavasti:
public static void main(String[] args) {
Kirja kirja1 = new Kirja("Fedor Dostojevski", "Rikos ja Rangaistus", 2);
Kirja kirja2 = new Kirja("Robert Martin", "Clean Code", 1);
Kirja kirja3 = new Kirja("Kent Beck", "Test Driven Development", 0.5);
CDLevy cd1 = new CDLevy("Pink Floyd", "Dark Side of the Moon", 1973);
CDLevy cd2 = new CDLevy("Wigwam", "Nuclear Nightclub", 1975);
CDLevy cd3 = new CDLevy("Rendezvous Park", "Closer to Being Here", 2012);
System.out.println(kirja1);
System.out.println(kirja2);
System.out.println(kirja3);
System.out.println(cd1);
System.out.println(cd2);
System.out.println(cd3);
}
Tulostus:
Fedor Dostojevski: Rikos ja Rangaistus Robert Martin: Clean Code Kent Beck: Test Driven Development Pink Floyd: Dark Side of the Moon (1973) Wigwam: Nuclear Nightclub (1975) Rendezvous Park: Closer to Being Here (2012)
Huom! Painoa ei ilmoiteta tulostuksessa.
Laatikko
Tee luokka laatikko, jonka sisälle voidaan tallettaa Talletettava
-rajapinnan toteuttavia tavaroita. Laatikko saa konstruktorissaan parametrina laatikon maksimikapasiteetin kiloina. Laatikkoon ei saa lisätä enempää tavaraa kuin sen maksimikapasiteetti määrää. Laatikon sisältämien tavaroiden paino ei siis koskaan saa olla yli laatikon maksimikapasiteetin.
Seuraavassa esimerkki laatikon käytöstä:
public static void main(String[] args) {
Laatikko laatikko = new Laatikko(10);
laatikko.lisaa( new Kirja("Fedor Dostojevski", "Rikos ja Rangaistus", 2) ) ;
laatikko.lisaa( new Kirja("Robert Martin", "Clean Code", 1) );
laatikko.lisaa( new Kirja("Kent Beck", "Test Driven Development", 0.7) );
laatikko.lisaa( new CDLevy("Pink Floyd", "Dark Side of the Moon", 1973) );
laatikko.lisaa( new CDLevy("Wigwam", "Nuclear Nightclub", 1975) );
laatikko.lisaa( new CDLevy("Rendezvous Park", "Closer to Being Here", 2012) );
System.out.println( laatikko );
}
Tulostuu
Laatikko: 6 esinettä, paino yhteensä 4.0 kiloa
Huom: koska painot esitetään doubleina, saattaa laskutoimituksissa tulla pieniä pyöristysvirheitä. Tehtävässä ei tarvitse välittää niistä.
Laatikon paino
Jos teit laatikon sisälle oliomuuttujan double paino
, joka muistaa laatikossa olevien esineiden painon, korvaa se metodilla, joka laskee painon:
public class Laatikko {
//...
public double paino() {
double paino = 0;
// laske laatikkoon talletettujen tavaroiden yhteispaino
return paino;
}
}
Kun tarvitset laatikon sisällä painoa esim. uuden tavaran lisäyksen yhteydessä, riittää siis kutsua laatikon painon laskevaa metodia.
Metodi toki voisi palauttaa myös oliomuuttujan arvon. Harjoittelemme tässä kuitenkin tilannetta, jossa oliomuuttujaa ei tarvitse eksplisiittisesti ylläpitää vaan se voidaan tarpeentullen laskea. Seuraavan tehtävän jälkeen laatikossa olevaan oliomuuttujaan talletettu painotieto ei kuitenkaan välttämättä enää toimisi. Miksi?
Laatikkokin on talletettava!
Rajapinnan Talletettava
toteuttaminen siis edellyttää että luokalla on metodi double paino()
. Laatikollehan lisättiin juuri tämä metodi. Laatikosta voidaan siis tehdä talletettava!
Laatikot ovat oliota joihin voidaan laittaa Talletettava
-rajapinnan toteuttavia olioita. Laatikot toteuttavat itsekin rajapinnan. Eli laatikon sisällä voi olla myös laatikoita!
Kokeile että näin varmasti on, eli tee ohjelmassasi muutama laatikko, laita laatikoihin tavaroita ja laita pienempiä laatikoita isompien laatikoiden sisään. Kokeile myös mitä tapahtuu kun laitat laatikon itsensä sisälle. Miksi näin käy?
Tässä tehtävässä teemme eliöita ja eliöistä koostuvia laumoja jotka liikkuvat ympäriinsä. Eliöiden sijaintien ilmoittamiseen käytetään kaksiulotteista koordinaatistoa. Jokaiseen sijaintiin liittyy kaksi lukua, x
- ja y
-koordinaatti. Koordinaatti x
kertoo, kuinka pitkällä "nollapisteestä" mitattuna sijainti on vaakasuunnassa, ja koordinaatti y
vastaavasti kuinka pitkällä sijainti on pystysuunnassa. Jos koordinaatiston käsite ei ole tuttu, voit lukea siitä lisää esimerkiksi wikipediasta.
Tehtävän mukana tulee rajapinta Siirrettava
, joka kuvaa asiaa jota voidaan siirtää paikasta toiseen. Rajapinta sisältää metodin void siirra(int dx, int dy)
. Parametri dx
kertoo, paljonko asia siirtyy x-akselilla ja dy
y-akselilla.
Tehtävässä toteutat luokat Elio
ja Lauma
, jotka molemmat ovat siirrettäviä. Toteuta kaikki toiminnallisuus pakkaukseen siirrettava
.
Elio-luokan toteuttaminen
Luo pakkaukseen siirrettava
luokka Elio
, joka toteuttaa rajapinnan Siirrettava
. Eliön tulee tietää oma sijaintinsa (x, y -koordinaatteina). Luokan Elio
APIn tulee olla seuraava:
-
public Elio(int x, int y)
Luokan konstruktori, joka saa olion aloitussijainnin x- ja y-koordinaatit parametrina -
public String toString()
Luo ja palauttaa oliosta merkkijonoesityksen. Eliön merkkijonoesityksen tulee olla seuraavanlainen"x: 3; y: 6"
. Huomaa että koordinaatit on erotettu puolipisteellä (;
) -
public void siirra(int dx, int dy)
Siirtää oliota parametrina saatujen arvojen verran. Muuttujadx
sisältää muutoksen koordinaattiinx
, muuttujady
sisältää muutoksen koordinaattiiny
. Esimerkiksi jos muuttujandx
arvo on 5, tulee oliomuuttujanx
arvoa kasvattaa viidellä
Kokeile luokan Elio
toimintaa seuraavalla esimerkkikoodilla.
Elio elio = new Elio(20, 30);
System.out.println(elio);
elio.siirra(-10, 5);
System.out.println(elio);
elio.siirra(50, 20);
System.out.println(elio);
x: 20; y: 30 x: 10; y: 35 x: 60; y: 55
Lauman toteutus
Luo seuraavaksi pakkaukseen siirrettava
luokka Lauma
, joka toteuttaa rajapinnan Siirrettava
. Lauma koostuu useasta Siirrettava
-rajapinnan toteutavasta oliosta, jotka tulee tallettaa esimerkiksi listarakenteeseen.
Luokalla Lauma
tulee olla seuraavanlainen API.
-
public String toString()
Palauttaa merkkijonoesityksen lauman jäsenten sijainnista rivin vaihdolla erotettuna. -
public void lisaaLaumaan(Siirrettava siirrettava)
Lisää laumaan uudenSiirrettava
-rajapinnan toteuttavan olion -
public void siirra(int dx, int dy)
Siirtää laumaa parametrina saatujen arvojen verran. Huomaa että tässä sinun tulee siirtää jokaista lauman jäsentä.
Kokeile ohjelmasi toimintaa alla olevalla esimerkkikoodilla.
Lauma lauma = new Lauma();
lauma.lisaaLaumaan(new Elio(73, 56));
lauma.lisaaLaumaan(new Elio(57, 66));
lauma.lisaaLaumaan(new Elio(46, 52));
lauma.lisaaLaumaan(new Elio(19, 107));
System.out.println(lauma);
x: 73; y: 56 x: 57; y: 66 x: 46; y: 52 x: 19; y: 107
Kakki sovelluksessa oleva koodi tulee sijoittaa pakkaukseen sovellus
.
Käytössämme on seuraava rajapinta:
public interface Sensori {
boolean onPaalla(); // palauttaa true jos sensori on päällä
void paalle(); // käynnistä sensorin
void poisPaalta(); // sulkee sensorin
int mittaa(); // palauttaa sensorin lukeman jos sensori on päällä
// jos sensori ei päällä heittää poikkeuksen IllegalStateException
}
Vakiosensori
Tee luokka Vakiosensori
joka toteuttaa rajapinnan Sensori
.
Vakiosensori on koko ajan päällä. Metodien paalle ja poisPaalta kutsuminen ei tee mitään. Vakiosensorilla tulee olla konstruktori, jonka parametrina on kokonaisluku. Metodikutsu mittaa
palauttaa aina konstruktorille parametrina annetun luvun.
Esimerkki:
public static void main(String[] args) {
Vakiosensori kymppi = new Vakiosensori(10);
Vakiosensori miinusViis = new Vakiosensori(-5);
System.out.println( kymppi.mittaa() );
System.out.println( miinusViis.mittaa() );
System.out.println( kymppi.onPaalla() );
kymppi.poisPaalta();
System.out.println( kymppi.onPaalla() );
}
Tulostuu:
10 -5 true true
Lampomittari
Tee luokka Lampomittari
joka toteuttaa rajapinnan Sensori
.
Aluksi lämpömittari on poissa päältä. Kutsuttaessa metodia mittaa
kun mittari on päällä mittari arpoo luvun väliltä -30...30 ja palauttaa sen kutsujalle. Jos mittari ei ole päällä, heitetään poikkeus IllegalStateException
.
Keskiarvosensori
Tee luokka Keskiarvosensori
joka toteuttaa rajapinnan Sensori
.
Keskiarvosensori sisältää useita sensoreita. Rajapinnan Sensori
määrittelemien metodien lisäksi keskiarvosensorilla on metodi public void lisaaSensori(Sensori lisattava)
jonka avulla keskiarvosensorin hallintaan lisätään uusi sensori.
Keskiarvosensori on päällä silloin kuin kaikki sen sisältävät sensorit ovat päällä. Kun keskiarvosensori käynnistetään, täytyy kaikkien sen sisältävien sensorien käynnistyä jos ne eivät ole käynnissä. Kun keskiarvosensori suljetaan, täytyy ainakin yhden sen sisältävän sensorin mennä pois päältä. Saa myös käydä niin että kaikki sen sisältävät sensorit menevät pois päältä.
Keskiarvosensorin metodi mittaa
palauttaa sen sisältämien sensoreiden lukemien keskiarvon (koska paluuarvo on int
, pyöristyy lukema alaspäin kuten kokonaisluvuilla tehdyissä jakolaskuissa). Jos keskiarvosensorin metodia mittaa
kutsutaan sensorin ollessa poissa päältä, tai jos keskiarvosensorille ei vielä ole lisätty yhtään sensoria heitetään poikkeus IllegalStateException
.
Seuraavassa sensoreja käyttävä esimerkkiohjelma (huomaa, että sekä Lämpömittarin että Keskiarvosensorin konstruktorit ovat parametrittomia):
public static void main(String[] args) {
Sensori kumpula = new Lampomittari();
kumpula.paalle();
System.out.println("lämpötila Kumpulassa "+kumpula.mittaa() + " astetta");
Sensori kaisaniemi = new Lampomittari();
Sensori helsinkiVantaa = new Lampomittari();
Keskiarvosensori paakaupunki = new Keskiarvosensori();
paakaupunki.lisaaSensori(kumpula);
paakaupunki.lisaaSensori(kaisaniemi);
paakaupunki.lisaaSensori(helsinkiVantaa);
paakaupunki.paalle();
System.out.println("lämpötila Pääkaupunkiseudulla "+paakaupunki.mittaa() + " astetta");
}
tulostuu (tulostetut lukuarvot riippuvat tietenkin arvotuista lämpötiloista):
lämpötila Kumpulassa -7 astetta
lämpötila Pääkaupunkiseudulla -10 astetta
Huom: kannattaa käyttää Vakiosensori-oliota keskiarvosensorin testaamiseen!
Kaikki mittaukset
Lisää luokalle Keskiarvosensori metodi public List<Integer> mittaukset()
, joka palauttaa listana kaikkien keskiarvosensorin avulla suoritettujen mittausten tulokset. Seuraavassa esimerkki metodin toiminnasta:
public static void main(String[] args) {
Sensori kumpula = new Lampomittari();
Sensori kaisaniemi = new Lampomittari();
Sensori helsinkiVantaa = new Lampomittari();
Keskiarvosensori paakaupunki = new Keskiarvosensori();
paakaupunki.lisaaSensori(kumpula);
paakaupunki.lisaaSensori(kaisaniemi);
paakaupunki.lisaaSensori(helsinkiVantaa);
paakaupunki.paalle();
System.out.println("lämpötila Pääkaupunkiseudulla "+paakaupunki.mittaa() + " astetta");
System.out.println("lämpötila Pääkaupunkiseudulla "+paakaupunki.mittaa() + " astetta");
System.out.println("lämpötila Pääkaupunkiseudulla "+paakaupunki.mittaa() + " astetta");
System.out.println("mittaukset: "+paakaupunki.mittaukset());
}
tulostuu (tulostetut lukuarvot riippuvat jälleen arvotuista lämpötiloista):
lämpötila Pääkaupunkiseudulla -10 astetta
lämpötila Pääkaupunkiseudulla -4 astetta
lämpötila Pääkaupunkiseudulla 5 astetta
mittaukset: [-10, -4, 5]
Tässä tehtävässä tehdään sovellus tiedoston rivi- ja merkkimäärän laskemiseen.
Rivien laskeminen
Tee pakkaukseen tiedosto
luokka Analyysi
, jolla on konstruktori public Analyysi(File tiedosto)
. Toteuta luokalle metodi public int rivimaara()
, joka palauttaa konstruktorille annetun tiedoston rivimäärän.
Metodi ei saa olla "kertakäyttöinen", eli sen pitää tuottaa oikea tulos myös usealla peräkkäisellä kutsulla. Huomaa, että kun teet tiedostoa vastaavan Scanner-olion, ja luet tiedoston koko sisällön nextLine
-komennoilla, et voi käyttää enää samaa skanneria tiedoston uudelleenlukemiseen!
Huom: jos testit sanovat timeout, et todennäköisesti muista lukea tiedostoa ollenkaan, eli nextLine
-kutsut puuttuvat!
Merkkien laskeminen
Toteuta luokkaan Analyysi
metodi public int merkkeja()
, joka palauttaa luokan konstruktorille annetun tiedoston merkkien määrän.
Metodi ei saa olla "kertakäyttöinen", eli sen pitää tuottaa oikea tulos myös usealla peräkkäisellä kutsulla.
Voit itse päättää miten reagoidaan jos konstruktorin parametrina saatua tiedostoa ei ole olemassa.
Projektisi testipakkauksessa on testausta varten tiedosto testitiedosto.txt. Ohjelmasta avatessa tiedoston nimeksi tulee antaa test/testitiedosto.txt
. Tiedoston sisältö on seuraava:
rivejä tässä on 3 ja merkkejä koska rivinvaihdotkin ovat merkkejä
Ohjelman toiminta testaustiedostolla:
File tiedosto = new File("src/testitiedosto.txt");
Analyysi analyysi = new Analyysi(tiedosto);
System.out.println("Rivejä: " + analyysi.rivimaara());
System.out.println("Merkkejä: " + analyysi.merkkeja());
Rivejä: 3 Merkkejä: 67
Tehtäväpohjassa tulee mukana luokka Varasto
, jonka tarjoamat konstruktorit ja metodit ovat seuraavat:
-
public Varasto(double tilavuus)
Luo tyhjän varaston, jonka vetoisuus eli tilavuus annetaan parametrina; sopimaton tilavuus (<=0) luo käyttökelvottoman varaston, jonka tilavuus on 0. -
public double getSaldo()
Palauttaa arvonaan varaston saldon, eli varastossa olevan tilavuuden. -
public double getTilavuus()
Palauttaa arvonaan varaston tilavuuden (eli sen, joka annettiin konstruktorille). - public double paljonkoMahtuu()
Palauttaa arvonaan tiedon, paljonko varastoon vielä mahtuu. - public void lisaaVarastoon(double maara)
Lisää varastoon pyydetyn määrän; jos määrä on negatiivinen, mikään ei muutu, jos kaikki pyydetty ei enää mahdu, varasto laitetaan täydeksi ja loput määräsätä "heitetään menemään", "vuotaa yli". -
public double otaVarastosta(double maara)
Otetaan varastosta pyydetty määrä, metodi palauttaa paljonko saadaan. Jos pyydetty määrä on negatiivinen, mikään ei muutu ja palautetaan nolla. Jos pyydetään enemmän kuin varastossa on, annetaan mitä voidaan ja varasto tyhjenee. -
public String toString()
Palauttaa olion tilan merkkijonoesityksenä tyyliin saldo = 64.5, tilaa 123.5
Tehtävässä rakennetaan Varasto
-luokasta useampia erilaisia varastoja. Huom! Toteuta kaikki luokat pakkaukseen varastot
.
Tuotevarasto, vaihe 1
Luokka Varasto
hallitsee tuotteen määrään liittyvät toiminnot. Nyt tuotteelle halutaan lisäksi tuotenimi ja nimen käsittelyvälineet. Ohjelmoidaan Tuotevarasto Varaston aliluokaksi! Toteutetaan ensin pelkkä yksityinen oliomuuttuja tuotenimelle, konstruktori ja getteri nimikentälle:
-
public Tuotevarasto(String tuotenimi, double tilavuus)
Luo tyhjän tuotevaraston. Tuotenimi ja vetoisuus annetaan parametrina. -
public String getNimi()
Palauttaa arvonaan tuotteen nimen.
Muista millä tavoin konstruktori voi ensi toimenaan suorittaa yliluokan konstruktorin!
Käyttöesimerkki:
Tuotevarasto mehu = new Tuotevarasto("Juice", 1000.0);
mehu.lisaaVarastoon(1000.0);
mehu.otaVarastosta(11.3);
System.out.println(mehu.getNimi()); // Juice
System.out.println(mehu); // saldo = 988.7, tilaa 11.3
Juice saldo = 988.7, vielä tilaa 11.3
Tuotevarasto, vaihe 2
Kuten edellisestä esimerkistä näkee, Tuotevarasto-olion perimä toString()
ei tiedä (tietenkään!) mitään tuotteen nimestä. Asialle on tehtävä jotain! Lisätään samalla myös setteri tuotenimelle:
- public void setNimi(String uusiNimi) asettaa tuotteelle uuden nimen.
- public String toString() palauttaa olion tilan merkkijonoesityksenä tyyliin Juice: saldo = 64.5, tilaa 123.5
Uuden toString()
-metodin voisi toki ohjelmoida käyttäen yliluokalta perittyjä gettereitä, joilla perittyjen, mutta piilossa pidettyjen kenttien arvoja saa käyttöönsä. Koska yliluokkaan on kuitenkin jo ohjelmoitu tarvittava taito varastotilanteen merkkiesityksen tuottamiseen, miksi nähdä vaivaa sen uudelleen ohjelmointiin. Käytä siis hyväksesi perittyä toString
iä.
Muista miten korvattua metodia voi kutsua aliluokassa!
Käyttöesimerkki:
Tuotevarasto mehu = new Tuotevarasto("Juice", 1000.0);
mehu.lisaaVarastoon(1000.0);
mehu.otaVarastosta(11.3);
System.out.println(mehu.getNimi()); // Juice
mehu.lisaaVarastoon(1.0);
System.out.println(mehu); // Juice: saldo = 989.7, tilaa 10.299999999999955
Juice Juice: saldo = 989.7, tilaa 10.299999999999955
Muutoshistoria
Toisinaan saattaa olla kiinostavaa tietää, millä tavoin jonkin tuotteen varastotilanne muuttuu: onko varasto usein hyvin vajaa, ollaanko usein ylärajalla, onko vaihelu suurta vai pientä, jne. Varustetaan siksi Tuotevarasto
-luokka taidolla muistaa tuotteen määrän muutoshistoriaa.
Aloitetaan apuvälineen laadinnalla.
Muutoshistorian muistamisen voisi toki toteuttaa suoraankin ArrayList<Double>
-oliona luokassa Tuotevarasto, mutta nyt laaditaan kuitenkin oma erikoistettu väline tähän tarkoitukseen. Väline toteutetaan kapseloimalla ArrayList<Double>
-olio.
Muutoshistoria
-luokan julkiset konstruktorit ja metodit:
-
public Muutoshistoria() luo tyhjän
Muutoshistoria
-olion. - public void lisaa(double tilanne) lisää muutoshistorian viimeisimmäksi muistettavaksi määräksi parametrina annetun tilanteen.
- public void nollaa() tyhjää muistin.
- public String toString() palauttaa muutoshistorian merkkijonoesityksen. ArrayList-luokan antama merkkijonoesitys kelpaa sellaisenaan.
Muutoshistoria.java, vaihe 2
Täydennä Muutoshistoria
-luokkaa analyysimetodein:
- public double maxArvo() palauttaa muutoshistorian suurimman arvon. Jos historia on tyhjä, metodi palauttaa nollan.
- public double minArvo() palauttaa muutoshistorian pienimmän arvon. Jos historia on tyhjä, metodi palauttaa nollan.
- public double keskiarvo() palauttaa muutoshistorian arvojen keskiarvon. Jos historia on tyhjä, metodi palauttaa nollan.
Muutoshistoria.java, vaihe 3
Täydennä Muutoshistoria
-luokkaa analyysimetodein:
- public double suurinMuutos() palauttaa muutoshistorian isoimman (huom: -5:n kokoinen muutos on isompi kuin 4:n kokoinen muutos) yksittäisen muutoksen itseisarvon. Jos historia on tyhjä tai yhden arvon mittainen, metodi palauttaa nollan. Itseisarvo on luvun etäisyys nollasta. Esimerkiksi luvun -5.5 itseisarvo on 5.5, luvun 3.2 itseisarvo on 3.2.
- public double varianssi() palauttaa muutoshistorian arvojen varianssin (käytetään otosvarianssin kaavaa). Jos historia on tyhjä tai yhden arvon mittainen, metodi palauttaa nollan.
Ohjeen varianssin laskemiseksi voit katsoa esimerkiksi Wikipediasta kohdasta populaatio- ja otosvarianssi. Esimerkiksi lukujen 3, 2, 7, 2 keskiarvo on 3.5, joten otosvarianssi on ((3 - 3.5)² + (2 - 3.5)² + (7 - 3.5)² + (2 - 3.5)²)/(4 - 1) ≈ 5,666667.)
MuistavaTuotevarasto, vaihe 1
Toteuta luokan Tuotevarasto
aliluokkana MuistavaTuotevarasto
. Uusi versio tarjoaa vanhojen lisäksi varastotilanteen muutoshistoriaan liittyviä palveluita. Historiaa hallitaan Muutoshistoria
-oliolla.
Julkiset konstruktorit ja metodit:
- public MuistavaTuotevarasto(String tuotenimi, double tilavuus, double alkuSaldo) luo tuotevaraston. Tuotenimi, vetoisuus ja alkusaldo annetaan parametrina. Aseta alkusaldo sekä varaston alkusaldoksi että muutoshistorian ensimmäiseksi arvoksi.
- public String historia() palauttaa tuotehistorian tyyliin [0.0, 119.2, 21.2]. Käytä Muutoshistoria-olion merkkiesitystä sellaisenaan.
Huomaa että tässä esiversiossa historia ei vielä toimi kunnolla; nyt vasta vain aloitussaldo muistetaan.
Käyttöesimerkki:
// tuttuun tapaan:
MuistavaTuotevarasto mehu = new MuistavaTuotevarasto("Juice", 1000.0, 1000.0);
mehu.otaVarastosta(11.3);
System.out.println(mehu.getNimi()); // Juice
mehu.lisaaVarastoon(1.0);
System.out.println(mehu); // Juice: saldo = 989.7, vielä tilaa 10.3
...
// mutta vielä historia() ei toimi kunnolla:
System.out.println(mehu.historia()); // [1000.0]
// saadaan siis vasta konstruktorin asettama historian alkupiste...
...
Tulostus siis:
Juice Juice: saldo = 989.7, vielä tilaa 10.299999999999955 [1000.0]
MuistavaTuotevarasto, vaihe 2
On aika aloittaa historia! Ensimmäinen versio ei historiasta tiennyt kuin alkupisteen. Täydennä luokkaa metodein
- public void lisaaVarastoon(double maara) toimii kuin Varasto-luokan metodi, mutta muuttunut tilanne kirjataan historiaan. Huom: historiaan tulee kirjata lisäyksen jälkeinen varastosaldo, ei lisättävää määrää!
-
public double otaVarastosta(double maara) toimii kuin
Varasto
-luokan metodi, mutta muuttunut tilanne kirjataan historiaan. Huom: historiaan tulee kirjata poiston jälkeinen varastosaldo, ei poistettavaa määrää!
Käyttöesimerkki:
// tuttuun tapaan:
MuistavaTuotevarasto mehu = new MuistavaTuotevarasto("Juice", 1000.0, 1000.0);
mehu.otaVarastosta(11.3);
System.out.println(mehu.getNimi()); // Juice
mehu.lisaaVarastoon(1.0);
System.out.println(mehu); // Juice: saldo = 989.7, vielä tilaa 10.3
...
// mutta nyt on historiaakin:
System.out.println(mehu.historia()); // [1000.0, 988.7, 989.7]
...
Tulostus siis:
Juice Juice: saldo = 989.7, vielä tilaa 10.299999999999955 [1000.0, 988.7, 989.7]
Muista miten korvaava metodi voi käyttää hyväkseen korvattua metodia!
MuistavaTuotevarasto, vaihe 3
Täydennä luokkaa metodilla
- public void tulostaAnalyysi(), joka tulostaa tuotteeseen liittyviä historiatietoja esimerkin esittämään tapaan.
Käyttöesimerkki:
MuistavaTuotevarasto mehu = new MuistavaTuotevarasto("Juice", 1000.0, 1000.0);
mehu.otaVarastosta(11.3);
mehu.lisaaVarastoon(1.0);
//System.out.println(mehu.historia()); // [1000.0, 988.7, 989.7]
mehu.tulostaAnalyysi();
Metodi tulostaAnalyysi kirjoittaa ilmoituksen tyyliin:
Tuote: Juice Historia: [1000.0, 988.7, 989.7] Suurin tuotemäärä: 1000.0 Pienin tuotemäärä: 988.7 Keskiarvo: 992.8
MuistavaTuotevarasto, vaihe 4
Täydennä analyysin tulostus sellaiseksi, että mukana ovat myös muutoshistorian suurin muutos ja historian varianssi.
Tehtävässä on tarkoitus toteuttaa yksinkertainen laskin. Laskimen käyttöliittymän tulee tarjota kaksi tekstikenttää sekä napit +
, -
sekä Z
.
Tehtävässä ei ole testejä. Palauta se vasta kun sovelluksessa on kaikki kohdat toteutettuna.
Layout kuntoon
Toteuta laskimen käyttöliittymä siten, että asettelijana on GridPane, jossa on kolme riviä. Ensimmäisellä kahdella rivillä on tekstikenttä, ja kolmannella rivillä on uusi GridPane, joka sisältää kolme saraketta. Ensimmäisessä sarakkeessa on +
-nappi, toisessa sarakkeessa on -
-nappi, ja kolmannessa sarakkeessa on Z
-nappi.
Ylempi tekstikentistä toimii "tuloskenttänä", johon käyttäjä ei saa syöttää numeroita. Alempi toimii kenttänä, johon käyttäjä saa syöttää numeroita. Tuloskentässä on aluksi numero 0 ja syötekenttä on tyhjä.
Perustoiminnallisuus
Laskimen toimintalogiikka on seuraava. Käyttäjän kirjoittaessa syötekenttään luvun n
ja painaessa +, lisätään tuloskentässä olevaan arvoon n
ja päivitetään tuloskenttä uuteen arvoon. Vastaavasti käyttäjän kirjoittaessa syötekenttään luvun n
ja painaessa -, vähennetään tuloskentässä olevasta arvosta n
ja päivitetään tuloskenttä uuteen arvoon. Jos käyttäjä painaa Z, nollautuu tuloskenttä.
Vihje: joskus on tarkoituksenmukaista hoitaa yhdellä tapahtumankuuntelijalla usean napin painallusten käsittely.
Hienosäätö
Laajennetaan vielä ohjelmaa seuraavilla ominaisuuksilla:
- Jos tuloskentässä on 0, ei Z-nappia voi painaa, eli se tulee olla asetettu "pois päältä". Muissa tilanteissa napin tulee olla päällä.
- Kun käyttäjä painaa jotain napeista +, -, Z syötekenttä tyhjenee.
- Jos syötekentässä oleva syöte ei ole kokonaisluku ja käyttäjä painaa jotain napeista+, -, Z syötekenttä tyhjenee ja tuloskentän tila ei muutu (paitsi napin ollessa Z).