Luku 2.2: Olion sisällä
Johdanto
Tässä luvussa määritellään yksittäisiä olioita Scala-koodissa. Osoittautuu, että se vaatii taitoa ja tekniikkaa, jota meillä on ennestään. Olion määrittelyyn sisältyy nimittäin kahdenlaisia asioita:
Olion toiminnot kuvataan metodeina eli olioon liitettyinä funktioina. Näiden funktioiden määritteleminen onnistuu aiemmista luvuista tuttuun tapaan.
Olio tarvitsee tavan, jolla se pitää kirjaa tiedoistaan. Tähän sopii toinen vanhastaan tuttu tekniikka eli muuttujat.
Uutta seuraavassa on lähinnä se, miten funktioita ja muuttujia liitetään olioon.
Tavoite: työntekijäolio
Laaditaan yksittäisolio, joka kuvaa erästä yksittäistä työntekijää kuvitteellisessa kirjanpitojärjestelmässä.
Katsotaan ensin REPL-esimerkki siitä, miten haluaisimme tämän olion toimivan.
Työntekijällä on ominaisuuksia kuten nimi ja kuukausipalkka. Olion ohjelmakoodissa on määritelty näille ominaisuuksille arvot. Arvoja voi tiedustella oliolta tässä esitettyyn tapaan:
tyontekija.nimires0: String = Matti Mikälienen tyontekija.kkpalkkares1: Double = 5000.0
Vaikutukselliselle korotaPalkkaa
-metodille annetaan parametriksi luku, jolla vanha
palkka kerrotaan. Tässä siis annetaan 10 %:n palkankorotus:
tyontekija.korotaPalkkaa(1.1)
Todennetaan, että edellinen käsky todella muutti olion tilaa:
tyontekija.kkpalkkares2: Double = 5500.0
Vaikutukseton ikaVuonna
-metodi kertoo paluuarvollaan, kuinka monta vuotta työntekijä
täyttää annettuna vuonna. Työntekijäolio tietää oman syntymävuotensa ja osaa sen
perusteella laskea ja ilmoittaa ikänsä:
tyontekija.ikaVuonna(2024)res3: Int = 59
Työntekijäoliolle on määritelty työaika desimaalilukuna. Arvo 1.0 tarkoittaa täyspäiväisyyttä (100 %:n työaika):
tyontekija.tyoaikares4: Double = 1.0
Työaikaa voi muuttaa yksinkertaisesti sijoituskäskyllä (vrt. radioesimerkki luvusta 2.1), joka lähettää oliolle viestin: "Aseta työajaksesi 0.6." Arvo 0.6 tarkoittaa osa-aikaisuutta; Matti Mikälienen asetetaan tekemään 60-prosenttista työkuukautta:
tyontekija.tyoaika = 0.6tyontekija.tyoaika: Double = 0.6
Vaikutukseton kuukausikulut
-metodi laskee ja palauttaa työntekijän kuukausittaisen
hinnan työnantajalleen. Hinta lasketaan tässä esimerkissä kuukausipalkan (nyt 5500
euroa), työajan (nyt 60 %) ja sivukulukertoimen (parametriarvo 1.3) tulona:
tyontekija.kuukausikulut(1.3)res5: Double = 4290.0
Vaikutukseton kuvaus
-metodi palauttaa merkkijonokuvauksen eräistä työntekijäolion
keskeisistä tiedoista. Metodi on parametriton, eikä kutsussa ole sulkeita nimen kuvaus
perässä (kuten ei yllä nimeä ja kuukausipalkkaa kysyessäkään ollut). Tässä kuvaus ensin
pyydetään oliolta ja sitten tulostetaan:
println(tyontekija.kuvaus)Matti Mikälienen (s. 1965), palkka 0.6 * 5500.0 euroa
Olion ohjelmakoodi ja metodien sisäinen toiminta
Työntekijäolion toteutus: yleiskuva
Alla on työntekijäolion määrittelevä ohjelmakoodi, joka löytyy myös Oliointro-moduulin
tiedostosta tyontekija.scala
. Muodostetaan siitä ensin kokonaiskuva ja tutkitaan vasta
sitten metodien yksityiskohtia.
object tyontekija:
var nimi = "Matti Mikälienen"
val syntynyt = 1965
var kkpalkka = 5000.0
var tyoaika = 1.0
def ikaVuonna(vuosi: Int) = vuosi - this.syntynyt
def kuukausikulut(kulukerroin: Double) = this.kkpalkka * this.tyoaika * kulukerroin
def korotaPalkkaa(kerroin: Double) =
this.kkpalkka = this.kkpalkka * kerroin
def kuvaus =
this.nimi + " (s. " + this.syntynyt + "), palkka " + this.tyoaika + " * " + this.kkpalkka + " euroa"
end tyontekija
Koko olion loppuun voi vapaaehtoisesti kirjoittaa end
-alkuisen
loppumerkin, mikä onkin tapana, ellei olion koodi ole
erityisen yksinkertainen ja lyhyt. Kirjoitamme siis sen.
Alku- ja loppumerkintöjen välissä on määritelty ensin olion
muuttujat. Määrittelyt ovat tutun näköisiä. Olion muuttujille
sijoitetaan tässä (alku)arvot. Oliolle määritellyn val
-muuttujan
arvon voi vain "katsoa" ilmaisulla kuten tyontekija.syntynyt
,
mutta var
-muuttujalle voi myös sijoittaa uuden arvon kuten yllä
tehtiin REPLissä työajalle.
Alempana on määritelty metodit def
-sanalla alkaen. Scalassa ei
ole sääntöä, joka määräisi kirjoittamaan ensin muuttujat ja sitten
metodit, mutta tämä on melko yleinen tapa. Metodeista lisää alla.
Sisennykset ovat tärkeät myös olion määrittelyssä. Kukin olion sisään määrittellyistä muuttujista ja metodeista on sisennetty alku- ja loppurivejä syvemmälle. Monirivisten metodien runkoja on sisennetty vielä pykälän verran lisää tästä.
Työntekijäolion sisäinen toimintatapa
Työntekijäolion toteutus: metodien koodi selitettynä
def ikaVuonna(vuosi: Int) = vuosi - this.syntynyt
def kuukausikulut(kulukerroin: Double) = this.kkpalkka * this.tyoaika * kulukerroin
def korotaPalkkaa(kerroin: Double) =
this.kkpalkka = this.kkpalkka * kerroin
def kuvaus =
this.nimi + " (s. " + this.syntynyt + "), palkka " + this.tyoaika + " * " + this.kkpalkka + " euroa"
Scalan sana this
tarkoittaa: "se olio, jonka metodia ollaan
suorittamassa". Tai olion näkökulmasta: "minä itse". Sanaa voi
käyttää pisteen kanssa viittaamaan olion osiin. Kuten äskeisestä
animaatiosta näkyy, käytännössä this
on parametrimuuttuja,
joka toimii kuin muutkin parametrimuuttujat. Se saa arvonsa
"metodikutsusta pisteen edestä".
Tässä siis määritellään oliolle: "Kun ikaVuonna
-metodiasi
kutsutaan, vastaa luvulla, jonka saat vähentämällä parametriksi
annetusta vuodesta oman syntynyt
-muuttujasi arvon."
"Kerro keskenään oma kuukausipalkkasi, oma työaikasi ja (parametrina paikallisessa muuttujassa oleva) kulukerroin."
"Kerro oma kuukausipalkkasi annetulla kertoimella ja sijoita tulos uudeksi omaksi kuukausipalkaksesi."
Metodeissakin noudatetaan luvussa 1.7 esitettyjä funktioiden muotoilusääntöjä. Muista yhtäsuuruusmerkit.
kuvaus
-metodi ei ota parametreja, eikä sille ole edes
määritelty parametriluetteloa.
this
-sanan käytöstä tarkemmin
this
-sanaa käytetään tällä kurssilla jotakuinkin aina, kun
viitataan metodia suorittavan olion omiin jäseniin. Sana
ei kuitenkaan usein ole teknisessä mielessä pakollinen.
Joskus this
on pakollinen. Se käy ilmi tästä työntekijäolion
versiosta, jossa on aiempaan verrattuna erilaista vain yhden
muuttujan nimi:
object tyontekija:
var nimi = "Matti Mikälienen"
val vuosi = 1965
var kkpalkka = 5000.0
var tyoaika = 1.0
def ikaVuonna(vuosi: Int) = vuosi - this.vuosi
// jne.
Tässä versiossa syntymävuosi on tallennettu
muuttujaan, jonka nimi on vuosi
eikä
syntynyt
, kuten alkuperäisessä.
Olion muuttuja on siis samanniminen kuin
ikaVuonna
-metodin parametri.
Ilman this
-sanaa kirjoitettu vuosi
tarkoittaa parametrina annettua vuotta.
this
kertoo, että tässä viitataan nimenomaan
olion muuttujaan eikä parametriin. Muutoin tämä
metodi vähentäisi parametrimuuttujansa arvon
siitä itsestään ja palauttaisi siis aina nolla.
Alkuperäisessä esimerkissämme syntymävuosi oli syntynyt
-muuttujassa.
Siinä metodeista olisi voinut jättää this
-sanan pisteineen joka
kohdasta poiskin, koska mikään paikallisista muuttujista ei ollut
olion muuttujien kanssa saman niminen.
Silloinkin, kun se ei ole pakollista, this
-sanan käyttö voi olla
perusteltua. Sana korostaa lukijalle sitä, missä kohdissa käytetään
olion muuttujia ja missä paikallisia. Suosittelemme kaikille
kurssilaisille this
-sanan käyttöä aina olion muuttujiin
viitatessa, sillä se selkeyttää ohjelmia.
Kyseessä on osin makuasia. Jos haluat — ja jos tiedät mitä teet —
niin saat kyllä kurssillakin jättää ei-välttämättömät this
-sanat
pois. Kurssimme ulkopuolella on (valitettavasti?) varsin yleistä
jättää this
-sana pois kun sallittua.
Tarkkana sisennysten ja välimerkkien kanssa
Oliota määritellessäkin on välimerkit saatava kuntoon, ja sisennysten kanssa on oltava huolellinen, kun koodin rakenteet ovat sisäkkäiset. Kohta pääset kirjoittamaan itse oman olion, mutta pohjustetaan sitä vielä pienellä tehtävällä, jossa arvioit, toimivatko annetut oliomäärittelyt vai onko niissä virhe.
Kussakin kohdassa on annettu testiolio
-nimisen olion määrittely — tai ainakin
yritelmä sellaisesta. Tämä olio ei tee mitään kiinnostavaa; keskitymme tässä
muotoseikkoihin.
Testioliolla tulisi olla laskeTulos
-niminen metodi, joka ottaa luvun ja palauttaa sen
perusteella laskemansa toisen luvun näin:
testiolio.laskeTulos(100)res6: Int = 204
Sillä pitäisi olla myös monista
-niminen metodi, joka ottaa merkkijonon ja palauttaa
sen moninkertaisena:
testiolio.monista("Jee")res7: String = JeeJeeJeeJee
Vastaa kussakin kohdassa annettua koodia parhaiten kuvaava vaihtoehto. (Yhdessä kohdassa oikeita vastauksia on kaksikin. Valitse kumpi vain niistä.)
Laadi itse olio
Laadi nyt itse olio, joka kuvaa yhtä pankkitiliä. Olion on toimittava seuraavan esimerkin mukaisesti.
Tiliolio: käyttöesimerkki
Tilillä on saldo (sentteinä) ja tilinumero, jotka voi kysyä siltä:
tili.saldores8: Int = 0 tili.numerores9: String = 15903000000776FI00
Tilille voi tallettaa rahaa. Tässä kuvaamme rahasummia kokonaislukuina, jotka vastaavat eurosenttien määriä. Lisätään tilille 200 euroa ja katsotaan taas saldo:
tili.talleta(20000)tili.saldores10: Int = 20000
Negatiivisen summan tallentaminen ei muuta saldoa:
tili.talleta(-1000)tili.saldores11: Int = 20000
nosta
-metodi vähentää tililtä rahaa ja palauttaa onnistuneen vähennyksen määrän:
tili.nosta(5000)res12: Int = 5000 tili.saldores13: Int = 15000
Alle nollan ei voi vähentää. Tässä saadaan vain 150,00 euroa ja tyhjennetään tili:
tili.nosta(50000)res14: Int = 15000 tili.saldores15: Int = 0
Tehtävänanto
Määrittele siis Scalalla olio tili
, joka toimii niin kuin yllä on kuvattu ja jolla on
seuraavat piirteet:
Sillä on muuttumaton numero
"15903000000776FI00"
.Sillä on saldo, joka on aluksi nolla mutta joka voi muuttua.
Sillä on vaikutuksellinen metodi
talleta
, joka lisää parametriarvon verran rahaa tilille (kunhan parametriarvo on positiivinen) eikä palauta mitään.Sillä on vaikutuksellinen metodi
nosta
, joka vähentää tililtä parametriarvon verran, jos mahdollista, tai tyhjentää tilin, jos parametriarvo on saldoa suurempi.nosta
-metodi paitsi vähentää tililtä rahaa myös palauttaa onnistuneesti nostetetun määrän kuten REPL-esimerkissä yllä.(Tässä tehtävässä sinun ei ole pakko huomioida mahdollisuutta, että
nosta
-metodin parametriarvo olisi negatiivinen. Kuitenkin jos haluat, voit laatia metodin sellaiseksi, että se negatiivisella parametriarvolla se jättää saldon koskemattomaksi.)
Kirjoita koodi Oliointro-moduulin tiedostoon o1/yksittaisia/tili.scala
merkittyyn
kohtaan.
Toimintaohje
Suositellut työvaiheet:
Katso käyttöesimerkki yltä huolella. Huomaa muuttujien nimet ja tietotyypit! Ei
tilinumero
vaannumero
ja saldon tyypiksiInt
.Etsi
tili.scala
-tiedostosta tiliolion määrittelyn alku ja kohta, johon olion tiedot kirjoitetaan.Kirjoita muuttujien määrittelyt ja muuttujille alkuarvot. Sisennä parilla lisävälilyönnillä.
Testaa, toimiiko. Käynnistä REPL-sessio Oliointro-moduuliin ja kokeile lausekkeita
tili.saldo
jatili.numero
.Kirjoita
talleta
- janosta
-metodit.Käytä apuna aiemmista luvuista tuttuja
min
- jamax
-funktioita.nosta
-metodissa tarvittavaan algoritmiin tutustuit jo luvun 1.8sakko
-tehtävässä.
Nollaa taas REPL ja testaa metodeitasi erilaisilla parametriarvoilla. (Muistutus: voit myös käyttää REPLin vasemman yläkulman Rerun-toimintoa .)
Palauta, kun olet vakuuttunut oliosi toimivan spesifikaation mukaisesti.
A+ esittää tässä kohdassa tehtävän palautuslomakkeen.
Mitä vikaa?
Kun olet tehnyt tehtävän, voit miettiä seuraavaa.
Teitkö pankkitilioliollesi saldo
-nimisen var
-muuttujan? Jos
teit, niin hyvä niin. Kuitenkin saatoit jo tulla ajatelleeksi
pientä kummallisuutta. Tiliolion saldoahan olisi näemmä tarkoitus
muuttaa talleta
- ja nosta
-metodeilla, jotka tekevät halutut
tarkastukset: ei lisätä negatiivista määrää eikä sallita saldon
menevän alle nollan. Kuitenkaan mikään ei estä meitä komentamasta
oliota asettamaan saldonsa negatiiviseksi näin: tili.saldo = -100000
Tietenkään tämä ei haittaa mitään, jos tuollaista sijoitusta ei koskaan tehdä. Vääränlaisen sijoituksen voi tulkita oliota käyttävän ohjelmoijan virheeksi. Kuitenkin monien ohjelmoijien mielestä ohjelmat tulisi laatia niin, että tällaisten virheiden riski olisi mahdollisimman pieni. Opit pian (luku 3.2), miten ei-toivotun sijoituskäskyn voi estää, mutta ihan vielä siitä ei tarvitse välittää.
On olemassa ohjelmointityylejä, joissa ei luettaisi ongelmaksi sitä, että olio tarjoaa käyttäjälleen sopimattomiakin käskyjä. Scala-ohjelmoijat tapaavat kuitenkin kuulua siihen joukkoon, jonka mielestä tällaisten virheiden mahdollisuuden tulee minimoida, jotta ohjelmoijan työ tehostuu ja saadaan luotettavampia, ammattimaisemmin laadittuja ohjelmistoja.
Yhteenvetoa
Osa Scala-ohjelman olioista on ns. yksittäisolioita eli muista olioista irrallisina määriteltyjä olioita.
Yksittäisolion määrittely koostuu muuttujien määrittelyistä ja metodien määrittelyistä.
Olion tietoihin voi määritellä muuttujan ja sille arvon aivan aiempien lukujen mukaisesti.
Samoin metodien määrittelyt ovat aivan vastaavia kuin jo tutuksi tulleiden funktioiden.
On erittäin yleistä, että metodin toteutuksessa on tarve viitata johonkin samaisen olion (siis metodia suorittavan olion) muuttujaan tai toiseen metodiin. Tähän voi Scalassa käyttää
this
-sanaa.Lukuun liittyviä termejä sanastosivulla: olio, yksittäisolio;
this
.
Päivitetty käsitekaavio:
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.
Joidenkin lukujen lopuissa on lukukohtaisia lisäyksiä tähän tekijäluetteloon.
Yksittäisen olion määrittely alkaa sanalla
object
. Sen perään kirjoitetaan ohjelmoijan valitsema nimi, jolla olioon voi viitata. Ja sen perään kaksoispiste.