Tämä kurssi on jo päättynyt.

Luku 4.1: Autoiluharjoitus

Tästä sivusta:

Pääkysymyksiä: Osaanko jo kohta toteuttaa Scala-luokkia sujuvasti?

Mitä käsitellään? Yhdistellään aiempien lukujen aiheita. Lisäksi: lyhennysmerkinnät sijoituskäskyille; vähän lisää metodinimien kuormittamisesta.

Mitä tehdään? Ohjelmoidaan ja leikitään annetulla autoiluohjelmalla.

Suuntaa antava työläysarvio:? Kolme, neljä tuntia? Omatoiminen ohjelmointi voi tuottaa haasteita, joten tehtävässä on jumiutumismahdollisuus. Varaa mukavasti aikaa, lue vinkit kunnolla ja hyödynnä kurssin neuvontakanavia, ettei aikaa mene tolkuttomasti. Jos ei jää jumiin, voi kyllä selvitä nopeamminkin.

Pistearvo: A5 + B80.

Oheismoduulit: CarSim (uusi).

../_images/person08.png

Aluksi: lyhennysmerkinnöistä

Kun sijoitamme esimerkiksi kokooja- tai askeltajamuuttujaan, käytämme uuden arvon määräävässä lausekkeessa muuttujan vanhaa arvoa. Kun kyseessä on lukuarvoinen muuttuja, uusi arvo usein määräytyy aritmeettisen laskutoimituksen tuloksena. Esimerkiksi:

this.saldo = this.saldo + maara
this.value = this.value + 1

Koska tämänkaltaiset sijoitukset ovat yleisiä ja koska niissä toistuu sama koodinpätkä useita kertoja, on määritelty lyhyempiä tapoja merkitä asia. Esimerkiksi äskeiset koodinpätkät voi kirjoittaa näinkin:

this.saldo += maara
this.value += 1

Sijoitusoperaattori += siis tarkoittaa: "Kasvata vasemmalla puolella mainitun muuttujan arvoa oikealla puolella olevan lausekkeen verran." Tai tarkemmin: "Ota vasemmalla puolella mainitun var-muuttujan vanha arvo ja oikealla puolella olevan lausekkeen arvo. Laske ne yhteen. Sijoita lopputulos vasemmalla puolella mainittuun muuttujaan."

(Tässä on muuten kyseessä täsmälleen samanlainen merkintä += kuin luvussa 1.5, jossa merkintää käytettiin lisäämään uusi arvo puskuriin. Nyt on kysymys eri asiasta eli yhteenlaskusta. Analogia tapausten välillä toki on: puskuria päivitetään lisäämällä vs. muuttujan arvoa päivitetään lisäämällä.)

Syntaktinen sokeri

Ei ole harvinaista, että ohjelmointikieli tarjoaa erilaisia tapoja kirjoittaa saman asian. Tällöin usein puhutaan syntaktisesta sokerista (syntactic sugar) eli sellaisista kielirakenteista, jotka eivät ole ohjelmointikielen ilmaisuvoiman kannalta välttämättömiä (eli eivät mahdollista uudenlaisia ohjelmia) mutta jotka tekevät asioiden ilmaisemisesta ohjelmoijan kannalta kätevämpää tai kauniimpaa. Yllä esitellyt lyhennysmerkinnät yleisille sijoituksille ovat esimerkki syntaktisesta sokerista.

Syntaktinen sokeri on makuasia, josta voi halutessaan kiistellä. Koska lähes kaikki yleiskäyttöiset ohjelmointikielet (kuten Scala) ovat laskennalliselta ilmaisuvoimaltaan yhtä hyviä, voisi jopa sanoa, että eräällä tavalla ohjelmointikielet yleensäkin ovat vain syntaktista sokeria.

Lisää lyhennyksiä

Vastaavia lyhennysmerkintöjä on enemmänkin; esimerkkejä niistä on alla taulukossa. Scala-työkalut automaattisesti "laventavat" lyhennetyt käskyt.

Pidemmin Lyhyemmin
arvo = arvo + toinen arvo += toinen
arvo = arvo - toinen arvo -= toinen
arvo = arvo * toinen arvo *= toinen
arvo = arvo / toinen arvo /= toinen
arvo = arvo % toinen arvo %= toinen

