Luku 4.1: Autoiluharjoitus
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 |
---|---|
|
|
|
|
|
|
|
|
|
|
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 11.2), joten näiden merkintöjenkin merkitys on
vähäisempi. Toisaalta Scalalla voi itsekin määritellä "omia
operaattoreita" (luku 5.2).
Pikkutehtäviä
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ä
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 luontiparametrit, 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
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
silti myös drive
-metodin dokumentaation jo, jotta ymmärrät tuon keskeisen metodin
tarkoituksen?)
Valinnainen vinkki location
- ja metersDriven
-metodeihin
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
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.
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:
CarSim-ohjelma käyttää
Pos
-olionx
:ää jay
:tä kuvaamaan leveys- ja pituusasteita maapallon pyöreällä pinnalla. Kuitenkin tämä tehtävä on pedattu sinulle niin, että voit edelleen ajatellaPos
-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
Trigonometriaa ei tarvita. Peruslaskutoimitukset (kerto-, yhteen-, jako-, vähennys-) riittävät.
Lisäksi voit hyödyntää luokan Pos
metodeita. Esimerkiksi jo tuttu
xDiff
ja yDiff
voivat olla hyödyllisiä, ja muitakin dokumentaatiossa
kuvattuja voit maun mukaan ottaa avuksi.
Lisävinkkejä
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 carTest
-pikkuohjelmassa,
irrallaan varsinaisen CarSim-sovelluksen kokonaisuudesta.
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 paluuarvojen tyypit Scalassa
Luvussa 1.8 oli ensi kerran puhetta siitä, että minkä tahansa Scala-funktion koodiin voi kirjata paluuarvon 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ä paluuarvon tyyppi. Voit todeta asian itsekin seuraavasti.
Toteutitko parametrittoman fuel
-metodin kutsumalla parametrillista fuel
-metodia,
kuten vinkattiin? Jos teit niin, voit nyt kokeilla poistaa tyyppimerkinnän (eli
kaksoispisteen ja Double
-sanan) metodin alusta. Saat virheilmoituksen overloaded
method fuel needs result type eli "kuormitetulle metodille fuel
tarvitaan paluuarvon
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)-- Cyclic Error: | def metodi(luku: Int) = this.metodi(luku, luku) | ^ | Overloaded or recursive method metodi needs return type 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 paluuarvon 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 (muuttujaanlisattyjenHinta
). Niinpä käytimme yksityisestivar
-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 julkinenpos
-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ää 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
end Profile
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 muinaisella Java-kielisellä edeltäjällä käskettiin opiskelijoita näin:
Miksi moinen? Ja miksei tuo mahtikäsky esimerkiksi Scalassa päde, vaan voit huoleti kirjoittaa lyhyemmin? Onko Scalassa sittenkin 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ä:
Perusteluja Java-käytännölle: Why use getters and setters?
Scala-käytännön selitystä: Getters and Setters in Scala
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 luontiparametreja. Tällä kurssilla tosin ei tarvitse.
Tehdään kokeeksi pieni luokka. Tarkoitus on ensinnäkin, että voimme luoda luokasta ilmentymän antaen luontiparametreiksi sanan ja luvun:
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:
Kokeilu(10)res1: Kokeilu = olio, jonka sana on laama ja luku 10
Kolmantena vaihtoehtona voitaisiin antaa vain sana, jolloin käytetään oletuslukua 12345:
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
end Kokeilu
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
luontiparametreina". 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ä
Kokeilu2("laama", 453534)
tai pelkästään 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.
val
/var
-sanan pois jättäminen ja private
Tämä viimeinen lisätietolaatikko käsittelee suhteellisen vähäpätöistä nimenomaisesti Scala-kieleen liittyvää seikkaa. Lue tai ohita.
Etsitään vastaus tähän hyvään opiskelijan kysymykseen:
Aika selkee homma. Eli jos ei tee luontiparametreista ilmentymämuuttujia oliota luodessa niin ohjelma kaatuu, jos myöhemmässä vaiheessa yrittää käyttää metodia, joka yrittää käyttää luontiparametreja, eikö vain?
Vastaus ei ole ihan niin yksinkertainen kuin voisi toivoa. Siihen liittyy eräs Scalan pieni kummallisuus, johon pääsemme kiinni tämän pikkuesimerkin kautta:
class Luokka(val eka: Int, toka: Int):
val kolmas = toka * toka
def metodi = this.eka
Kyse on siitä, mitä tapahtuu, jos tuossa...
... luontiparametrin eka
edessä ei
lukisikaan val
eikä var
, mutta...
... metodi olisi silti määritelty käyttämään kyseistä muuttujaa?
Tai vastaavasti: mitä jos metodi käyttäisi
eka
-muuttujan sijaan myös toka
-muuttujaa,
jota ei ole kirjattu ilmentymämuuttujaksi
val
- tai var
-sanalla?
Kysymyksen esittänyt opiskelija ehdottaa, että tästä seuraisi ohjelman kaatuminen ajonaikaiseen virheeseen. Vielä parempi voisi olla, jos tuollainen yritys tuottaisi käännösaikaisen virheilmoituksen ennen ohjelma-ajoa. Kumpikaan noista ei kuitenkaan ole se, mitä oikeasti Scalan sääntöjen mukaan tapahtuu.
Se, mitä oikeasti tapahtuu, selviää kohta, kunhan kerrataan nämä asiat luontiparametreista ja niiden yhteyteen määritellyistä ilmentymämuuttujista:
Jos luontiparametrin alkuun kirjoittaa vain
val
/var
, sen arvo tulee sijoitetuksi ilmentymämuuttujaan. Tuo ilmentymämuuttuja on julkinen ellei toisin mainita.Jos edessä on sana
private
, niin ilmentymämuuttujaa ei voi käyttää tuon luokan ulkopuolelta mutta luokan koodin sisältä voi, käsiteltiinpä sitten mitä tahansa tuon luokan ilmentymää.
Jos luontiparametrin edestä jättää kaikki määreet (
val
,var
,private
) pois, niin tuo parametri on tarkoitettu käytettäväksi ilmentymän alustavassa koodissa (joka kirjoitetaan yleensä siihen luokan määrittelyn alkuun ennen metodeita). Esimerkiksi yllätoka
on tällainen muuttuja. Ilmanval
/var
-sanaa ilmentymämuuttuja ei tule määritellyksi. Ainakaan tavallisesti. Mutta katso esimerkki alla.
class Luokka( eka: Int, toka: Int):
val kolmas = toka * toka
def metodi = this.eka
Koodi on muuten sama kuin yllä paitsi,
että eka
n edessä ei lue val
.
Tällainen luontiparametrimuuttuja
olisi käytettävissä vain oliota luodessa eikä
jäisi ilmentymämuuttujana olion osaksi...
... mutta jos tuollaista ilman val
- tai
var
-sanaa määriteltyä parametria kuitenkin
käytetään jostakin metodista, kuten tässä,
niin tulee huomaamattomasti määritellyksi
parametrin niminen yksityinen
ilmentymämuuttuja.
Äskeinen koodi siis toimii samoin kuin jos parametrimuuttuja olisi
esitelty muodossa private val eka: Int
. O1-kurssilla emme
kirjoita ohjelmia tuohon tyyliin.
Yhteenvetoa
Sovellus voi hyödyntää ulkoisten tahojen tarjoamia rajapintoja. Jotkin tällaiset rajapinnat eli APIt 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 paluuarvon 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, Kaisa Ek, Joonatan Honkamaa, Antti Immonen, Jaakko Kantojärvi, Onni Komulainen, Niklas Kröger, Kalle Laitinen, Teemu Lehtinen, Mikael Lenander, Ilona Ma, Jaakko Nakaza, Strasdosky Otewa, Timi Seppälä, Teemu Sirkiä, Joel Toppinen, Anna Valldeoriola Cardó 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, Juha Sorva ja Jaakko Nakaza. 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; sitä 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 suunnitteluun ja toteutukseen on osallistunut useita opiskelijoita yhteistyössä O1-kurssin opettajien 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.
Ilmentymämuuttujat
name
jastatus
ovat yksityisiä.