// Otetaan käyttöön olion kopiointiin tarvittavat tietovirrat // ja sarjallistamisvalmiuden osoittava rajapinta. import java.io.*; /* * Olio-ohjelmoinnin perusteet I. * * Laatikkoa mallintava luokka. Laatikko voi sisältää minkä tahansa * tyyppisen olion monimuotoisuuden ansiosta: attribuutin tyyppi on Object, * joka kanssa mikä tahansa luokkatyyppi on yhteensopiva. Laatikko voidaan * avata ja sulkea. * * Laatikko osaa muodostaa itsestään merkkijonoesityksen, vertailla itseään * toiseen laatikkoon sekä sarjallistaa ja kopioida itsensä. * * Jorma Laurikkala (jorma.laurikkala@tuni.fi). * * Viimeksi muutettu 11.2.2020. * */ public class Laatikko implements Serializable { // Viite laatikon sisältöön. private Object sisältö; // Tosi, jos laatikko on auki. private boolean auki; /* * Luodaan tyhjä avattu laatikko. Parametriton rakentaja tarvitaan, * jotta tämän luokan olion sarjallistaminen on mahdollista. */ public Laatikko() { sisältö = null; auki = true; } /* * Alustaa uuden laatikon sisällöksi uuden olion, jos olio on olemassa. * Heittää IllegalArgumentException-poikkeuksen, jos uusi olio on hukassa. * Laatikko voidaan määritellä avoimeksi tai suljetuksi. */ public Laatikko(Object uusiSisältö, boolean onAuki) throws IllegalArgumentException { sisältö(uusiSisältö); auki(onAuki); } /* * Palauttaa viitteen laatikon sisältämään olioon. */ public Object sisältö() { return sisältö; } /* * Asettaa laatikon sisällöksi uuden olion, jos se on olemassa. * Heittää IllegalArgumentException-poikkeuksen, jos uusi olio on hukassa. */ public void sisältö(Object uusiSisältö) throws IllegalArgumentException { if (uusiSisältö == null) { throw new IllegalArgumentException(); } sisältö = uusiSisältö; } public boolean auki() { return auki; } public void auki(boolean onAuki) { auki = onAuki; } /* * Korvataan siten, että paluuarvo kertoo mitä laatikossa on. */ @Override public String toString() { // Olion toString-metodia kutsutaan automaattisesti, kun olio yhdistetään // merkkijonoon. return "Laatikossa on \"" + sisältö + "\" ja laatikko on " + (auki ? "auki" : "kiinni") + "."; } /* * Vertailee tätä ja toista laatikkoa tämän laatikon sisällön equals-metodin avulla, * jolloin laatikot ovat samat, jos niiden sisällöt ovat samat. */ @Override public boolean equals(Object toinen) { try { // Asetetaan olioon Laatikko-tyyppinen viite, jotta voidaan kutsua // aksessoreita. Laatikko toinenLaatikko = (Laatikko)toinen; // Samuus päätellään laatikon sisällön osalta käyttämällä equals-metodia. // Toista attribuuttia voidaan vertailla operaattorilla, koska se on // alkeistyyppiä. Object-luokan equals-metodia ei kutsuta, koska se // vertailee viitteitä. boolean samaSisältö = sisältö.equals(toinenLaatikko.sisältö()); boolean samoinAuki = auki == toinenLaatikko.auki(); return samaSisältö && samoinAuki; } catch (Exception e) { return false; } } /* * Muodostetaan hajautuskoodi kääreluokkien metodien avulla Joshua Blochin * menetelmää käyttäen ja sisällön null-arvo nollakoodina huomioiden. */ @Override public int hashCode() { int tulos = 17; tulos = 31 * tulos + (sisältö == null ? 0 : sisältö.hashCode()); tulos = 31 * tulos + Boolean.hashCode(auki); return tulos; } /* * Syväkopioi tämän olion ja palauttaa viitteen kopioon. Paluuarvo on null, * jos kopiointi epäonnistui. Toteutusta voi optimoida. Ei tarkista millään * tavalla osaavatko osaoliot syväkopioida itsensä. */ public Laatikko syväkopioi() { try { // Byte-tyyppisten alkioiden (tavujen) taulukkoon kirjoittava virta. ByteArrayOutputStream bos = new ByteArrayOutputStream(); // Olion tavuiksi muuntava virta, joka liittyy taulukkoon kirjoittavaan // virtaan. ObjectOutputStream oos = new ObjectOutputStream(bos); // Kirjoitetaan olio tavumuodossa taulukkoon. oos.writeObject(this); // Tyhjennetään puskuri ja suljetaan virta. oos.flush(); oos.close(); // Liitetään taulukkoon tavuja lukeva syötevirta. ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); // Tavut olioksi muuttava virta, joka liittyy taulukosta lukevaan virtaan. ObjectInputStream ois = new ObjectInputStream(bis); // Kopio saadaan aikaiseksi lukemalla olion tavut taulukosta. Object kopio = ois.readObject(); // Palautetaan oikean tyyppinen viite. return (Laatikko)kopio; } // Sarjallistettavan olion oletusrakentaja hukassa. catch (InvalidClassException e) { e.printStackTrace(); return null; } // Löytyi olio, joka ei sarjallistu. catch (NotSerializableException e) { e.printStackTrace(); return null; } // Tapahtui jotain yllättävää. catch (Exception e) { System.out.println("Paniikki!"); e.printStackTrace(); return null; } } }