Näiden lyhennysmerkintöjen käyttö on yleistä, ja käytämme niitä jatkossa myös tällä kurssilla. Voit käyttää niitä itse esimerkiksi tämän luvun ohjelmointitehtävässä tai olla käyttämättä. Joka tapauksessa on tarpeen pystyä lukemaan koodia, jossa niitä käytetään.

Usein kysytty kysymys

Eräissä yleisissä ohjelmointikielissä (esim. C, Java) on oheisiakin lyhyempi ilmaisu, jolla lisätään tai vähennetään ykkönen muuttujan arvosta. Esimerkiksi luku++ ja luku-- muuttavat muuttujan arvoa yhdellä kuten luku += 1 ja luku -= 1. Onko tällaisia merkintöjä Scalassakin?

Vastaus: Ei ole, koska lisälyhennykset on katsottu tarpeettomiksi. Usein Scalalla ohjelmoidaan tyylillä, jossa var-muuttujia käytetään niukasti (luku 10.2), joten näiden merkintöjenkin merkitys on vähäisempi. Toisaalta Scalalla voi itsekin määritellä "omia operaattoreita" (luku 5.2).

Pikkutehtäviä

Tarkastele seuraavaa koodinpätkää, ja mieti, mitä arvoja luku-muuttuja saa, kun koodi suoritetaan.

var luku = 5
luku += 1
luku *= 3
luku += luku
luku -= 2 * (luku - 13)

Kirjoita tähän allekkain viisi riviä, joilla luettelet järjestyksessä ne kokonaisluvut, jotka ovat lukumuuttujan arvoina kunkin ohjelmamme rivin suorittamisen jälkeen.

Myös merkkijonoilla on operaattorit += ja *=.

Tarkastele seuraavaa koodinpätkää.

var teksti = ""
teksti += "<e--gg#h>ed--"
teksti += "<h>f#-<h>de"
teksti *= 3
teksti += "/135"
play(teksti)
Sivumennen sanoen: Muuttujan alkuarvo on tässä tyhjä merkkijono (empty string) eli merkkijono, joka ei sisällä yhtäkään merkkiä (ei edes välilyöntejä, lainausmerkkejä tms.). Tyhjä merkkijono kirjataan koodiin kahtena lainausmerkkinä, joiden välissä ei ole mitään. Tällaiseen merkkijonoon voi operaattoreilla + ja += yhdistää muita arvoja, kuten tässäkin esimerkissä tehdään.

Minkä merkkijonon koodinpätkä lähettää play-funktiolle? Selvitä ja liimaa tähän.

Lisäharjoite

Muokkaa luvun 3.5 VendingMachine-luokkaa niin, että käytät tavallisten sijoituskäskyjen sijaan yllä esiteltyjä lyhennysmerkintöjä siellä missä mahdollista.

Luokan koodi löytyy Miscellaneous-moduulista.

CarSim-tehtävä

../_images/carsim.png

CarSim on autosimulaattoriohjelma, jossa käyttäjä voi ajella palluroina kuvattuja autoja maailman kartalla.

Tehtävänanto

Nouda moduuli CarSim. Se sisältää hyvän osan sovellusohjelmasta. Käyttöliittymä on annettu kokonaan valmiina. Keskeinen osa ohjelmaa kuitenkin puuttuu. Tehtäväsi on

  • lukea luokan o1.carsim.Car dokumentaatio hartaudella;
  • kirjoittaa Scala-ohjelmakoodi, joka toteuttaa tuon täysin puutteellisena annetun luokan; ja
  • testata, että se toimii dokumentaation mukaisesti, ja korjata tarvittaessa.

???

Annetussa luokan Car raakileessa esiintyy useassa kohdassa kolme peräkkäistä kysymysmerkkiä: ???. Kyseessä on Scala-kielen tarjoama tapa merkitä, että osa ohjelmasta on toistaiseksi toteuttamatta. Jos ???-lausekkeen yrittää evaluoida (eli vaikkapa annetun Car-luokan metodeita kutsua), syntyy ajonaikainen poikkeustilanne.

Kaksi fuel-metodia?

Huomasitko, että luokassa on kaksi eri fuel-nimistä metodia, joilla on erilaiset parametriluettelot?

Nämä metodit ovat toisistaan ihan erilliset, mutta voit hyödyntää yhtä toisen toteutuksessa.

Hieman lisätietoa keskenään samannimisistä metodeista löytyy tämän autotehtävän jäljestä.

