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

Kurssin viimeisimmän version löydät täältä: O1: 2024

Luku 2.2: Olion sisällä

Tästä sivusta:

Pääkysymyksiä: Miten toteutan itse (yhden) olion Scalalla? Miten olio toimii, kun sen metodia kutsutaan?

Mitä käsitellään? Yksittäisolion ohjelmakoodi, olion muuttujat, metodien toteuttaminen, "itseen viittaaminen" eli this.

Mitä tehdään? Pieni ohjelmointiharjoitus lyhyen pohjustuksen jälkeen.

Suuntaa antava työläysarvio:? Reilu tunti.

Pistearvo: A50.

Oheisprojektit: Oliointro.

../_images/person06.png

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 kuitenkin 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 palautusarvollaan, 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(2019)res3: Int = 54

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-projektin tiedostosta yksittaisia.scala. Koetetaan muodostaa 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"

}
Yksittäisen olion määrittely alkaa sanalla object. Sen perään kirjoitetaan ohjelmoijan valitsema nimi, jolla olioon voi viitata.
Olion varsinainen määrittely kirjataan aaltosulkeiden sisään. Sulkeet ovat tässä pakolliset.
Aaltosulkeiden sisällä 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.
Huomaa asteittain kasvavat sisennykset.

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 välimerkkisääntöjä. Aaltosulut on vaikutuksettomista metodeista tapana jättää pois, jos toteutuksena on vain yksi lauseke.
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 piirteisiin. Sana ei kuitenkaan usein ole teknisessä mielessä pakollinen.

Joskus this on pakollinen, mikä 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. Ilman sitä tämä metodi vähentäisi parametrimuuttujansa arvon siitä itsestään ja palauttaisi siis aina nolla.

Alkuperäisen esimerkkimme työntekijäolion metodeista this-sanan pisteineen olisi voinut jättää joka kohdasta poiskin, koska mikään paikallisista muuttujista ei ollut olion muuttujien kanssa samanniminen.

Silloinkin, kun se ei ole pakollista, this-sanan käyttö 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 osittain 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. this-sanojen pois jättäminen sen ollessa sallittua on kurssimme ulkopuolella varsin yleistä.

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ä:

import o1._import o1._
tili.saldores6: Int = 0
tili.numerores7: String = 15903000000776FI00

Tilille voi tallentaa 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.saldores8: Int = 20000

Negatiivisen summan tallentaminen ei muuta saldoa:

tili.talleta(-1000)tili.saldores9: Int = 20000

nosta-metodi vähentää tililtä rahaa ja palauttaa onnistuneen vähennyksen määrän:

tili.nosta(5000)res10: Int = 5000

Alle nollan ei voi vähentää. Tässä saadaan vain 150,00 euroa ja tyhjennetään tili:

tili.nosta(50000)res11: Int = 15000
tili.saldores12: 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 metodi talleta, joka lisää parametriarvon verran rahaa tilille (kunhan parametriarvo on positiivinen) eikä palauta mitään.
  • Sillä on metodi nosta, joka vähentää tililtä parametriarvon verran, jos mahdollista, tai tyhjentää tilin, jos parametriarvo on saldoa suurempi. Onnistuneesti nostettu määrä palautetaan.
  • (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-projektin tiedostoon o1/yksittaisia.scala sille merkittyyn kohtaan.

Toimintaohje

Kannattaa edetä seuraavasti:

  1. Katso käyttöesimerkki yltä huolella. Huomaa muuttujien nimet ja tietotyypit! Ei tilinumero vaan numero ja saldon tyypiksi Int.
  2. Etsi yksittaisia.scala-tiedostosta tiliolion määrittelyn alku ja aaltosulkeet, joiden väliin olion varsinainen määritelmä kirjoitetaan.
  3. Kirjoita muuttujien määrittelyt ja laita niille alkuarvot. Sisennä parilla lisävälilyönnillä.
  4. Testaa, toimiiko, Käynnistä REPL-sessio Oliointro-projektille, anna import-käsky ja kokeile lausekkeita tili.saldo ja tili.numero.
  5. Kirjoita talleta- ja nosta-metodit.
    • Käytä apuna aiemmista luvuista tuttuja min- ja max-funktioita.
    • nosta-metodissa tarvittavaan algoritmiin tutustuit jo luvun 1.8 tehtävässä.
  6. Nollaa taas REPL ja testaa metodeitasi erilaisilla parametriarvoilla. (Muistutus: voit myös käyttää REPLin Relaunch Interpreter and Replay History -toimintoa.)
  7. 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 tekeminen on mahdollista tulkita oliota käyttävän ohjelmoijan virheeksi. Kuitenkin monien ohjelmoijien mielestä ohjelmat tulisi laatia siten, 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, joista tällaisten virheiden mahdollisuuden tulee minimoida, jotta ohjelmoijan työ tehostuu ja saadaan luotua luotettavampia 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!

Kierrokset 1–13 ja niihin liittyvät tehtävät ja viikkokoosteet on laatinut Juha Sorva.

Kierrokset 14–20 on laatinut Otto Seppälä. Ne eivät ole julki syksyllä, mutta julkaistaan ennen kuin määräajat lähestyvät.

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 ovat suunnitelleet Juha Sorva ja Teemu Sirkiä. Niiden teknisen toteutuksen ovat tehneet Teemu Sirkiä ja Riku Autio käyttäen Teemun toteuttamia Jsvee- ja Kelmu-työkaluja.

Muut diagrammit ja materiaaliin upotetut vuorovaikutteiset esitykset on laatinut Juha Sorva.

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

Opetustapa, jossa 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+ on luotu Aallon LeTech-tutkimusryhmässä pitkälti opiskelijavoimin. Pääkehittäjänä toimii tällä hetkellä Jaakko Kantojärvi, jonka lisäksi järjestelmää kehittävät useat tietotekniikan ja informaatioverkostojen opiskelijat.

Kurssin tämänhetkinen henkilökunta on kerrottu luvussa 1.1.

Joidenkin lukujen lopuissa on lukukohtaisia lisäyksiä tähän tekijäluetteloon.

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