Luento 2 T-106.1240 Ohjelmoinnin jatkokurssi T1 & T-106.1243 Ohjelmoinnin jatkokurssi L1 Luennoitsija: Otto Seppälä Kurssin WWW: http://www.cs.hut.fi/opinnot/t-106.1240/s2007
Abstrakti luokka Abstrakti luokka on luokkamäärittely jossa osan metodeista toteutus on siirretty aliluokkien tehtäväksi Toteutuksettomat metodit ovat ns. Abstrakteja metodeja Abstraktista luokasta ei voi muodostaa olioita Jos abstraktia metodia kutsuttaisiin ei tiedettäisi mitä tehdä Abstraktin metodin tehtävä on kuvata jokin toiminto, joka aliluokilla väistämättä on, mutta jolla ei itse abstraktin luokan tasolla ole järkevää toteutusta Esim abstraktissa luokassa Kuvio voi olla abstrakti metodi piirrä() Kuitenkin vasta aliluokissa Soikio ja Suorakulmio metodille on olemassa mahdolliset toteutukset Koska metodi on luokassa Kuvio, voidaan kuvioille kuitenkin kutsua metodia piirrä. Lopullinen toiminta riippuu aliluokasta.
Moniperinnän ongelma Moniperinnässä luokalla on useampi välitön yliluokka Moniperintä on ongelmallista Yliluokat voisivat sisältää samoilla puumerkeillä olevat metodit joilla olisi eri toteutukset - Ei tiedettäisi kumpaa kutsua Diamond of death Yliluokilla yhteinen yliluokka ja omat tavat käyttää sen resursseja Kielissä jotka tukevat moniperintää on erilaisia mekanismejä ongelman kiertämiseen Java ei tue moniperintää Moniperinnän kieltäminen selkiyttää monia asioita Entä jos olion kannattaisi olla kahden eri yliluokan instanssi? Ratkaisu löytyy rajapinnoista
Rajapinnat Rajapinta, interface, on Javan tarjoama mekanismi, jolla voidaan määritellä tyyppi määrittelemättä sille lainkaan toteutusta Abstraktia luokkaa muistuttava rakenne, joka ei kuitenkaan saa sisältää lainkaan metoditoteutuksia tai muuttujia (paitsi vakioita) Metodien esittelyihin ei kuitenkaan tarvita tässä abstract-sanaa, sillä rajapinnan metodit ovat aina abstrakteja ja julkisia Rajapinta näyttää muuten luokkamäärittelyltä, mutta sanan class sijaan tulee merkintä interface public interface Liikkuva { } public void siirry ( Paikka p );
Rajapinnat Rajapinnalla määritetty tyyppi on yleensä jonkinlainen ominaisuus, jonka rajapinnan täyttävät luokat omaavat Jokainen ominaisuuden täyttävä luokka kertoo toteuttavansa (implements) rajapinnassa vaaditut metoditoteutukset Jos luokka sekä täyttää rajapinnan että perii luokan merkitään perintä ensin. public class Kontti implements Liikkuva { public void siirry ( Paikka p ){...toteutus public class Kirje implements Liikkuva { public void siirry ( Paikka p ){... Kirjeille sopiva toteutus
Rajapinnat Koska mikä tahansa luokka voi täyttää rajapinnan riippumatta luokan muusta toteutuksesta rajapinta tarjoaa ohjelmoijalle valtavasti mahdollisuuksia Hyvin erityyppisiä luokkia voidaan käsitellä rajapinnan kautta näennäisen samankaltaisesti koska rajapinta takaa että tietyt palvelut (metodit) ovat varmasti tarjolla Kontti k = new Kontti(); teejotain( k ); teejotain( new Kirje() ); ---- public void teejotain ( Liikkuva esine ){... toteutusta esine.siirry(x); }
Rajapinnat Rajapinta Vain abstrakteja, julkisia metodeja (ei tarvitse erikseen kertoa) Vakioita = (public final static kenttiä) jotka pitää kaikki alustaa Koko rajapinnan näkyvyys on public tai default (ei mitään) Luokka voi täyttää samanaikaisesti monta eri rajapintaa Rajapinnat luetellaan pilkuilla eroteltuna listana implementsmääreen jälkeen Moniperinnän kaltaista ongelmatilannetta ei synny...paitsi jos rajapinnoissa voi olla samannimiset metodit jotka on tarkoitettu aivan eri tehtäviin...tai jos muuten samanlaiset metodit omaavat erityyppiset paluuarvot
Rajapinnat Abstrakti luokka voi täyttää rajapinnan vaikka rajapinnan vaatimat metodit olisivat abstrakteja Rajapinta voi periä yhden tai useamman rajapinnan koska toteutusristiriitaa ei juurikaan ole
Rajapinta Comparable Comparable-rajapinnan määrittelemä ominaisuus on nimenmukaisesti verrattavuus Rajapinta määrittelee yhden metodin compareto. Olion tulee verrata itseään parametriolioon ja palauttaa kokonaisluvulla onko parametriolio yhtäsuuri, suurempi vai pienempi Tämä riittää useimmille tietorakenteille ja algoritmeille monenlaiseen järjestyksen ylläpitoon. Algoritmit käsittelevät rajapinnan täyttäviä olioita Comparableolioina. public interface Comparable { } public int compareto ( Object o );
Rajapinta Comparable Comparable-rajapinnan määrittelemä ominaisuus on nimenmukaisesti verrattavuus Tämä rajapinta määrittelee yhden metodin compareto Olion tulee verrata itseään parametriolioon ja palauttaa kokonaisluvulla onko parametriolio yhtäsuuri, suurempi vai pienempi Tämä riittää useimmille tietorakenteille ja algoritmeille monenlaiseen järjestyksen ylläpitoon. Algoritmit käsittelevät rajapinnan täyttäviä olioita Comparableolioina. public interface Comparable <VerrattavaTyyppi> { } public int compareto ( VerrattavaTyyppi o );
Rajapinta Comparable Suuri määrä Javan luokista täyttää Comparablerajapinnan String Integer Date Vastaavasti mm. Collections Frameworkista löytyy paljon metodeja ja luokkia jotka osaavat toimia Comparable-olioiden kanssa esim Collections.sort osaa järjestää listoja joiden alkiot täyttävät Comparable-rajapinnan Eclipse-esimerkki livenä...
Muita rajapintoja Serializable ns. Marker Interface joka ei määrittele lainkaan metodeja vaan jota käytetään pelkästään tietyn ominaisuuden omaavien luokkien tunnistamiseen Serializable antaa java:lle luvan sarjallistaa olio muistista kuvaukseksi jonka voi tallentaa/siirtää jonnekin ja myöhemmin/muualla taas ladata takaisin muistiin
Generics Java-kielen versiossa 5 kieleen lisättiin tuki geneerisille tyypeille Mahdollistavat luokan sisältämien muuttujien ja paluuarvojen tyypin määrittämisen tapauskohtaisesti käyttämällä tyyppiparametreja Vähentävät tarvetta suorittaa tyyppipakotusta luokan ulkopuolella Esim version 4 ArrayList säilöi vain Object-viittauksia jotka piti tyyppipakottaa rakenteesta otettaessa Vähentävät samalla ajonaikaisia tyyppipakotusvirheitä (ClassCastException) koska generics:it hoidetaan jo käännösvaiheessa
Generics Allaoleva rivi luo ArrayList:in joka säilöö String-olioita ArrayList<String> sanalista = new ArrayList<String>(); Tässä String on tyyppiparametrille annettava arvo Määritelmästä johtuen sanalistan add-metodi hyväksyy vain Stringluokan olioita parametrikseen aliluokan tai alirajapinnankin oliot kelpaavat. String:illä vaan ei ole aliluokkia eikä sitä voi periä koska luokka on final. ArrayList<String> sanalista = new ArrayList<String>(); sanalista.add( Hei vaan! ); String sana = sanalista.get(0);
Geneerinen Luokka Seuraava pätee sekä luokille, että rajapinnoille,mutta yksinkertaisuuden vuoksi puhumme seuraavassa vain luokista Geneerisessä luokassa on aina yksi tai useampi tyyppiparametri Tyyppiparametrille annetaan jokin muuttujanimi, joka korvataan käännösaikaisesti oikeilla tyypeilla Luokkamäärittelyn yhteydessä tyyppiparametri esitellään < ja >- merkkien välissä heti luokan nimen jälkeen Tyyppiä voi tämän jälkeen käyttää luokassa esim. muuttujien ja paluuarvojen tyyppinä public class OmaLista<OmaTyyppi> { public OmaTyyppi vaihdaalkio(omatyyppi uusialkio){... jne.
Geneerinen Luokka 1. Kierroksen kolmostehtävässä on suhteellisen monimutkaiselta vaikuttava geneerisen luokan määrittely class BSTNode <ItemType extends Comparable <ItemType>> extends BinaryTreeNode <ItemType> Puretaan auki <ItemType extends Comparable<ItemType>> Tyyppiparametrin tulee olla verrattavissa samantyyppisiin alkioihin Comparable-rajapinnan kautta extends BinaryTreeNode <ItemType> BSTNode on peritty BinaryTreeNodesta. BSTNode säilyttää dataansa BinaryTreeNodessa, joten BinaryTreeNoden täytyy myös pystyä säilyttämään ItemType-tyyppisiä alkioita
Geneerinen Luokka Tyyppiparametrille voi myös määrätä vaatimuksia <Tyyppi extends Tulostettava> Tyyppiparametriksi Tyyppi saa antaa vain : rajapinnan Tulostettava täyttäviä luokkia tai luokan Tulostettava aliluokkia <Tyyppi extends Tulostettava & Tallennettava> Tyyppiparametriksi Tyyppi saa antaa vain molemmat ehdot täyttäviä tyyppejä
Geneerinen metodi Myös metodimäärittely voi olla geneerinen (riippumatta luokan geneerisyydestä) Tällöin tyyppiparametria ei erikseen anneta vaan se päätellään metodin parametrien tyypeistä Tyyppiparametri esitellään puumerkissä ennen paluuarvoa esim: public <Tyyppi> Tyyppi[] jokatoinen( Tyyppi[] taulu ) { Tyyppi[] uusitaulu = new Tyyppi[ taulu.length / 2 ]; for (int i = 0; i < taulu.length / 2; i++) { uusitaulu[ i ] = taulu[ i * 2 ]; } } return uusitaulu;
WildCard void tallennalistaan( Tässä? on ns. wildcard - List <? super String> lista, String alkio) Lista johon voi tallentaa merkkijonoja sekä yliluokan (Object) olioita. Käytetään esimerkiksi kun halutaan mikä tahansa rakenne johon voidaan tallentaa merkkijonoja. super-määrite toimii vain wildcardeille. Sen yhteydessä ei voi käyttää tyyppiparametria tyyliin Tyyppi super String void tulostalista( List <? extends Tulostettava> lista) Lista josta otettavat alkiot ovat Tulostettava-luokan/rajapinnan instansseja void kaannalista( List<?> kaannatama) Lista mitä vain tyyppiä Käytännössä sama kuin List<? extends Object>
Sisäluokat Sisäluokat ovat toisten luokkien luokkamäärittelyn sisällä määriteltyjä luokkia. Sisäluokkia on käytännössä neljää tyyppiä jäsenluokat (member classes) staattiset sisäluokat paikalliset sisäluokat anonyymit sisäluokat Esimerkki sisäluokan käytöstä ovat mm. java APIn HashMaprakenne joka tallentaa avain-arvo-parin Map.Entry tyyppiseen olioon.
Sisäluokat Staattiset sisäluokat (Static nested classes) Muuten kuin tavallisia luokkia, mutta pystyvät lukemaan luokan staattisia kenttiä ja suorittamaan sen staattisia metodeja Eivät liity mihinkään ulompaan olioon! Joissakin tilanteissa tämä on erittäin kätevää Jäsenluokat (inner classes) Jäsenluokan olio liittyy aina tiettyyn ulomman luokan olioon. (enclosing instance) Se voi kutsua kaikkia tämän olion metodeita ja lukea sekä asettaa sen kaikkia kenttiä. Ulommalla oliolla on vastaavat oikeudet sisempään olioon. Ulomalla oliolla voi olla mikä tahansa määrä sisempiä olioita
Sisäluokat Paikalliset sisäluokat (local class) Metodin tms. lohkon sisällä esiteltyä nimettyä luokkaa kutsutaan paikalliseksi sisäluokaksi. Paikallisen sisäluokan tyyppi on näkyvissä vain esittelylohkonsa sisällä. Paikallinen sisäluokka näkee ulkoluokan kentät ja metodit, mutta myös sen metodin joka sisäluokan olion loi, vakiomuuttujat Tämä estää sisäluokkaa vaikuttamasta metodin muuttujiin, jolloin vältetään mahdolliset rinnakkaisuusongelmat Vastaavasti vakiomuuttujien arvot ovat varmasti selvillä Lisäksi tämä toimii myös kun metodin suoritus on jo päättynyt Usein metodin parametrista voi tehdä vakion final-määreellä sisäluokkaa ajatellen.
Sisäluokat Anonyymit sisäluokat (anonymous inner classes) Luokille voi rakentaa lennossa aliluokkia samantapaisella menetelmällä kuin tavallisestikin. Käytännössä anonyymi sisäluokka on paikallinen luokka jolle ei anneta erillistä nimeä. Vastaavasti voidaan kirjoittaa toteutus rajapinnalle Anonyymin sisäluokan toteutus kirjoitetaan välittömästi konstruktorikutsun perään. Toteutus on kuten tavallinen luokan toteutusosa. Anonyymin sisäluokan voi tehdä kaikkialla missä voi kutsua konstruktoria Comparable vertailija = new Comparable(){ public int compareto(object o){...jotain koodia... } }; huomaa puolipiste
Sisäluokat Anonyymit sisäluokat Graafisissa käyttöliittymissä käytetään tyypillisesti paljon anonyymejä sisäluokkia. Tyypilisesti nämä ovat tapahtumankuuntelijoita Pikkuasioiden hoitoon ei kannata käyttää omaa tiedostoa Koodi on editoitavissa siellä missä sitä tarvitaan
Tulevia luentoja (alustava) 17.9 Rajapinnat, Generics, Design Patterns I Olioiden tyypeistä: Luokat, interfacet, geneeristen luokkien kirjoittaminen. Rajapinnat Java APIssa. Mikä on suunnittelumalli? Muutama suunnittelumalli. 24.9 Projektiohjeistusta Ohjeita projektin aihevalintaan ja suunnitelman kirjoitukseen. Projektin jakaminen hallittaviin ja testattaviin kokonaisuuksiin. Toteutusjärjestys. Tiedostoformaattien suunnitteluperiaatteita. 1.10. Design for re-use, Design Patterns II, kirjastot Ohjelmien rakentaminen tulevia muutoksia ajatellen, defensive programming, muunneltavuutta edistävät suunnittelumallit. Katsaus joihinkin Java API:n kirjastoihin ja ideoihin niiden suunnittelussa.
Tulevia luentoja (alustava) 8.10 Graafiset käyttöliittymät, Versionhallinta Tapahtumat ja tapahtumankäsittely, GUI:n perustoiminnallisuus, Grafiikan piirtäminen. Versionhallinta. 15.10 Rinnakkaisohjelmoinnin alkeet Javan rinnakkaisuusprimitiivit, Luokka Thread, Runnablerajapinta, Perus Producer-Consumer esimerkkitoteutus, blocking, potentiaaliset ongelmat kuten deadlock. Graafiset käyttöliittymät ja rinnakkaisuus 22.10 Vaihtelevia aiheita Esim. Ohjelmien profilointi ja profiilien tulkinta, asiaa koodin optimoinnista, Lisää kirjastoja, yleiskatsaus palautettuihin suunnitelmiin ja ohjeistusta projektiin.
Design Patterns Suunnittelumallit
Suunnittelumalli Mikä on suunnittelumalli (Design Pattern) Suunnittelumalli on tiiviisti esitetty yleinen kuvaus jostakin toimivasta ratkaisusta yleiseen ongelmaan. Suunnittelumallien ideoita voi käytettäessä soveltaa vapaasti. Malleja voi myös yhdistellä, ja niin usein tehdäänkin. Vertailukohtana voi ajatella vaikkapa erilaisia puuliitostapoja, tanssiliikkeitä, Niksi-pirkan vinkkejä jne. Suunnittelumalli kuvaa yleisen ratkaisutavan ja sovellusalue lopulta määrää sen kuinka mallia kulloinkin käytetään.
Gang of Four Patterns Seuraavaksi käydään läpi joitakin kirjassa Gamma, Helm, Johnson, Vlissides : Design Patterns Elements of Reusable Object-Oriented Software, 1995 esiteltyjä suunnittelumalleja. Tässä kirjassa esitettyjä malleja kutsutaan yleisesti nimellä Gangof-Four patterns kirjan neljän kirjoittajan mukaan. Seuraavat kalvot perustuvat k.o. kirjaan.
Adapter Ongelma Jokin osa olemassaolevaa koodia toimii tietyn rajapinnan täyttävien olioiden kanssa. Tarjolla olevien olioiden luokka ei kuitenkaan täytä tätä rajapintaa. Ratkaisu Alkuperäistä luokkaa ei kuitenkaan haluta muuttaa. Adapter sovittaa jonkin olemassaolevan luokan rajapinnan muotoon, jota jokin toinen osa ohjelmasta odottaa. Tästä suunnittelumallista on kaksi versiota : olioadapteri luokka-adapteri
Adapter - olioadapteri Toteutus : Käyttävä luokka tekee kaiken toimintansa adapteri-olioiden kanssa, jotka täyttävät vaaditun rajapinnan. Adapterioliosta on viittaus varsinaiseen olioon Rajapinnan mukaiset metodikutsut delegoidaan viittauksen kautta eteenpäin oikealle oliolle Käyttävä Luokka Tuttu Rajapinta Adapteri Käytettävä Luokka
Adapter - luokkadapteri Adapteri voi myös periä luokan (jos mahdollista), jonka rajapintaa se muuttaa. Tällöin adapteri täyttää rajapinnan vaatimat metodit, joiden tehtävä on kutsua yliluokan metodeja. Käyttävä Luokka Tuttu Rajapinta Adapteri Käytettävä Luokka
Adapteri Esimerkki: List-olion joka sisältää Comparable rajapinnan täyttäviä olioita saa automaattisesti järjestettyä Collections-luokasta löytyvällä sort-metodilla. Robotti-luokan oliot haluttaisiin järjestää painon mukaan, mutta luokka ei täytä Comparable-rajapintaa eikä luokkaa voi muuttaa. public class VertailuSovitin implements Comparable<VertailuSovitin> { public VertailuSovitin( Robotti r ) { this.robotti = r; } } public int compareto( VertailuSovitin other ){ if (robotti.annapaino() < other.robotti.annapaino()) return -1; else if (robotti.annapaino() == other.robotti.annapaino()) return 0; return 1; }