Suosittelemme, että noudatat seuraavia työvaiheita.

1/6: Ilmentymämuuttujat (ainakin alustavasti)

Sinun on itse valittava ilmentymämuuttujat niin, että tarvittavat tilatiedot pysyvät tallessa olioissa metodikutsujen välilläkin. Osan muuttujista tulee olla yksityisiä, jotta niiden arvoja ei voi mielivaltaisesti muuttaa luokan ulkopuolelta.

Dokumentaatio kuvaa konstruktoriparametrit, ja annetusta koodistakin ne löytyvät. Osa niistä on myös (perustellusti) määritelty ilmentymämuuttujiksi. Mitä muita oleellisia piirteitä kullakin autolla on eli mistä muusta Car-olion on pidettävä kirjaa, jotta sen metodit voisivat toimia? Millaisiin muuttujiin nuo tiedot voisi tallentaa? Mitkä ovat muuttujien tyypit ja roolit?

Kaikkia ilmentymämuuttujia ei välttämättä tarvitse keksiä heti. Voit myöhemmin palata lisäämään muuttujia, jos huomaat niille tarvetta.

Pitäydy pyydetyssä rajapinnassa

Älä lisää luokkaan julkisia osia, joita ei pyydetty. Yksityisiä osia voit lisätä vapaasti.

Sama ohje pätee kaikkiin niihin kurssin ohjelmointitehtäviin, joissa on annettu tarkka spesifikaatio toteutettavien luokkien julkisesta rajapinnasta.

Valinnainen vinkki ilmentymämuuttujista

Näytä vinkkiPiilota

Ilmentymämuuttujissa on tarpeen pitää kirjaa niistä asioista, jotka säilyvät metodikutsujen välilläkin. Näitä ovat muun muassa polttoaineen kulutus ja tankin koko, jotka onkin jo annetussa koodissa ilmentymämuuttujiksi merkitty.

Useat metodit käsittelevät tankissa olevan bensan määrää. Tuo tieto on pidettävä tallessa ilmentymämuuttujassa, jotta se ei katoa metodikutsujen välillä.

Metodit location ja drive käsittelevät auton sijaintia, jonka on myös tallennuttava osaksi auton tietoja.

metersDriven ja drive tarvitsevat tietoa siitä, paljonko autolla on siihen mennessä ajettu.

Muita ilmentymämuuttujia et tarvitse. Jos haluat pitää metodin sisäisesti tallessa jotakin tietoa, voit kirjata sen paikalliseen muuttujaan.

2/6: Helpommat(?) metodit

drive on metodeista monimutkaisin. Yksi mahdollisuus on edetä tehtävän parissa niin, että unohdat sen toistaiseksi ja laadit toteutukset ensin muille metodeille. (Luithan kuitenkin myös drive-metodin dokumentaation jo, jotta ymmärrät tuonkin metodin tarkoituksen?)

Valinnainen vinkki location- ja metersDriven-metodeihin

Näytä vinkkiPiilota

Kunhan sinulla on sijaintia ja ajettua matkaa vastaavat ilmentymämuuttujat, näiden metodien toteutuksista tulee äärimmäisen yksinkertaiset.

Huom. Vaikket olisi vielä tehnytkään drive-metodia, voit tässä olettaa, että se tulee vielä myöhemmin toteutettua ja päivittää sijaintia ja ajettua matkaa sopivasti. Näille kahdelle muulle metodille jää tehtäväksi vain palauttaa arvo; metodit ovat vaikutuksettomia.

Valinnainen vinkki fuel-metodeihin

Näytä vinkkiPiilota

Parametrillinen fuel-metodi on samantapainen kuin eräät aiemmissa tehtävissä laatimasi metodit (esim. nostot pankkitililtä). Huomaa, mitä metodin tulee palauttaa. Käytä paikallista apumuuttujaa.

Parametriton fuel-metodi on toteutettavissa erittäin yksinkertaisesti kutsumalla parametrillista fuel-metodia riittävän suurella parametriarvolla.

Valinnainen vinkki fuelRatio- ja fuelRange-metodeihin

Näytä vinkkiPiilota

Näissä metodeissa selviät tavallisilla laskutoimituksilla. Huomaa yksiköt: prosentit nollan ja sadan väliltä; auton "kantama" metreinä (eikä satoina kilometreinä).

