Tehtävissä käytetty versio OmaString-luokasta: public class OmaString { public static final int MAKSIMIPITUUS = 100; protected char [] mjono; // merkkijononon esitys char-taulukkona private int pituus; // montako alkioita mjono:n alusta on käytössä //----------------------------------------------------------------------- //konstruktorit: //----------------------------------------------------------------------- /* * luo tyhjää merkkijonoa esittävän OmaString-olion * Huom: taulukon koko on aina 100 (MAKSIMIPITUUS); */ public OmaString() { this.mjono = new char[MAKSIMIPITUUS]; this.pituus = 0; } /* * luo OmaString-olion annetusta String-oliosta * Esim: Jos parametri on "kissa vie", taulukkoa täytetään * alusta alkaen merkki merkiltä ja pituus-kenttä saa arvon 9. */ public OmaString(String s) { this.mjono = new char[MAKSIMIPITUUS]; this.pituus = s.length(); // Korkeintaan MAKSIMIPITUUS kappaletta merkkejä otetaan mukaan: if(this.pituus > MAKSIMIPITUUS) this.pituus = MAKSIMIPITUUS; for(int i=0; i < this.pituus; i++) this.mjono[i] = s.charAt(i); } /* * luo aidon kopion parametrina annetusta OmaString-oliosta, * myös taulukko kopioidaan */ public OmaString(OmaString m) { this.mjono = new char[MAKSIMIPITUUS]; this.pituus = m.length(); for(int i=0; i this.pituus) return false; if(this.pituus == MAKSIMIPITUUS) return false; // Siirretään loppua eteenpäin for (int i=this.pituus; i>ind; i--) this.mjono[i] = this.mjono[i-1]; // lisätään uusi merkki this.mjono[ind] = merkki; this.pituus++; return true; } /* * poistaa OmaString-merkkijonosta merkin kohdasta ind. Jos ind on liian * suuri tai liian pieni, mikään ei muutu. Metodi palauttaa arvon true, * jos poisto onnistui, muuten false. */ public boolean delete(int ind) { // indeksi kunnossa? if((ind > this.pituus-1) || (ind < 0)) return false; // siirretään merkkejä alkuun päin for(int i = ind; i < this.pituus-1; i++) this.mjono[i] = this.mjono[i+1]; this.pituus--; return true; } /* * palauttaa arvonaan OmaString-merkkijonoa vastaavan String-olion */ public String toString() { String palautus = new String(this.mjono); palautus = palautus.substring(0, this.pituus); return palautus; } } //========================== 16 ====================================== import java.util.Scanner; public class OmaStringPlus extends OmaString implements Vertailtava { public OmaStringPlus() { super(); } public OmaStringPlus(String s) { super(s); } public OmaStringPlus(OmaString m) { super(m); } /* Vertaa this-OmaStringiä parametrina annettuun OmaStringiin. * palauttaa arvon -1, 0 tai 1 * (palauttaa myös 1, jos parametri on null) */ public int compareTo(OmaStringPlus toinen) { // hoidellaan virhetilanne palauttamalla 1 (hmmm ja hmm!) if (toinen == null) return 1; int lyhyempi; if (this.length() < toinen.length()) lyhyempi = this.length(); else lyhyempi = toinen.length(); // vertaillaan kaikkia merkkejä lyhyemmän vertailtavan pituuteen asti for (int n = 0; n < lyhyempi; n++) { if (this.charAt(n) < toinen.charAt(n)) return -1; else if (this.charAt(n) > toinen.charAt(n)) return 1; } // lyhyempi oli pidemmän alkuosa vai olivatko samat? if (this.length() < toinen.length()) return -1; else if (this.length() > toinen.length()) return 1; else return 0; } /* * Tutkii this-OmaStringin ja parametrina annetun OmaStringin samuutta: * * true jos samat, muuten false */ public boolean equals(OmaStringPlus toinen) { return this.compareTo(toinen) == 0; } /* palauttaa merkin annetusta indeksistä * (jos indeksi on virheellinen, palauttaa '\u0000'-merkin) */ public char charAt(int ind) { if (ind < 0 || ind >= this.length()) return '\u0000'; return this.mjono[ind]; } /* Etsii merkkiä OmaString-oliosta * palauttaa ensimmäisen löytyneen merkin indeksin * tai -1, jos ei löytynyt */ public int indexOf(char merkki) { for (int n = 0; n < this.length(); n++) if (this.charAt(n) == merkki) return n; // ei löytynyt! return -1; } /* Etsii OmaString-oliota OmaString-oliosta * palauttaa ensimmäisen löytyneen olion aloitusindeksin * tai -1, jos ei löytynyt tai parametri oli null */ public int indexOf(OmaStringPlus haettava) { if (haettava == null) return -1; // katsotaan joka merkin kohdalla voisiko etsitty alkaa tästä for (int i = 0; i < this.length() - haettava.length() + 1; i++) { // etene jos merkit ovat samoja, aina merkki.length()-arvoon saakka int j = 0; while (j < haettava.length() && this.charAt(i + j) == haettava.charAt(j)) j++; // kaikki merkit mätsäsivat, siis löytyi ja alkaen i:stä! if (j == haettava.length()) return i; } // ei löytynyt, nyyh return -1; } /** * Rajapinnan Vertailtava vaatimat vertailuaksessorit pienempi, isompi, * yhtäsuuri. Nämä kaikki aiheuttavat poikkeuksen * ClassCastException jos yritetään verrata OmaString-oliota johonkin muuhun * kuin toiseen OmaString-olioon. */ public boolean pienempi(Vertailtava toinen) { // this-olio < toinen-olio return (this.compareTo((OmaStringPlus) toinen) == -1); } public boolean isompi(Vertailtava toinen) { // this-olio > toinen-olio return (this.compareTo((OmaStringPlus) toinen) == 1); } public boolean yhtäSuuri(Vertailtava toinen) { // this-olio = toinen-olio return (this.compareTo((OmaStringPlus) toinen) == 0); } public static void main(String[] args) { Scanner lukija = new Scanner(System.in); System.out.print(kehystä("Anna jokin teksti tutkittavaksi:") + "\n>"); String apu = lukija.nextLine(); String historia[] = new String[100]; OmaStringPlus oma = new OmaStringPlus(apu); int valinta = 0; int indeksi, histIndeksi=0; historia[histIndeksi] = oma.toString(); char vanha, uusi; do { System.out.println("------------------------"); System.out.println("->" + oma + "<-"); System.out.println("------------------------"); System.out.println("Mitä haluat tehdä?"); System.out.println("0. Lopetus!"); System.out.println("1. Pituus?"); System.out.println("2. Korvaa merkki"); System.out.println("3. Poista merkki"); System.out.println("4. Lisää merkki"); //compareTo, equals, charAt, indexOf(char), indexOf(OmaString): System.out.println("5. Vertaa merkkijonoa toiseen"); System.out.println("6. Etsi merkkiä"); System.out.println("7. Etsi merkkijonoa"); System.out.println("8. Näytä muutoksien historia"); System.out.println("9. Uusi merkkijono!"); System.out.println("------------------------"); System.out.print(">"); valinta = lukija.nextInt(); if(valinta == 1) { System.out.println(kehystä("Merkkijonon pituus on: " + oma.length())); } if(valinta == 2) { histIndeksi++; System.out.println(kehystä("Anna korvattava merkki:")); System.out.print(">"); vanha = lukija.next().charAt(0); System.out.println(kehystä("Anna korvaava merkki:")); System.out.print(">"); uusi = lukija.next().charAt(0); oma.replace(vanha, uusi); historia[histIndeksi] = oma.toString(); } if(valinta == 3) { histIndeksi++; System.out.println(kehystä("Anna poistettavan indeksi:")); System.out.print(">"); indeksi = lukija.nextInt(); if(!oma.delete(indeksi)) { System.out.println(kehystä("Virheellinen indeksi!")); histIndeksi--; } historia[histIndeksi] = oma.toString(); } if(valinta == 4) { histIndeksi++; System.out.println(kehystä("Anna lisättävä merkki:")); System.out.print(">"); uusi = lukija.next().charAt(0); System.out.println(kehystä("Anna lisättävän indeksi:")); System.out.print(">"); indeksi = lukija.nextInt(); if(!oma.insert(indeksi, uusi)) { System.out.println(kehystä("Virheellinen indeksi!")); histIndeksi--; } historia[histIndeksi] = oma.toString(); } if(valinta == 5) { System.out.print(kehystä("Anna verrattava merkkijono:") + "\n>"); // Jos näin ei tee, Scanner-olioon jää jumittamaan rivinvaihto // seuraavan next...()-kutsun kiusaksi lukija.nextLine(); // nyt pitäisi olla muuta luettavaa kuin rivinvaihto apu = lukija.nextLine(); OmaStringPlus toinen = new OmaStringPlus(apu); int tulos = oma.compareTo(toinen); if (tulos == -1) System.out.println(kehystä("Alkuperäinen merkkijono on pienempi.")); else if (tulos == 1) System.out.println(kehystä("Alkuperäinen merkkijono on suurempi.")); else System.out.println(kehystä("Merkkijonot ovat samat.")); } if(valinta == 6) { System.out.print(kehystä("Anna etsittävä merkki:") + "\n>"); char merkki = lukija.next().charAt(0); indeksi = oma.indexOf(merkki); if (indeksi != -1) System.out.println(kehystä("Löytyi indeksillä " + indeksi)); else System.out.println(kehystä("Eipä löytynyt, nyyh :/")); } if(valinta == 7) { System.out.print(kehystä("Anna esittävä merkkijono:") + "\n>"); // ks. yltä miksi pitää lukea tämä rivi lukija.nextLine(); apu = lukija.nextLine(); OmaStringPlus merkki = new OmaStringPlus(apu); indeksi = oma.indexOf(merkki); if (indeksi != -1) System.out.println(kehystä("Löytyi alkaen indeksistä " + indeksi)); else System.out.println(kehystä("Eipä löytynyt, nyyh :/")); } if(valinta == 8) { System.out.println(kehystä("Historia:")); for(int i=0; i <= histIndeksi; i++) System.out.println(i + " " +historia[i]); System.out.println("------------------------"); } if(valinta == 9) { System.out.print(kehystä("Anna merkkijono tutkittavaksi:") + "\n>"); lukija.nextLine(); apu = lukija.nextLine(); OmaStringPlus newString = new OmaStringPlus(apu); oma = newString; histIndeksi=0; historia[histIndeksi] = oma.toString(); } } while(valinta !=0); } //---------------------------------------------- // Kehystää merkkijonon (käyttöliittymää varten) //---------------------------------------------- private static String kehystä(String s) { String apu = ""; for(int i=0; i < s.length(); i++) apu +="-"; apu += "\n" + s + "\n"; for(int i=0; i < s.length(); i++) apu +="-"; return apu; } } //========================== 17 ====================================== /** * Ohjelmoinnin jatkokurssi, syksy 2008, 5. harjoitukset, tehtävä 19 * Numerojono-luokka. * * @author Juhana Laurinharju * @author Janne Salo * @author Sami Nikander */ import java.util.Scanner; public class Numerojono extends OmaStringPlus { /** * Luo tyhjän numerojonon. */ private Numerojono() { // Kutsutaan yliluokan parametritonta konstruktoria, // joka vain luo 0-pituisen OmaString-olion. super(); } // ----------------------------------------------------------------------- // Aksessorit, joiden kannalta on yhdentekevää, onko kyseessä numerojono // vai mikä tahansa merkkijono, peritään sellaisinaan: // length(), toString(), compareTo(), // equals(OmaString toinen), charAt(int index), // indexOf(char merkki), indexOf(OmaString haettava) // ja delete(int index) // // Aksessoreihin, joilla numerojonoon tuodaan uusia merkkejä, // lisätään tarkistus, joka pitää huolen siitä, että jonoon // päätyy vain numeromerkkejä. Korvataan siis aksessorit: // replace(char vanha, char uusi) ja // insert(int index, char character) // ----------------------------------------------------------------------- /** * Korvaa this-olion kaikki vanha-merkit numeerisella * uusi-merkillä. Metodi käyttää yliluokan toteutusta korvaamiseen, * mutta tarkistaa korvaavan merkin numeerisuuden ennen yliluokan * replace-metodin kutsumista. */ public void replace(char vanha, char uusi) { if (Character.isDigit(uusi)) // on numero super.replace(vanha, uusi); } // ----------------------------------------------------------------------- /** * Asettaa Numerojono-olioon parametrina annetun merkin, mikäli indeksi * on kelvollinen, tilaa on jäljellä ja merkki on numero. * Metodi käyttää yliluokan toteutusta korvaamiseen, mutta tarkistaa * lisättävän merkin numeerisuuden ennen yliluokan insert-metodin kutsumista. * Indeksin ja mahtumisen tarkistuksissa luotetaan yliluokan metodeihin. * Palauttaa arvon true, jos lisäys onnistui, muuten false. */ public boolean insert(int ind, char merkki) { if (Character.isDigit(merkki)) return super.insert(ind, merkki); else return false; } // ----------------------------------------------------------------------- /** * Palauttaa kokonaislukuesityksen this-oliosta. * Jos numerojono on tyhjä, palauttaa -1. * Numerojono voi esittää vain ei-negatiivista lukua, jolloin * paluuarvoa -1 voidaan käyttää virhetilanteen esittämiseen. * HUOM: Metodi kaatuu poikkeukseen NumberFormatException, * jos jonon sisältö ei mahdu int-lukutyyppiin! */ public int value() { if (this.length() > 0) return Integer.parseInt(this.toString()); else return -1; } // ----------------------------------------------------------------------- // HUOM! Tätä ei vaadittu tehtävänannossa! Metodi on apuaksessori, // joka helpottaa numerosarjojen syöttämistä // Numerojono-olioden arvoksi. /** * Asettaa this-olion arvoksi parametrina annetun merkkijonon. * Syötteen numeerisuus tarkistetaan merkki kerrallaan. * Operaatiota ei tehdä ollenkaan jos merkkijonossa on yksikin * merkki joka ei ole numero tai jos merkkijono on liian pitkä * lisättäväksi. Mahtumisessa luotetaan insert-metodin toteutukseen * (joka puolestaan luottaa siinä yliluokan toteutukseen). * Palauttaa true, jos koko syöte oli kelvollinen, false jos * vastaan tuli epäkelpo merkki tai syöte oli liian pitkä. */ public boolean setValue(String s) { /* * On suotavaa, että tarkistukset tehdään ennen kuin mitään lisätään * Numerojonoon. Jos tarkistuksia tehtäisiin lisäyksen yhteydessä, * niin metodin palauttaessa false kutsuja tietäisi vain, että jotain * meni jossain kohtaa pieleen, muttei tietäisi missä kohtaa. Nyt * kutsujalle kerrotaan heti jos homma ei tule onnistumaan jolloin * kutsuja voi reagoida asiaan paremmin. */ char[] staulu = s.toCharArray(); for (int i = 0; i < staulu.length; i++) { if (!Character.isDigit(staulu[i])) return false; } if (length() + staulu.length > OmaString.MAKSIMIPITUUS) return false; // kaikki numerot eivät mahdu for (int i = 0; i < staulu.length; i++) { insert(i, staulu[i]); } return true; } // ----------------------------------------------------------------------- /** * Pääohjelma esittelee Numerojonon käyttöä. */ public static void main(String[] args) { Scanner lukija = new Scanner(System.in); Numerojono oma = new Numerojono(); String historia[] = new String[OmaString.MAKSIMIPITUUS]; int valinta = 0; int indeksi = 0; int histIndeksi = 0; historia[histIndeksi] = oma.toString(); char vanha, uusi; final int LOPETUS = 0, ARVO = 1, KORVAA = 2, POISTA = 3, LISÄÄ = 4, VERTAA = 5, ETSI_MERKKIÄ = 6, ETSI_NUMEROJONOA = 7, HISTORIA = 8, UUSI = 9; kehota("Anna jokin numerojono tutkittavaksi:"); String apu = lukija.nextLine(); oma.setValue(apu); do { kehystä("->" + oma + "<- (pituus " + oma.length() + ")"); tulostaValikko(); System.out.print(">"); valinta = lukija.nextInt(); if(valinta == ARVO) { // Tässä vähän hassusti tehdään ensin muunnos // String -> int ja heti perään takaisin int -> String... kehystä("Numerojono lukuarvona: " + oma.value()); } if(valinta == KORVAA) { histIndeksi++; kehota("Anna korvattava merkki:"); vanha = lukija.next().charAt(0); kehota("Anna korvaava merkki:"); uusi = lukija.next().charAt(0); oma.replace(vanha, uusi); historia[histIndeksi] = oma.toString(); } if(valinta == POISTA) { histIndeksi++; kehota("Anna poistettavan indeksi:"); indeksi = lukija.nextInt(); if(!oma.delete(indeksi)) { kehystä("Virheellinen indeksi!"); histIndeksi--; } historia[histIndeksi] = oma.toString(); } if(valinta == LISÄÄ) { histIndeksi++; kehota("Anna lisättävä merkki:"); uusi = lukija.next().charAt(0); kehota("Anna lisättävän indeksi:"); indeksi = lukija.nextInt(); if(!oma.insert(indeksi, uusi)) { kehystä("Virheellinen indeksi tai arvo!"); histIndeksi--; } historia[histIndeksi] = oma.toString(); } if(valinta == VERTAA) { kehota("Anna verrattava numerojono:"); apu = lukija.next(); Numerojono toinen = new Numerojono(); toinen.setValue(apu); int tulos = oma.compareTo(toinen); if (tulos == -1) kehystä("Alkuperäinen numerojono on pienempi."); else if (tulos == 1) kehystä("Alkuperäinen numerojono on suurempi."); else kehystä("numerojonot ovat samat."); } if(valinta == ETSI_MERKKIÄ) { kehota("Anna etsittävä numerojono:"); char merkki = lukija.next().charAt(0); indeksi = oma.indexOf(merkki); if (indeksi != -1) kehystä("Löytyi indeksillä " + indeksi); else kehystä("Eipä löytynyt, nyyh :/"); } if(valinta == ETSI_NUMEROJONOA) { kehota("Anna etsittävä numerojono:"); apu = lukija.next(); Numerojono etsittava = new Numerojono(); if (etsittava.setValue(apu)) { indeksi = oma.indexOf(etsittava); if (indeksi != -1) kehystä("Löytyi alkaen indeksistä " + indeksi); else kehystä("Eipä löytynyt, nyyh :/"); } else { kehystä("Et antanut numerojonoa!"); } } if(valinta == HISTORIA) { kehystä("Historia:"); for(int i=0; i <= histIndeksi; i++) System.out.println(i + " " +historia[i]); System.out.println("------------------------"); } if(valinta == UUSI) { kehota("Anna numerojono tutkittavaksi:"); apu = lukija.next(); oma = new Numerojono(); oma.setValue(apu); histIndeksi = 0; historia[histIndeksi] = oma.toString(); } } while(valinta != LOPETUS); } // // Pääohjelman pikku apulaisia. // // Kehystää ja tulostaa merkkijonon (käyttöliittymää varten) private static void kehystä(String s) { String kehys = ""; for(int i=0; i < s.length(); i++) kehys += "-"; System.out.println(kehys + "\n" + s + "\n" + kehys); } // Tulostaa kehysten lisäksi komentokehotteen '>' private static void kehota(String s) { kehystä(s); System.out.print(">"); } private static void tulostaValikko() { System.out.println("Mitä haluat tehdä?"); System.out.println("0. Lopetus!"); System.out.println("1. Jonon arvo lukuna?"); System.out.println("2. Korvaa merkki"); System.out.println("3. Poista merkki"); System.out.println("4. Lisää merkki"); //compareTo, equals, charAt, indexOf(char), indexOf(OmaString): System.out.println("5. Vertaa numerojonoa toiseen"); System.out.println("6. Etsi merkkiä"); System.out.println("7. Etsi numerojonoa"); System.out.println("8. Näytä muutoksien historia"); System.out.println("9. Uusi numerojono!"); System.out.println("------------------------"); } } //========================== 18 ====================================== /** * Ohjelmoinnin jatkokurssi, syksy 2008, 5. harjoitukset, tehtävä 18 * * Abstrakti luokka erilaisten eläinten yhteisten toimintojen mallintamiseen. * */ public abstract class Elain { protected double paino; protected String nimi; /** * Tulostaa eläimen ääntelyä. Abstrakti metodi: lajikohtainen toteutus on * pakko antaa aliluokassa. Tämän olemassaolo estää ilmentymien luonnin * Elain-luokasta. */ public abstract void ääntele(); /** * Antaa eläimelle ruokaa parametriarvon verran. Eläimen paino kasvaa * ruokamäärän verran, lisäys kuitenkin korkeintaan 50% nykypainosta. * Loppu ruoka jää syömättä, syödyn annoksen koko annetaan paluuarvona. */ public double syö(double määrä) { if (määrä <= 0.0) return 0.0; if (määrä > this.paino / 2) määrä = this.paino / 2; this.paino += määrä; return määrä; } public double annaPaino() { return paino; } /** * Asettaa eläimen painon. Jos parametrina annettu paino ei ole positiivinen * luku, ei tee mitään. Palauttaa true, jos paino asetettiin onnistuneesti, * muuten false. */ public boolean asetaPaino(double paino) { if (paino > 0) { this.paino = paino; return true; } else { return false; } } /** * Antaa eläimelle ruokaa oletusannoksen verran. Oletusannos on 1.0 kg. */ public double syö() { return this.syö(1.0); } /** * Suorittaa ruoansulatuksen jälkikäsittelyvaiheen. Eläimen paino vähenee * 5% ja samalla eläin tuottaa saman määrän lantaa (5% painosta). */ public double ulosta() { double lanta = this.paino * 0.05; this.paino -= lanta; return lanta; } /** * Palauttaa havainnollisen String-esityksen eläimestä. */ public String toString() { return this.nimi + ": Paino " + this.paino + " kg."; } } /** * Hevosta kuvaava Elain-luokan aliluokka. */ class Hevonen extends Elain { private static final int OLETUSPAINO = 50; private static final String OLETUSNIMI = "Polle"; public Hevonen() { this(OLETUSNIMI); } public Hevonen(String nimi) { this(OLETUSPAINO, nimi); } public Hevonen(int paino, String nimi) { // jos outo paino, luodaan oletuspainoinen if (paino < 30 || paino > 1500) this.paino = OLETUSPAINO; else this.paino = paino; if (nimi != null) this.nimi = nimi; else this.nimi = OLETUSNIMI; } /** * Tulostaa hevosen ääntelyä, implementoi Elain-luokan abstraktin metodin. */ public void ääntele() { if (Math.random() < 0.5) System.out.println("Ii-haha-haa"); else System.out.println("Hirnauskis!"); } /** Hevonen hörisee. */ public void hörise() { System.out.println("Hörhör"); } /** Ruokkii hevosta sopivan annoksen verran. */ public double syö() { if (Math.random() < 0.025) this.hörise(); // joskus heppa hörisee kun saa ruokaa return super.syö(3.0); } /** Luo havainnollisen String-esityksen hevosesta. */ public String toString() { return "Hevonen: " + super.toString(); } } /** * Kissaa kuvaava Elain-luokan aliluokka */ class Kissa extends Elain { // kissan maksimipaino private final static double MAXPAINO = 20.0; private final static String OLETUSNIMI = "Miuku"; public Kissa() { this(OLETUSNIMI); } public Kissa(String nimi) { this(0.1, nimi); } public Kissa(double paino, String nimi) { // jos outo paino luodaan oletuspainoinen if (paino < 0.1 || paino > MAXPAINO) this.paino = 0.1; else this.paino = paino; if (nimi != null) this.nimi = nimi; else this.nimi = OLETUSNIMI; } /* * Tulostaa kissan ääntelyä, implementoi Elain-luokan abstraktin metodin */ public void ääntele() { System.out.println("Miau"); if (Math.random() > 0.25) // Joskus kissa kehrääkin this.kehrää(); } public void kehrää() { System.out.println("Purrrrrrrr."); } /** Ruokkii kissaa kissalle sopivan annoksen verran. */ public double syö() { if (Math.random() < 0.05) // Joskus kissa kehrää saadessaan ruokaa this.kehrää(); return super.syö(0.5); } /** Luo havainnollisen String-esityksen kissasta. */ public String toString() { return "Kissa: " + super.toString(); } } /** * Nautaa kuvaava Elain-luokan aliluokka */ class Nauta extends Elain { private static final int OLETUSPAINO = 50; private static final String OLETUSNIMI = "Mansikki"; public Nauta() { this(OLETUSNIMI); } public Nauta(String nimi) { this(OLETUSPAINO, nimi); } public Nauta(int paino, String nimi) { // jos outo paino luodaan oletuspainoinen if (paino < 50 || paino > 1500) this.paino = OLETUSPAINO; else this.paino = paino; if (nimi != null) this.nimi = nimi; else this.nimi = OLETUSNIMI; } /** * Tulostaa naudan ääntelyä, implementoi Elain-luokan abstraktin metodin. */ public void ääntele() { System.out.println("Ammuuu"); } /** Ruokkii nautaa sopivan annoksen verran. */ public double syö() { return super.syö(3); } /** * Suorittaa ruoansulatuksen jälkikäsittelyvaiheen. Naudan paino vähenee * 1% ja lantaa syntyy sama määrä. */ public double ulosta() { double lanta = this.paino * 0.01; this.paino -= lanta; return lanta; } /** Luo havainnollisen String-esityksen naudasta. */ public String toString() { return "Nauta: " + super.toString(); } } /** * Lehmää eli täysikasvuista naarasnautaa kuvaava luokka. */ class Lehma extends Nauta implements Lypsava { public Lehma() { super(); } public Lehma(String nimi) { super(nimi); } public Lehma(int paino, String nimi) { super(paino, nimi); } /** * Tuottaa maitoa 5% lehmän painosta. Lehmän paino vähenee saman verran. * Metodi toteuttaa rajapintaluokassa määritellyn lypsä-metodin. */ public double lypsä() { double maitoa = this.paino * 0.05; this.paino -= maitoa; return maitoa; } /** Luo havainnollisen String-esityksen lehmästä. */ public String toString() { return "Lehmä+" + super.toString(); } } /** * Tammaa eli naarashevosta kuvaava luokka. */ class Tamma extends Hevonen implements Lypsava { public Tamma() { super(); } public Tamma(String nimi) { super(nimi); } public Tamma(int paino, String nimi) { super(paino, nimi); } /** * Tuottaa maitoa 1% tamman painosta. Tamman paino vähenee saman verran. * Tamma tuottaa vähemmän maitoa kuin lehmä. * Metodi toteuttaa rajapintaluokassa määritellyn lypsä-metodin. */ public double lypsä() { double maitoa = this.paino * 0.01; this.paino -= maitoa; return maitoa; } /** Luo havainnollisen String-esityksen tammasta. */ public String toString() { return "Tamma+" + super.toString(); } } /** * Rajapinta lypsävien olioiden toteutettavaksi. */ public interface Lypsava { /** * Tuottaa maitoa ja vähentää painoa. */ public double lypsä(); } /** * Juustotehdas-luokka */ public class Juustotehdas { private Lypsava lypsettävä; public Juustotehdas(Lypsava tuotantoelain) { this.lypsettävä = tuotantoelain; } public double teeJuustoa(double määrätoive) { double saatuMäärä = 0.0; double viimeLypsy; do { viimeLypsy = lypsettävä.lypsä(); // ehkä ehtyy saatuMäärä += viimeLypsy; } while (saatuMäärä < määrätoive && viimeLypsy > 0.0001); return saatuMäärä; } } import java.util.Scanner; /** * Ohjelmoinnin jatkokurssi, syksy 2008, 5. harjoitukset, tehtävä 18 * * Ohjelma eläinluokkien ja juustotehtaan kokeilemiseen. */ public class Farmi { private static Scanner lukija = new Scanner(System.in); public static void main(String[] args) { Elain[] eläimet = new Elain[5]; // oletusdirektiivi tilakoolle System.out.println("Tervetuloa farmariksi. Määrittele 5 eläintä.\n" + "K: kissa\n" + "N: nauta\n" + "H: hevonen\n" + "L: lehmä\n" + "T: tamma"); String rivi, nimi; for (int i = 0; i < eläimet.length; i++) { System.out.println("Mitä lajia on eläin numero " + i + "? : "); rivi = lukija.nextLine(); System.out.println("Mikä on sen nimi? : "); nimi = lukija.nextLine(); // muutakin voisi kysellä, vaikkapa case-haaroissa switch (rivi.toUpperCase().charAt(0)) { case 'K': // kissa eläimet[i] = new Kissa(nimi); break; case 'N': // nauta eläimet[i] = new Nauta(nimi); break; case 'H': // hevonen eläimet[i] = new Hevonen(nimi); break; case 'L': // lehmä eläimet[i] = new Lehma(nimi); break; case 'T': // tamma, oletuseläin default: eläimet[i] = new Tamma(nimi); } } System.out.println("\nFarmi perustettu! Tässä tilannekatsaus:"); for (int i = 0; i < eläimet.length; i++) if (eläimet[i] != null) System.out.println(i + ": " + eläimet[i]); System.out.println("\nRuokinta-aika alkaa."); for (int i = 0; i < eläimet.length; i++) if (eläimet[i] != null) eläimet[i].syö(); System.out.println("\nRuokinta-aika päättyi! Uusi tilanne:"); for (int i = 0; i < eläimet.length; i++) if (eläimet[i] != null) { System.out.print(i + ": " + eläimet[i] + " - - "); eläimet[i].ääntele(); } System.out.println("\nOstettiinpa uusi ja tanakka lehmä."); Lehma heluna = new Lehma(1450, "Heluna"); System.out.println(heluna); Juustotehdas emmental = new Juustotehdas(heluna); int kierros = 0; while (emmental.teeJuustoa(5) > 4.99) kierros++; System.out.println("Kaikkensa antaneena siitä tuli " + kierros + " kappaletta viiden yksikön juustoja."); System.out.println("Moisen riiston seurauksena Heluna painaa enää "+heluna.annaPaino()+" kiloa"); // Havainnollistetaan vielä paremmin polymorfismia System.out .println("\nTunnustellaan sokeasti mansikkia ja karvista, katsotaan keitä ne ovat."); Elain geneerinen; Lehma mansikki = new Lehma("Mansikki"); Kissa karvinen = new Kissa("Karvinen"); geneerinen = mansikki; geneerinen.ääntele(); geneerinen = karvinen; geneerinen.ääntele(); System.out.println("\nKokeillaan onko Mansikki myös lypsettävä."); Juustotehdas edam = new Juustotehdas(mansikki); kierros = 0; while (edam.teeJuustoa(5) > 4.99) kierros++; System.out.println("Kaikkensa antaneena siitä tuli " + kierros + " kappaletta viiden yksikön juustoja."); } } //========================== 19 ja 20 ====================================== /** * Ohjelmoinnin jatkokurssi, syksy 2008, 5. harjoitukset * Tehtävän 20 rajapintaluokka */ public interface Vertailtava { public boolean pienempi(Vertailtava toinen); // this-olio < toinen-olio public boolean isompi(Vertailtava toinen); // this-olio > toinen-olio public boolean yhtäSuuri(Vertailtava toinen); // this-olio = toinen-olio } /** * Ohjelmoinnin jatkokurssi, syksy 2008, 5. harjoitukset, tehtävän 20 havainnollistusohjelma. * */ class Vertailu { public static void main(String[] args) { // esimerkki onnistuneesta järjestämisestä: // järjestetään omastring-oliot järjestykseen Vertailtava[] vertailtavat1 = new Vertailtava[7]; vertailtavat1[0] = new OmaStringPlus("aa"); vertailtavat1[1] = new OmaStringPlus("pee"); vertailtavat1[2] = new OmaStringPlus("cee"); vertailtavat1[3] = new OmaStringPlus("tee"); vertailtavat1[4] = new OmaStringPlus("äf"); vertailtavat1[5] = new OmaStringPlus("gee"); vertailtavat1[6] = new OmaStringPlus((OmaStringPlus) vertailtavat1[1]); tulostaJaJärjestä(vertailtavat1); // esimerkki onnistuneesta järjestämisestä: // järjestetään pikkuvarasto-oliot järjestykseen Vertailtava[] vertailtavat2 = new Vertailtava[7]; vertailtavat2[0] = new PikkuVarasto(10, "mehu"); vertailtavat2[1] = new PikkuVarasto(10, "olut"); vertailtavat2[2] = new PikkuVarasto(5, "olut"); vertailtavat2[3] = new PikkuVarasto(25, "mehu"); vertailtavat2[4] = new PikkuVarasto(10, "mehu"); vertailtavat2[5] = new PikkuVarasto(100, "energiajuoma"); vertailtavat2[6] = new PikkuVarasto(0, "mehu"); tulostaJaJärjestä(vertailtavat2); // esimerkki epäonnistuneesta järjestämisestä: // yritetään järjestää pikkuvarasto-olioita ja omastringejä keskenään // järjestä-metodi aiheuttaa ClassCastExceptionin Vertailtava[] vertailtavat3 = new Vertailtava[7]; vertailtavat3[0] = new PikkuVarasto(10, "mehu"); vertailtavat3[1] = new PikkuVarasto(10, "olut"); vertailtavat3[2] = new OmaStringPlus("cee"); vertailtavat3[3] = new PikkuVarasto(25, "mehu"); vertailtavat3[4] = new OmaStringPlus("äf"); vertailtavat3[5] = new OmaStringPlus("gee"); vertailtavat3[6] = new PikkuVarasto(0, "mehu"); System.out.println("Kohta räsähtää!"); tulostaJaJärjestä(vertailtavat3); } public static void järjestä(Vertailtava[] taulu) { // yksinkert. vaihtojärj. for (int i = 0; i < taulu.length - 1; ++i) for (int j = i + 1; j < taulu.length; ++j) // Vertailu aiheuttaa ClassCastExceptionin jos // taulun alkiot ovat eri tyyppiä if ((taulu[i]).isompi(taulu[j])) { Vertailtava apu = taulu[i]; taulu[i] = taulu[j]; taulu[j] = apu; } } public static void tulostaJaJärjestä(Vertailtava[] taulu) { System.out.println("ennen järjestämistä:\n------------------"); for (Vertailtava v : taulu) System.out.println(v); järjestä(taulu); System.out.println("järjestämisen jälkeen:\n------------------"); for (Vertailtava v : taulu) System.out.println(v); } } /* * * Metodin oikea käyttötapa on sellainen, jossa taulukkoon on viety vain * keskenään saman tyyppisiä olioita. Jos taulukossa on vaikkapa sekaisin * OmaStringPlus- ja PikkuVarasto-olioita, käy huonosti! Miksi? * * Koska OmaStringPlus että PikkuVarasto toteuttavat Vertailtava-rajapinnan * niiden ilmentymiä voidaan sijoittaa samaan taulukkoon. Kuitenkin niiden * vertaileminen keskenään aiheuttaa ajoaikaisen virheen (ClassCastException), * kun Vertailtava-oliolle yritetään tehdä eksplisiittistä tyyppimuunnosta, * koska pikkuvarasto ja omastring ovat eri tyyppiä * * Voisiko asialle tehdä jotakin? * * Periaatteessa voitaisiin tarkistaa vertailumetodin alussa, ovatko * vertailtavat oliot samaa tyyppiä ja palauttaa false jos näin ei ole. * * Esim: * if (toinen instanceof OmaStringPlus) * return false; * * (Object-luokasta löytyy myös metodi getClass, joka palauttaa tiedon olion ajonaikaisesta luokasta * Class-oliona. Class-oliolta voi puolestaan kysellä nimeä metodilla getName, eli voitaisiin tehdä esim. * seuraavasti: * if (!this.getClass().getName().equals(toinen.getClass().getName())) * return false; * * Tai jopa käyttää luokan nimien aakkosjärjestystä järjestämisperusteena! Tässä ei kuitenkaan * ole juurikaan järkeä, ks. perustelut alla) * * Tällöin järjestysalgoritmi järjestää eri tyyppejä sisältävän taulukon * johonkin epämääräiseen järjestykseen. Jossain erikoistapauksessa voi olla * hyödyllistä että erityyppiset oliot voidaan järjestää keskenään johonkin * järjestykseen, mutta yleisesti ottaen siinä ei ole mitään mieltä. * Näin ollen lieneekin parempi ettei asialle yritä tehdä mitään vaan * annetaan ajoaikaisen virheen tapahtua. Virhe on tällöin * käyttäjän omalla vastuulla samoin kuin taulukon alivuoto, ylivuoto tai * nollalla jako. */ /** * Ohjelmoinnin jatkokurssi, syksy 2008, 5. harjoitukset, tehtävän 20 muokattu * Pikkuvarasto-luokka. */ public class PikkuVarasto implements Vertailtava { // toteutuksen tietorakenteet: private double maara; // tuotteen määrä >= 0 private String tuote; // tuotteen nimi // ----- konstruktorit: ----------- public PikkuVarasto() { this.maara = 0.0; this.tuote = "(nimetön)"; } public PikkuVarasto(double maara, String tuote) { this.tuote = tuote; if (maara > 0) this.maara = maara; else this.maara = 0.0; } // ----- aksessorit: -------- public double paljonkoOn() { return this.maara; } public String mikaNimi() { return this.tuote; } public void vieVarastoon(double paljonko) { // vain >0 kelpaa if (paljonko > 0) this.maara += paljonko; } public double otaVarastosta(double paljonko) { // vain >0 kelpaa if (paljonko <= 0) return 0; if (paljonko <= this.maara) { // vähennetään varastosta this.maara -= paljonko; // koko parametri return paljonko; } else { // annetaan mitä voidaan! paljonko = this.maara; this.maara = 0; return paljonko; } } public String toString() { // varastotilanne merkkijonona return "(" + this.tuote + ": " + this.maara + ")"; } // ----- vertailuaksessorit: ----------- // Tehdään avuksi vastaavanlainen CompareTo-metodi, // jollainen oli OmaStringissäkin. /** * Vertailee this-oliota toiseen Pikkuvarastoon. Ensisijainen * järjestysperuste on pikkuvaraston nimi, toissijainen peruste * sisällön määrä. Palauttaa -1 jos this-olio edeltää toista * Pikkuvarastoa, 0 jos ne ovat identtiset, 1 muutoin. */ public int compareTo(PikkuVarasto toinen) { if (this.mikaNimi().compareTo(toinen.mikaNimi()) < 0) return -1; if (this.mikaNimi().compareTo(toinen.mikaNimi()) > 0) return 1; // Tähän päästään jos nimet ovat samat. Seuraavaksi vertaillaan // sisällön määrää. if (this.paljonkoOn() < toinen.paljonkoOn()) return -1; if (this.paljonkoOn() > toinen.paljonkoOn()) return 1; // Tähän päästään jos pikkuvarastoilla on sama nimi ja sisältö. return 0; } /** * Rajapinnan Vertailtava vaatimat vertailuaksessorit pienempi, isompi, * yhtäsuuri. Nämä kaikki aiheuttavat poikkeuksen ClassCastException jos * yritetään verrata pikkuvarastoa johonkin muuhun kuin toiseen * pikkuvarastoon. */ public boolean pienempi(Vertailtava toinen) { // this-olio < toinen-olio return (this.compareTo((PikkuVarasto) toinen) == -1); } public boolean isompi(Vertailtava toinen) { // this-olio > toinen-olio return (this.compareTo((PikkuVarasto) toinen) == 1); } public boolean yhtäSuuri(Vertailtava toinen) { // this-olio = toinen-olio return (this.compareTo((PikkuVarasto) toinen) == 0); } }