Helsingin yliopisto > Tietojenkäsittelytieteen laitos > Ohjelmointitekniikka (Java) -opintojakso keväällä 2005

Koetehtävän 3 arvostelukertomus

Tehtävä

  1. Javan versio 1.5 toi mukanaan paljon uutuuksia, joista geneerisyys kaikkine lisäyksineen (Autoboxing, For-Each, ...) oli merkittävin. Selitä mistä on kysymys ja havainnollista pienin mutta valaisevin ohjelmaesimerkein tätä uutta käännösaikaista tyyppiparametrointia.
  2. Oletetaan että tehtävänäsi olisi laatia ohjelma, joka saa syötteenään joukon sanoja. Syöte voi koostua useista riveistä. Syötteen loputtua ohjelma tulostaa aakkosjärjestyksessä sanat, joita syötteessä esiintyi ja jokaisen sanan perässä esiintymisten lukumäärän. Selosta miten käyttäisit hyväksesi java.util -paketin tarjoamia työkaluja ja Java-kielen uusia ominaisuuksia. Täydellistä ohjelmakoodia ei tarvitse kirjoittaa, oleellista on osata soveltaa edellämainittuja työkaluja ja ominaisuuksia annettuun tehtävään.

Esimerkkivastaus

  1. Geneerisyys toi uutena ominaisuutena tyyppiparametrien käytön. Tyyppiparametrejä käyttäen ohjelmoija voi tehdä luokkia, joilla on parametrinä yksi tai useampi tyyppi, esimerkiksi tähän tapaan:

    public class Esimerkki<E> {
        //Jotain koodia...
    }
    

    Tässä esimerkissä E on tyyppiparametri.

    Kun tälläisestä geneerisestä luokasta luodaan instassi määritellään samalla konkreettisesti tyyppiparametrin arvo. Tätä tyyppiparametriä voidaan käyttää esimerkiksi metodikutsujen argumenteissa ja paluuarvoissa, vaikkapa tähän tapaan:

    public E metodi(E parametri) {
        //Jotain Koodia...
        return parametri;
    }
    
    Geneerisyyden etu on koodin siisteytymisessä ja lisääntyneessä turvallisuudessa. Esimerkiksi Javan kokoelmaluokkia käytettäessä oli ennen tavanomaista joutua tekemään eksplisiittinen tyyppimuunnos koska kokoelmissa saattoi olla minkä tyyppisiä olioita tahansa eikä kääntäjä voinut tietää mitä:

    String merkkijono = (String)iteraattori.next();
    
    Paitsi että tyyppimuunnosten kirjoittaminen joka kerta on kankeaa se on myös virhealtista, koska virheellinen tyyppimuunnos huomataan vasta ajon aikana. Geneerisiä luokkia käytettäessä kääntäjä tietää mitä tyyppiä palautusarvo on eikä sitä tarvitse siksi erikseen mainita. Lisäksi kääntäjä pystyy varoittamaan virheistä jo ohjelmaa käännettäessä. Uusi versio geneerisyyttä käytettäessä näyttäisi tältä:

    String merkkijono = iteraattori.next();
    
    Myös muutamat muut Javan uuden version ominaisuudet tukevat geneeristen luokkien käyttöä. Esimerkiksi uusi for-each-silmukka helpottaa kokoelmien läpikäyntiä:

    for (String merkkijono : jokinKokoelma) {
        //Jotain koodia...
    }
    
    Toinen geneerisyyden yhteydessä usein käytetty uusi ominaisuus on Autoboxing, mikä tarkoittaa sitä että alkeistyyppiset muuttujat muutetaan automaattisesti vastaaviksi luokiksi tarvittaessa ja toisinpäin. Esimerkiksi näin:

    int i = new Integer(1);
    
    Javan geneerisyys on puhtaasti käännöaikainen ominaisuus. Ne virheet mitä havaitaan havaitaan kääntämisen aikana eikä tyyppitietoutta säilytetä enää ajon aikana.
  2. import java.util.*;
    
    public class Sanalaskin {
        public static void main(String[] args) {
            Scanner sc = new Scanner(System.in);
            TreeMap<String,Integer> tm = new TreeMap<String,Integer>();
    
            while (sc.hasNext()) {
                    String word = sc.next();
                    Integer oldValue = tm.get(word);
                    tm.put(word,oldValue == null ? 1 : ++oldValue);
                }
    
            for (String word : tm.keySet())
                System.out.printf("%s %d\n",word,tm.get(word));
        }
    }
    

    Tässä ratkaisussa java.util-paketin työkaluja ja Javan uusia ominaisuuksia on käytetty seuraavasti:

    Syöte luetaan Scanner-luokan avulla.

    Sanat ja niiden esiintymistiheys talletetaan TreeMap-luokan avulla, jolloin sanat ovat automaattisesti aakkosjärjestyksessä.

    Käytetään uutta geneeristä TreeMap-luokkaa.

    Käytetään Autoboxing-ominaisuutta.

    Käytetään uutta tulostuksen muotoilua.

    Käytetään for-each-silmukkaa sanojen läpikäyntiin lopussa.