3/6: Testaa luokkaasi irrallisena ohjelmakomponenttina

Kokeile ajaa annettu testiohjelma CarTest, joka käyttää Car-luokkaa varsinaisesta kokonaisesta CarSim-simulaattoriohjelmasta irrallaan. Tuon testiohjelman pitäisi nyt toimia muuten paitsi ajamisen osalta.

Voit muokata testiohjelmaa kattavuuden parantamiseksi. Voit myös kokeilla Car-luokan käyttöä REPLissä.

4/6: Testaa luokkaasi CarSim-sovelluksen osana

Käynnistä CarSim käynnistysoliosta o1.carsim.gui.CarSim. Kokeile autojen luomista ja tankkaamista graafisessa käyttöliittymässä. Lyhyt käyttöohje näkyy siinä ikkunan alareunassa.

5/6: Toteuta myös drive-metodi

Kuten dokumentaatiostakin käy ilmi, tässä tehtävässä voit olettaa auton liikkuvan suoraa viivaa pitkin, "linnuntietä". Joskus se jää matkan varrelle, kun bensa loppuu.

Jos auton pysähtymissijainnin määrittämiseen liittyvä matematiikka tuottaa vaikeuksia, niin näistä vinkeistä voi olla apua:

  • Vaikka CarSim käyttääkin Pos-olion x:ää ja y:tä kuvaamaan leveys- ja pituusasteita maapallon pyöreällä pinnalla, niin tämä tehtävä on pedattu sinulle niin, että voit edelleen ajatella Pos-olioiden yksinkertaisesti kuvaavan pisteitä tavallisessa, litteässä, kaksiulotteisessa koordinaatistossa.
  • Unohda ohjelmointi hetkeksi, piirrä kaavio auton liikkeestä ja selvitä matemaattinen pulma ensin. Mieti vasta sitten, miten saat ongelman ratkaisun kirjoitettua Scalaksi.
  • Sinun ei tarvitse mallintaa auton nopeutta. Riittää, kun huolehdit dokumentaation kuvaamista asioista.

Valinnainen vinkki tarvittavista laskutoimituksista

NäytäPiilota

Trigonometriaa ei tarvita. Peruslaskutoimitukset (kerto-, yhteen-, jako-, vähennys-) riittävät.

Lisäksi voit hyödyntää luokan Pos metodeita. Esimerkiksi jo tuttu distance on hyödyllinen, ja muitakin dokumentaatiossa kuvattuja voit maun mukaan ottaa avuksi.

Lisävinkkejä

NäytäPiilota

Huomaa yksiköt: matkat metreinä, kulutus sataa kilometriä kohden.

Hankalin kohta saattaa olla sijainnin määrittäminen bensan loppuessa. Se onnistuu helpoimmin, kun lasket suhteen oikeasti ajetun matkan ja ajettavaksi määrätyn matkan (eli metodin toisen parametrin) välillä. Koska mallinnamme auton liikkeen suorana viivana kohti määränpäätä, auton kaksi koordinaattia muuttuvat samassa suhteessa.

6/6: Testaa lisää

Testaa ajamismetodia. Suosittelemme nytkin, että kokeilet sitä ensin irrallaan CarSim-sovelluksen kokonaisuudesta CarTest-pikkuohjelmassa.

Leiki sitten autoiluohjelmalla.

Miten autot ajavat CarSimissä?

Ehkä kummastelet, miten CarSimissä autolta sujuu mutkittelukin, vaikka toteutuksesi on suoraviivaisempi.

CarSim-sovellus kyllä käyttää juuri laatimaasi autoluokkaa mutta taiten: se kuvaa lähtösijainnin ja määränpään välisen mutkittelevan reitin pikkuisina suorina paloina ja kutsuu drive-metodiasi kullekin palaselle peräjälkeen. Jos haluat, saat palaset näkyviin CarSimin Settings-valikosta.

Entä reitin haku? Siihen CarSim käyttää Here.com:in verkkopalvelua, joka muistuttaa esimerkiksi Google-yhtiön tarjoamia sijaintipalveluita. Myös karttatiedot sovellus noutaa ilmaisesta verkko-APIsta.

Kaikki tämä on tehty CarSimin valmiina annetussa koodissa, jota voit silmäillä mutta joka on monin paikoin sellaista, ettei siitä ohjelmoinnin aloittelija saane vielä tolkkua. Joka tapauksessa sen havainnon voit tehdä, että ohjelma voi käyttää toisten tahojen tarjoamia palveluja apunaan.

A+ esittää tässä kohdassa tehtävän palautuslomakkeen.

Kuormittamisesta

Samannimisiä metodeita

Äskeissä tehtävässä oli kaksi fuel-metodia. Toinen otti yhden parametrin, toinen vain tyhjän parametriluettelon. Kuten viimeistään tästä näkyy, luokassa (tai yksittäisoliossa) voi mainiosti olla useita keskenään samannimisiä metodeita. Tämä on kuitenkin sallittua vain, jos samannimisillä metodeilla on erilaiset parametriluettelot.

Saman nimen käyttöä sanotaan kuormittamiseksi (overloading). Kyse on sinänsä ihan samasta kuin luvussa 1.7, jossa oli kaksi pystypalkki-nimistä funktiota; tällä kertaa vain kuormitit laatimasi autoluokan metodeita.

Metodin määrittelyyn kirjattu nimi sekä parametriluettelo kuuluvat metodin puumerkkiin (eli signatuuriin; signature). Kun kutsut olion metodia — olio.metodinNimi(parametrit) — suoritettava metodi määräytyy sen mukaan, minkä metodin puumerkki vastaa kutsussa esiintyviä tietoja. Kahta puumerkiltään identtistä metodia ei samaan luokkaan voi määritellä.

Periaatteessa kuormitetut metodit voisivat tehdä jotakin keskenään ihan erilaistakin, mutta yleinen käyttö kuormittamiselle on laatia erilaisia variaatioita samasta toiminnosta kuten autotehtävässä yllä.

Kuormittaminen ja palautusarvojen tyypit Scalassa

Luvussa 1.8 oli ensi kerran puhetta siitä, että minkä tahansa Scala-funktion koodiin voi kirjata palautusarvon tyypin. Joissakin tilanteissa tämä on myös pakollista, jotta Scalan automaattinen tyyppipäättely toimisi oikein. Ehkä yleisin tällainen pakkotilanne liittyy metodinimien kuormitukseen: kun metodi kutsuu toista samannimistä metodia, kutsuvalle "kaimalle" on määriteltävä palautusarvon tyyppi. Voit todeta asian itsekin seuraavasti.

Toteutitko parametrittoman fuel-metodin kutsumalla parametrillista fuel-metodia, kuten vinkattiin? Jos teit niin, voit nyt kokeilla poistaa tyyppimäärittelyn (kaksoispisteen ja Double-sanan) metodin alusta. Saat virheilmoituksen overloaded method fuel needs result type eli "kuormitetulle metodille fuel tarvitaan palautusarvon tyyppi".

Ilmiö on helppo havaita myös REPLissä:

class Luokka {
  def metodi(eka: Int, toka: Int) = eka + toka
  def metodi(luku: Int)           = this.metodi(luku, luku)
}<console>:13: error: overloaded method metodi needs result type
       def metodi(luku: Int) = this.metodi(luku, luku)
                                          ^
class Luokka {
  def metodi(eka: Int, toka: Int) = eka + toka
  def metodi(luku: Int): Int      = this.metodi(luku, luku)
}defined class Luokka

Tämä on nyt siis vain eräs Scalan pieni erikoisuus, ei sen keskeisempi asia. Onneksi virheilmoitus muistuttaa asiasta varsin selkeästi. Ja muista, että mille tahansa metodille saa aina kirjata palautusarvon tyypin koodiin.

Lisämateriaalia: ilmentymämuuttujia käsittelevistä metodeista

Nimeämishaaste: ilmentymämuuttuja vs. metodi

Keskenään samankaltaisia tilanteita:

  • Luvun 3.2 tilausluokassa halusimme, että kokonaishintaa voi tiedustella ulkopuolelta (metodilla kokonaishinta) mutta ei voi ulkoisesti muokata suoralla sijoituksella (muuttujaan lisattyjenHinta). Niinpä käytimme yksityisesti var-muuttujaa ja erillistä julkista metodia, joka palauttaa sen arvon.
  • Sovelsimme samaa ratkaisumallia myös FlappyBug-peliin, jossa ötökän ja esteen sijainnin muutokset tapahtuvat rajatusti luokan sisäisesti käyttämässä currentPos-muuttujassa. Luokkien käyttäjille tarjotaan julkinen pos-metodi.
  • Luvun 3.5 Match-luokassa maalimäärät kirjattiin yksityisiin muuttujiin (awayCount, homeCount). Ulkopuolelta vastaavat arvot sai selville metodeilla (awayGoals, homeGoals).
  • Ehkä teit jotakin samantapaista myös äskeisessä tehtävässä?