Pisteytys

  1. Vastauksesta sai enintään 4 pistettä.

    Eri kohdista sai pisteitä seuraavasti:

    1 piste: Selitettiin mitä geneerisyys on ja geneerisyyden keskeiset piirteet.

    1 piste: Selitettiin miksi ja miten geneerisyyttä käytetään.

    1 piste: Esiteltiin geneerisyyteen liittyviä muita Javan ominaisuuksia.

    1 piste: Käytettiin apuna edellämainittujen kohtien selittämisessä koodiesimerkkejä.

    Jokerimerkkien käsittelyä ei vaadittu eikä siitä saanut pisteitä.

    Jokaisesta näistä kohdista saadun pisteen saattoi menettää kokonaan tai osittain seuraavin perustein:

    -0 pistettä: Kohdan selityksessä on vain pieniä ja suhteellisen merkityksettömiä tai kysymykseen liittymättömiä epätarkkuuksia tai puutteita.

    -½ pistettä: Kohdan selityksessä on joitain kohtia jotka ovat väärin tai ilmaistu puutteellisesti tai epäselvästi. Perusajatus on kuitenkin ymmärretty.

    -1 piste: Kohtaa ei käsitelty tai käsiteltiin väärin.

    Pyöristykset tehtiin ylöspäin.

  2. Vastauksesta sai enintään 3 pistettä.

    Itse kysytynlaisen ohjelman laatimisen tulisi tässä vaiheessa olla triviaalia, joten siitä ei itsessään saanut pisteitä. Jos kuvattu algoritmi oli kuitenkin väärin tai mitään siihen viittaavaakaan ei esitetty siitä menetti 1 pisteen.

    Tehtävässä sai pisteitä java.util-paketin työkalujen ja Javan uusien ominaisuuksien käytöstä tehtävän ratkaisuun. Sekä työkalun tai ominaisuuden käytön selittäminen että koodi tai pseudokoodi hyväksyttiin. Syntaksivirheistä tai metodien ja luokkien nimien väärin muistamisesta ei pisteitä vähennetty jos vastauksesta ilmeni että asia oltiin ymmärretty oikein.

    Pisteitä sai työkalujen tai tekniikoiden käytöstä seuraavasti:

    1 piste:Käytettiin jotain Map-rajapinnan toteuttavaa kokoelmaluokkaa esiintyneiden sanojen ja vastaavan esiintymistiheyden tallentamiseen.

    ½ pistettä:Käytettiin Vector-luokkaa tai vastaavaa esiintyneiden sanojen tallennukseen. Tästä ei saanut pisteitä jos sai jo pisteen edellisestä kohdasta.

    ½ pistettä:Käytettiin Collections- tai Arrays-luokan välineitä järjestämiseen.

    ½ pistettä:Käytettiin kokoelmaluokkaa joka säilyttää järjestyksen automaattisesti.

    ½ pistettä:Käytettiin soveltuvissa kohdin luokkien uusia geneerisiä versioita.

    ½ pistettä:Käytettiin Scanner-luokkaa syötteen lukemiseen.

    ½ pistettä:Käytettiin muotoiltua tulostamista.

    ½ pistettä:Käytettiin for-each-rakennetta silmukassa.

    ½ pistettä:Käytettiin Autoboxing-ominaisuutta.

    Huomaa, että listan pisteiden summa on enemmän kuin 3. Ylijäävät pisteet jätettiin huomioimatta. Kaikkia listan tekniikoita ei siis tarvinnut käyttää yltääkseen täysiin pisteisiin. Pyöristykset tehtiin alaspäin.

    Pisteet jäivät saamatta kohdasta, jossa tekniikkaa oli käytetty väärin tai sen käyttö oli selitetty epäselvästi tai puutteellisesti. Pelkästä tekniikan mainitsemisesta ei saanut pisteitä vaan siihen vaadittiin tekniikan kytkemistä ratkaisuun.

Pisteiden jakauma

Yhteispisteiden keskiarvoksi muodostui 4,4.

Taulukko 1. Pisteiden jakauma
yhteispisteet vastausten lukumäärä
7 6
6 13
5 9
4 13
3 8
2 6
1 2
0 1