Yhteistä noille tilanteille on, että ulkopuolinen "saa katsoa muttei koskea" tiettyä tietoa. Luokkien laatijan on valittava kaksi nimeä, jotka molemmat kuvaavat tavallaan samaa asiaa: toinen metodille ja toinen yksityiselle muuttujalle.

Tähän ei ole mitään patenttiratkaisua, ja eri ohjelmoijat tekevät eri tavoin.

Jos kahta hyvää nimeä ei keksi, käytä parempaa julkisen metodin nimenä ja huonompaa yksityisen muuttujan nimenä. Näin asetetat etusijalle luokan käytön helppouden.

Jotkut harrastavat lyhenteitä sisäisten ilmentymämuuttujien nimissä. Tällöin pitää kuitenkin varoa, että nimien ymmärrettävyys säilyy.

Jotkut tykkäävät käyttää etuliitteitä ilmentymämuuttujien nimissä, esim. mBuyer (missä m = member eli luokan jäsen) tai alaviivallinen _buyer.

Eräillä kielillä ohjelmoidessa tyyli on usein tällainen: buyer (muuttuja) ja getBuyer (metodi). Tämä tyyli ei kuitenkaan priorisoi luokan käytön kätevyyttä. Scalassa ei ole tapana nimetä näin.

Useimmissa tämän kurssin ohjelmointitehtävissä julkiset nimet on määrätty etukäteen ja yksityiset voit keksiä itse.

Huomaa muuten, että tämä nimeämishaaste koskee nimenomaan var-muuttujia. val-muuttujat ovat yksinkertaisempia, koska val-muuttujaan ei voi sijoittaa uutta arvoa eikä tarvetta erilliselle metodille ole.

Kysyttyä: eikö Scalassa yleensä käytetä "gettereitä" ja "settereitä" kuten esimerkiksi Javassa?

Yllä oli esimerkkejä ohjelmista, joissa yksityisen var-muuttujan yhteyteen oli määritelty ns. "getteri" eli olion ominaisuuden arvon palauttava julkinen metodi. Kuitenkaan emme ole tehneet tällaisia "gettereitä" useimmille ilmentymämuuttujille.

Scala-ohjelmissa on yleistä jättää muuttujia julkisiksi, kun kyseessä on olion ulospäin näkyvä ominaisuus. "Getterit" ovat pikemminkin poikkeus kuin sääntö.

Tarkastellaan esimerkkiä:

class Profile(val name: String, var status: String) {
  // metodit tänne
}

Yllä profiilin nimestä ja statustekstistä kirjaa pitävät muuttujat ovat julkisia. Niiden arvoja voi tutkia luokan ulkopuolelta ja jälkimmäiseen voi myös sijoittaa vaikkapa käskyllä munProfiili.status = "lomalla". Jos olisimme laatineet luokan enemmän "Java-tyyliin", olisimme kirjoittaneet pidemmin:

class Profile(private val name: String, private var status: String) {

  def getName = this.name

  def getStatus = this.status

  def setStatus(status: String) = {
    this.status = status
  }

  // muut metodit tänne
}
Ilmentymämuuttujat name ja status ovat yksityisiä.
Tarjotaan erilliset metodit profiiliolioiden ominaisuuksien tutkimiseen ja muuttamiseen.

Tällaisia "gettereitä" ja "settereitä" käytetään mm. Java-ohjelmoinnissa aivan jatkuvasti. Lukemattomien tyylioppaiden mukaan julkisia ilmentymämuuttujia tulisi Javassa karttaa kuin ruttoa, heinäsirkkoja ja raemyrskyä. Tämänkin kurssin taannoisella Java-kielisellä edeltäjällä käskettiin opiskelijoita näin:

../_images/java_xi-fi.png

Vanhan liiton ohjelmointia

Miksi moinen? Ja miksei tuo mahtikäsky esimerkiksi Scalassa päde, vaan voit huoleti kirjoittaa lyhyemmin? Onko Scalassa itse asiassa sittenkin aina kulissien takana eräänlaisia "gettereitä", vaikka emme niitä määrittelekään?

Näihin aiheisiin voit tutustua nettilähteiden kautta, jos siltä tuntuu. Tässä pari linkkiä:

Lisämateriaalia: konstruktoreista ja parametreista

Konstruktorin kuormittaminen

Tuossa oli puhetta metodien kuormittamisesta. Myös konstruktoria voi kuormittaa: voit määritellä erilaisia tapoja luoda luokasta ilmentymiä käyttäen erilaisia parametreja. Tällä kurssilla tosin ei tarvitse.

Tehdään kokeeksi pieni luokka. Tarkoitus on ensinnäkin, että voimme luoda luokasta ilmentymän antaen konstruktoriparametreiksi sanan ja luvun:

new Kokeilu("kissa", 100)res0: Kokeilu = olio, jonka sana on kissa ja luku 100

Tällaisen luokanhan voi määritellä näin:

class Kokeilu(val sana: String, val luku: Int) {
  override def toString = "olio, jonka sana on " + this.sana + " ja luku " + this.luku
}

Mutta entä jos haluamme, että voimme luoda olion myös niin, että annamme vain luvun, jolloin käytetään oletussanaa "laama"? Näin:

new Kokeilu(10)res1: Kokeilu = olio, jonka sana on laama ja luku 10

Kolmantena vaihtoehtona voitaisiin antaa vain sana, jolloin käytetään oletuslukua 12345:

new Kokeilu("koira")res2: Kokeilu = olio, jonka sana on koira ja luku 12345

Vaihtoehtoiset olionluontitavat järjestyvät konstruktoria kuormittamalla, mille on Scalassa oma merkintätapansa:

class Kokeilu(val sana: String, val luku: Int) {

  def this(sana: String) = this(sana, 12345)

  def this(luku: Int) = this("laama", luku)

  override def toString = "olio, jonka sana on " + this.sana + " ja luku " + this.luku

}
Luokan alussa määritellään edelleen ensisijainen, "normaali" tapa luoda ilmentymä. Tässä siihen käytetään kahta parametria.
Merkintä def this aloittaa vaihtoehtoisen konstruktorin määrittelyn. Sen perässä olevissa sulkeissa ilmoitetaan, mitä parametreja tässä luontitavassa vastaanotetaan.
Merkintä this(...) tarkoittaa vapaasti muotoillen: "tee ilmentymä normaaliin tapaan käyttäen sulkeissa olevia tietoja konstruktoriparametreina". Esimerkiksi tässä määritellään, että jos on annettu vain sana, niin luodaan olio käyttäen tuota sanaa ja lukua 12345.

Konstruktorin kuormittaminen ei kuitenkaan ole ainoa tai välttämättä kätevin tapa lisätä joustavuutta olion luomiseen: katso seuraava laatikko.

Oletusparametriarvot

Seuraavasta luokasta voi luoda ilmentymän joko käskyllä new Kokeilu2("laama", 453534) tai pelkästään new Kokeilu2("laama").

class Kokeilu2(val sana: String, val luku: Int = 12345) {
  override def toString = "olio, jonka sana on " + this.sana + " ja luku " + this.luku
}
Jos jälkimmäistä parametria ei anneta, käytetään parametrimuuttujan yhteyteen kirjattua oletusparametriarvoa (default parameter value).

Oletusparametriarvoja voi määritellä konstruktorien lisäksi myös metodeille.

Nimetyt parametriarvot

Äsken esiteltiin lyhyesti oletusparametriarvoja, jotka ovat joskus käteviä mutta joita sinun ei ole välttämätöntä määritellä tällä kurssilla.

Samaan kategoriaan kuuluvat myös nimetyt parametriarvot.

Haastavaa luettavaa

Hakusanoilla builder pattern löytyy haastavampaa lisäluettavaa konstruktoriparametrien tiimoilta aiemmin jo ohjelmoineille.

Kiinnostavaa mutta erittäin aloittelijaepäystävällistä materiaalia löytyy artikkelista Type-Safe Builder Pattern in Scala.

Yhteenvetoa

  • Sovellus voi hyödyntää ulkoisten tahojen tarjoamia rajapintoja. Jotkin tällaiset rajapinnat eli API:t ovat tarjolla verkon yli.
    • Esimerkiksi tämän luvun sovellus käytti erään yhtiön karttapalvelua.
    • Verkkopalvelujen hyödyntämistä opetellaan kunnolla vasta jatkokursseilla.
  • Yleisille var-muuttujien muokkauskäskyille on tarjolla (mm.) Scalassa lyhennysmerkintöjä kuten += ja *=.
  • Metodinimiä voi kuormittaa: luokassa voi olla useita erillisiä metodeita, joilla on keskenään sama nimi mutta erilaiset parametriluettelot ja eri toteutukset.
    • Scalassa kuormitetulle metodille, joka kutsuu toista samannimistä, on erikseen kirjattava palautusarvon tyyppi ohjelmakoodiin.
  • Lukuun liittyviä termejä sanastosivulla: API; kuormittaa, puumerkki eli signatuuri; syntaktinen sokeri.

Palaute

Huomaathan, että tämä on henkilökohtainen osio! Vaikka olisit tehnyt lukuun liittyvät tehtävät parin kanssa, täytä palautelomake itse.

Tekijät

Tämän oppimateriaalin kehitystyössä on käytetty apuna tuhansilta opiskelijoilta kerättyä palautetta. Kiitos!

Materiaalin luvut tehtävineen ja viikkokoosteineen on laatinut Juha Sorva.

Liitesivut (sanasto, Scala-kooste, usein kysytyt kysymykset jne.) on kirjoittanut Juha Sorva sikäli kuin sivulla ei ole toisin mainittu.

Tehtävien automaattisen arvioinnin ovat toteuttaneet: (aakkosjärjestyksessä) Riku Autio, Nikolas Drosdek, Joonatan Honkamaa, Jaakko Kantojärvi, Niklas Kröger, Teemu Lehtinen, Strasdosky Otewa, Timi Seppälä, Teemu Sirkiä ja Aleksi Vartiainen.

Lukujen alkuja koristavat kuvat ja muut vastaavat kuvituskuvat on piirtänyt Christina Lassheikki.

Yksityiskohtaiset animaatiot Scala-ohjelmien suorituksen vaiheista suunnittelivat Juha Sorva ja Teemu Sirkiä. Teemu Sirkiä ja Riku Autio toteuttivat ne apunaan Teemun aiemmin rakentamat työkalut Jsvee- ja Kelmu.

Muut diagrammit ja materiaaliin upotetut vuorovaikutteiset esitykset laati Juha Sorva.

O1Library-ohjelmakirjaston ovat kehittäneet Aleksi Lukkarinen ja Juha Sorva. Useat sen keskeisistä osista tukeutuvat Aleksin SMCL-kirjastoon.

Tapa, jolla käytämme O1Libraryn työkaluja (kuten Pic) yksinkertaiseen graafiseen ohjelmointiin, on saanut vaikutteita tekijöiden Flatt, Felleisen, Findler ja Krishnamurthi oppikirjasta How to Design Programs sekä Stephen Blochin oppikirjasta Picturing Programs.

Oppimisalusta A+ luotiin alun perin Aallon LeTech-tutkimusryhmässä pitkälti opiskelijavoimin. Nykyään tätä avoimen lähdekoodin projektia kehittää Tietotekniikan laitoksen opetusteknologiatiimi ja tarjoaa palveluna laitoksen IT-tuki. Pääkehittäjänä on tällä hetkellä Markku Riekkinen, jonka lisäksi A+:aa ovat kehittäneet kymmenet Aallon opiskelijat ja muut.

A+ Courses -lisäosa, joka tukee A+:aa ja O1-kurssia IntelliJ-ohjelmointiympäristössä, on toinen avoin projekti. Sen ovat luoneet Nikolai Denissov, Olli Kiljunen, Nikolas Drosdek, Styliani Tsovou, Jaakko Närhi ja Paweł Stróżański yhteistyössä Juha Sorvan, Otto Seppälän, Arto Hellaksen ja muiden kanssa.

Kurssin tämänhetkinen henkilökunta löytyy luvusta 1.1.

Lisäkiitokset tähän lukuun

CarSim-ohjelman muokkasi Here.com:in palvelua käyttäväksi kurssin pääassari emeritus Teemu Sirkiä.

Luvussa tehdään vääryyttä The Beatlesin musiikille. Kiitos ja anteeksi.

a drop of ink
Palautusta lähetetään...