Kurssin viimeisimmän version löydät täältä: O1: 2024
Luku 2.4: Luokan koodi ja ilmentymämuuttujat
Tästä sivusta:
Pääkysymyksiä: Miten luokkia määritellään ohjelmakoodissa? Miten ohjelmakoodi määrää, mitä tapahtuu, kun luokasta luodaan uusi ilmentymä? Miten luokan ohjelmakoodia sovelletaan yksittäiselle ilmentymälle? Voiko ilmentymän toiminta poiketa luokan määrittelystä?
Mitä käsitellään? Luokan ohjelmakoodi, ja erityisesti: konstruktoriparametrien määrittelyt ja ilmentymämuuttujat. Metodien räätälöinti ilmentymäkohtaisesti. Teräsmies.
Mitä tehdään? Sekä luetaan että ohjelmoidaan.
Suuntaa antava työläysarvio:? Puolitoista tuntia? Yli? Alle? Vaarin vanhat villahousut? Luku edellyttää kaikkien aiempien asioiden osaamista. Ilmentymämuuttujien ja konstruktoriparametrien toiminta voi joka tapauksessa aiheuttaa aluksi päänvaivaa. Älä epäröi kysyä assarilta neuvoa!
Pistearvo: A65.
Kertausta
Tässä luvussa tutustutaan ohjelmakoodiin, jossa määritellään luokkia. Näin yhdistyy se, mitä tiedät yksittäisolioiden ohjelmakoodin laatimisesta (luku 2.2) siihen, mitä opit luokista ja niiden suhteesta olioihin (luku 2.3). Aloitetaan pikakertauksella niistä asioista, jotka ovat kaikkein tärkeimpiä tämän luvun ymmärtämiseksi.
Luokista ja olioista tiedetään jo:
- Luokat ovat määritelmiä niistä tietotyypeistä, joita ohjelma käsittelee. Luokka määrittelee yleiskäsitteen, josta voidaan luoda ilmentymiä; luokka voi kuvata esimerkiksi opiskelijan käsitettä.
- Luokan voi instantioida. Toisin sanoen: luokasta voi luoda ilmentymän eli olion, jolla on luokan kuvaama tietotyyppi. Tällainen olio on yksittäistapaus yleiskäsitteestä, esimerkiksi yksittäinen opiskelija.
- Kun luomme ilmentymän luokasta (Scalassa
new
-käskyllä), on usein tarpeen kirjata konstruktoriparametreja. Näitä parametreja voidaan käyttää ilmentymäkohtaisten tietojen (esim. luotavan opiskelijaolion nimen ja opiskelijanumeron) alustamiseen. - Saman luokan eri olioilla on siis keskenään eri tiedot (esim. opiskelijaolioilla voi olla eri nimet). Näillä tiedoilla on kuitenkin samat tietotyypit (esim. kunkin opiskelijaolion nimi on merkkijono).
- Samantyyppisillä olioilla on myös keskenään samat metodit. Esimerkiksi kaikilla opiskelijaolioilla on ilmoittautumismetodi, jos sellainen on niitä kuvaavaan luokkaan määritelty.
Yksittäisolioista tiedetään jo:
- Scalalla voi määritellä yksittäisolioita, joille ei erikseen laadita luokkaa.
- Yksittäisolion ohjelmakoodiin kirjoitetaan muuttujat, joiden arvoiksi kirjataan olion tietoja. Voimme myös toteuttaa oliolle metodeita.
- Metodien toteutuksessa sana
this
viittaa olioon itseensä.this.jokuMuuttuja
-tyyppisellä ilmaisulla voimme näin viitata olion metodeista olion omiin muuttujiin.
Luokka vs. yksittäisolio
Luvussa 2.2 toteutimme yksittäisolion, joka kuvasi yhtä työntekijää, ja käytimme sitä.
Toisaalta äskeisessä luvussa 2.3 käytimme luokkaa Tyontekija
, joka kuvasi työntekijöitä
yleisenä tietotyyppinä ja oli siksi yksittäisoliota hyödyllisempi.
Alla on vertailun vuoksi sekä luvun 2.2 yksittäisolion että (nyt ensimmäistä kertaa)
Tyontekija
-luokan ohjelmakoodi. Ne ovat monilta osin aivan samanlaiset, mutta myös
tärkeitä eroja löytyy.
Seuraavien kahden koodilaatikon välissä olevat vihreätaustaiset kommentit korostavat eroavat kohdat sekä niiden yllä että alla olevasta koodista, kun viet kursorin kommentin päälle.
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. " + syntynyt + "), palkka " + this.tyoaika + " * " + this.kkpalkka + " euroa"
}
Tyontekija
-ilmentymälle asetetaan tiedoiksi
parametreina vastaanotettuja arvoja. (Tästä lisää alempana.)class Tyontekija(annettuNimi: String, annettuSyntymavuosi: Int, annettuPalkka: Double) {
var nimi = annettuNimi
val syntynyt = annettuSyntymavuosi
var kkpalkka = annettuPalkka
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. " + syntynyt + "), palkka " + this.tyoaika + " * " + this.kkpalkka + " euroa"
}
Katsotaan nyt tarkemmin Tyontekija
-luokan toimintaa.
Ilmentymämuuttujat
Miten luokan ohjelmakoodi toimii, kun luomme uuden ilmentymän?
Tarkastellaan asiaa animaation avulla. Animaatio esittelee tärkeän ilmentymämuuttujan (instance variable) käsitteen.
Voit ajatella olion luomisprosessia myös aiemmin käytetyn lomakevertauksen kautta. Alla on päivitetty versio luvun 2.3 esityksestä; se on höystetty ilmentymämuuttujan käsitteellä.
Tarkennus ilmentymän luomisesta ja luokan ohjelmakoodista
Yllä mainittiin, että luokan sisään kirjoitetut käskyt suoritetaan, kun uusi ilmentymä luodaan. Esimerkiksi uuden työntekijäluokasta instantioidun olion ilmentymämuuttujille sijoitetaan tällöin (alku)arvot.
Huomaa kuitenkin, että tällä tarkoitettiin välittömästi luokkamäärittelyn rajaavien aaltosulkeiden sisään kirjoitettuja käskyjä, ei metodien määrittelyjen sisältämiä. Metodien koodi suoritetaan vain silloin, kun erikseen kyseistä metodia kutsumalla käsketään.
Esimerkiksi työntekijäluokan ilmentymää luotaessa ei suinkaan tule kutsutuksi mikään sen
metodeista ikaVuonna
, kuukausikulut
, korotaPalkkaa
tai kuvaus
, vaikka nämä
metodit on luokan ohjelmakoodin sisällä määritelty. Mainittuja metodeita voi luokan
käyttäjä määrätä suoritettaviksi new
-käskyn annettuaan, kuten aiemmissa esimerkeissä on
tehty.
Pikkutehtäviä ilmentymämuuttujista
Konstruktoriparametrit vs. ilmentymämuuttujat
Metodin parametrimuuttujat ovat olemassa vain metodin suorituksen ajan. Vastaavasti konstruktoriparametrit ovat olemassa ja niiden arvot talletettuina tietokoneen muistiin vain olion luomisprosessin ajan. Ne sijaitsevat kutsupinon kehyksessä. Tämä muistialue vapautetaan muuhun käyttöön heti, kun olio on saatu luotua.
Ilmentymämuuttujat puolestaan kuuluvat olion yhteyteen pysyvämmin. Niiden arvot säilyvät olion luomisen jälkeen ja myös metodikutsujen välillä.
Käskyt kuten val nimi = annettuNimi
siis pistävät parametreina saatuja arvoja talteen
myöhempää käyttöä varten.
Työntekijä- ja lemmikkiluokkien tapauksessa konstruktoriparametreilla ei tehty mitään muuta kuin sijoitettiin niiden arvoja suoraan ilmentymämuuttujiin. Voikin herätä kysymys, miksi oliota luotaessa edes tarvitaan erikseen muuttujia konstruktoriparametreille, jos niiden arvot vain sijoitetaan ilmentymämuuttujiin.
On järkevää, että konstruktoriparametrit voi määritellä erikseen, koska konstruktoriparametreilla voi tehdä myös muuta kuin kopioida ne ilmentymämuuttujiin; näet tästä esimerkkejä myöhemmin. Toisaalta sellaiset "kopiointisijoitukset", joita esimerkkiluokissamme esiintyy, ovat yleisiä, ja on totta, että ne olisivat ilmaistavissa yksinkertaisemminkin. Niinpä Scala tarjoaa mahdollisuuden tiivistää koodia:
Kätevämpi tapa kirjoittaa luokkia
Seuraavat kaksi versiota Tyontekija
-luokasta tekevät käytännössä täsmälleen saman
asian. Ensimmäinen on jo nähty alkuperäinen ja toinen "tiivistetty" versio.
class Tyontekija(annettuNimi: String, annettuSyntymavuosi: Int, annettuPalkka: Double) {
var nimi = annettuNimi
val syntynyt = annettuSyntymavuosi
var kkpalkka = annettuPalkka
var tyoaika = 1.0
// metodien koodi tähän
}
var
- ja val
-sanat voi kirjoittaa myös heti luokkamäärittelyn
alkuun kuten alla. Tämä tarkoittaa, että sekä edellytetään
näiden kolmen tiedon antamista konstruktoriparametreiksi, kun
Tyontekija
-tyyppistä oliota luodaan, että tallennetaan
annetut tiedot olion ilmentymämuuttujiin.annettu
-alkuisia
parametrimuuttujia ilmentymämuuttujien lisäksi. Niitä ei
tiiviissä versiossa ole lainkaan. Tiiviissä versiossa
konstruktoriparametrien nimet ovat samat kuin ilmentymämuuttujien
ja niiden arvot kopioituvat ilmentymämuuttujiin ilman erillistä
käskyä.class Tyontekija(var nimi: String, val syntynyt: Int, var kkpalkka: Double) {
var tyoaika = 1.0
// metodien koodi tähän
}
Voit halutessasi tarkastella jälkimmäisen version toimintaa myös tästä animaatiosta.
Tässä käytetty tiivimpi tapa tekee koodista lyhyemmän ja kenties helpomman lukea. Joka tapauksessa tämä tyyli on Scala-ohjelmoinnissa erittäin yleinen ja siihen on hyvä totutella heti. Tiiviimpää tyyliä tulemme käyttämään jatkuvasti.
Ilmentymän metodien toiminta
Metodikutsut toimivat luokasta instantioiduille olioille aivan samalla tavalla kuin
yksittäisolioille. Kuten luvussa 2.2 nähtiin, sanaa this
voi käyttää metodien
ohjelmakoodissa viittaamaan siihen olioon, jolle metodia on kutsuttu. Luokan ilmentymäksi
luotu olio pääsee sekin käsiksi esimerkiksi omiin ilmentymämuuttujiinsa ilmaisulla, joka
on muotoa this.muuttuja
.
Jos tämä asia tuntuu ihan selvältä, ohita surutta seuraava animaatio; siinä ei ole muuta uutta opittavaa. Muutoin katso se.
Pikkutehtävä: suorakaidetiiviste
Oletetaan, että meillä on käytössä luokka Suorakaide
, jota voi käyttää näin:
Sivumennen sanoen
Tämä käy esimerkiksi siitä, että asioita voi kuvata ohjelmissa erilaisista näkökulmista. Esimerkiksi suorakaide voi tarkoittaa eri ohjelmissa eri asioita.
Aiemmissa luvuissa olemme piirtäneet suorakaiteiden kuvia. Nyt mallinnamme suorakaidetta geometrisena käsitteenä.
val testi = new Suorakaide(10.0, 15.5)testi: o1.Suorakaide = o1.Suorakaide@152a308 testi.sivu1res0: Double = 10.0 testi.sivu2res1: Double = 15.5 testi.alares2: Double = 155.0
Suorakaideoliolta voi siis kysyä ainakin sen sivujen pituudet ja pinta-alan.
Tässä yksi tapa määritellä noin käytettävä luokka:
class Suorakaide(annettuSivunPituus: Double, annettuToinenSivunPituus: Double) {
val sivu1 = annettuSivunPituus
val sivu2 = annettuToinenSivunPituus
def ala = this.sivu1 * this.sivu2
// jne. (Täällä voisi olla muita metodeita.)
}
Tämä tapa on kuitenkin tarpeettoman monisanainen. Määrittele luokka lyhyemmin määrittelemällä ilmentymämuuttujat jo luokan otsikkorivillä kuten edellä opetettiin.
Kirjoita ratkaisusi Oliointro-moduulin tiedostoon Suorakaide.scala
, josta löytyy
annettuna tuo pidempi ohjelmakoodi. Muokkaa annettu koodi pyydetynlaiseksi.
Varmista REPLissä kokeilemalla, että uusittu luokkasi toimii esimerkin mukaisesti.
A+ esittää tässä kohdassa tehtävän palautuslomakkeen.
Pikkutehtävä: suorakaiteen kuvaus
Kokoava pikkuharjoitus: tililuokka
Lisätreeniä: suorakaiteita grafiikaksi
Kuten sanottu, yllä laadittu suorakaideluokka kuvaa eräitä suorakaiteitten matemaattisia ominaisuuksia. On toki mahdollista lisätä luokkaan myös toimintoja, joissa suorakaidetta käsitellään graafisena elementtinä.
Lisää Suorakaide
-luokkaan metodi kuvaksi
, joka:
- ottaa yhden
Color
-tyyppisen parametrin, ja - palauttaa suorakaiteen kuvan (tyyppiä
Pic
), jossa leveytenä onsivu1
, korkeutenasivu2
ja värinä metodin saama parametriarvo.
A+ esittää tässä kohdassa tehtävän palautuslomakkeen.
Äskeisessä tehtävässä väri miellettiin lisätiedoksi, joka välitetään suorakaideoliolle parametriksi aina, kun halutaan suorakaideolion tuottavan itsestään uuden kuvan.
Vaihtoehtoisesti voimme mallintaa väriä kunkin suorakaiteen ominaisuutena.
Laadi samaan Suorakaide.scala
-tiedostoon toinen luokka nimeltä
VarillinenSuorakaide
. Kopioi Suorakaide
-luokan koodi pohjaksi siihen alle ja vaihda
kopion nimi. Muokkaa sitten VarillinenSuorakaide
-luokkaa:
- Lisää kolmanneksi konstruktoriparametriksi
Color
-tyyppinen väriarvo. - Huolehdi siitä, että tuohon väriin pääsee käsiksi myös
vari
-nimisen ilmentymämuuttujan kautta. - Muokkaa metodia
kuvaksi
niin, että se on parametriton ja käyttää kyseisen suorakaiteen omaa väriä palauttamassaan kuvassa.
A+ esittää tässä kohdassa tehtävän palautuslomakkeen.
Kumpi laadituista luokista on parempi? Ero ei ole suuri, ja vastaus riippuu joka tapauksessa siitä, millaisen tai millaisten laajempien ohjelmakokonaisuuksien osaksi luokka olisi tarkoitettu.
Mielenkiintoisempi ja yleisluontoisempi kysymys on: kannattaako yleensäkään kirjoittaa mallinnettavaa käsitettä (kuten suorakaide) kuvaavaan luokkaan sellaista koodia, joka koskee ohjelman ilmiasuun liittyviä asioita (kuten värejä ja kuvia)? Palaamme tähän vielä kurssin aikana.
Monsteritehtävä
Laaditaan luokka, jolla voisi kuvata hirviöitä jossakin yksinkertaisessa, muutoin kuvitteellisessa pelissä.
Hirviöillä on hirviötyyppi ("örkki", "vampyyri", tms.) sekä kuntopisteet. Luokkaa tulisi voida käyttää seuraavan esimerkin kaltaisesti.
val otus = new Hirvio("louhikäärme", 200)otus: Hirvio = Hirvio@597f1753 otus.tyyppires3: String = louhikäärme otus.taysiKuntores4: Int = 200 otus.nykykuntores5: Int = 200 otus.kuvausres6: String = louhikäärme (200/200)
Hirviön tila muuttuu, kun se vahingoittuu vaikkapa sankarin hyökkäyksestä:
otus.vahingoitu(30)otus.nykykuntores7: Int = 170 otus.vahingoitu(40)otus.nykykuntores8: Int = 130 otus.kuvausres9: String = louhikäärme (130/200)
vahingoitu
-metodille ilmoitetaan, paljonko kuntopisteitä hirviö
menettää. Tämä vaikutuksellinen metodi vain muuttaa hirviön tilaa
eikä palauta mitään.Tutkitaan luokan toteutusta:
Odds-tehtävä (osa 1/9)
Todennäköisyyksiä (engl. odds) voidaan kuvata eri tavoin. Esimerkiksi kuusisivuista noppaa heitettäessä voimme sanoa, että kuutosen heittämiseen on "yksi kuudesta" -mahdollisuus. Eri ilmaisu samalle on "5/1", mikä kertoo, että on viisi tapaa olla heittämättä kuutosta ja yksi tapa heittää se. Toisin sanoen: on viisi kertaa niin todennäköistä, että kuutosta ei heitetä kuin että se heitetään. Vielä yksi ilmaisutapa on 16,67 %, joka voidaan myös kirjoittaa muotoon 0.1667.
Laaditaan luokka, jolla voidaan kuvata tapahtumien toteutumismahdollisuuksia ja esittää niitä eräissä muodoissa ja lopulta myös tehdä tiettyjä todennäköisyyslaskuja. Tässä tehtävässä aloitamme luokan toteuttamisen; luokkaa jatkokehitetään useassa pienessä tehtävässä myöhemmissä luvuissa.
Pohjustus: Odds
-luokan haluttu toiminnallisuus
Tarkoitus olisi, että yhtä Odds
-luokasta luotua oliota voitaisiin käyttää esimerkiksi
kuvaamaan todennäköisyyttä heittää kuutonen tai vedonlyöntitoimiston tarjoamaa arviota
Norjan todennäköisyydestä voittaa Euroviisut. Olion voisi luoda näin:
val rollingSix = new Odds(5, 1)rollingSix: Odds = o1.Odds@1c60524
Nyt rollingSix
-muuttuja sisältää viittauksen Odds
-tyyppiseen olioon, joka kuvaa
kuutosen heittämisen todennäköisyyttä. Konstruktoriparametriksi annettiin "epäonnistuvien
tapausten määrä" (5) ja "onnistuvien tapausten määrä" (1), jotka määräävät todennäköisyyden.
Voimme nyt pyytää oliolta todennäköisyystietoja erilaisissa muodoissa. Metodilla
probability
saadaan kuutosen heiton todennäköisyys eli laskutoimituksen 1.0 / (5 + 1)
tulos desimaalilukuna:
rollingSix.probabilityres10: Double = 0.16666666666666666
Metodilla fractional
todennäköisyys saadaan suhteellisessa muodossa:
rollingSix.fractionalres11: String = 5/1
Kuten näet, tämän metodin palautusarvo on merkkijono, jossa konstruktoriparametreiksi annettujen lukujen välissä on kauttamerkki.
decimal
-metodilla saadaan probability
-metodin palauttaman luvun käänteisluku,
joka kertoo, "yksi monestako" on tapahtuman todennäköisyys. Tässä tapauksessa yksi kuudesta:
rollingSix.decimalres12: Double = 6.0
Otetaan toinen esimerkki. Monet kansainväliset vedonlyöntitoimistot ilmoittavat kohteensa
murtolukuina (kuten fractional
-metodin palautusarvossa). Odds
-olio voi toimia
vedonlyöntikohteen kuvaamisessa. Oletetaan nyt vaikkapa, että vedonlyöntitoimisto tarjoaa
Norjan euroviisuvoitolle luvut 5/2, ja mallinnetaan tämä Odds
-oliona:
val norwayWin = new Odds(5, 2)norwayWin: o1.Odds = o1.Odds@1e75d66 norwayWin.probabilityres13: Double = 0.2857142857142857 norwayWin.fractionalres14: String = 5/2 norwayWin.decimalres15: Double = 3.5
Yllä näkyvä decimal
-metodin palautusarvo ilmoittaa kyseisen kohteen voittokertoimen.
Esimerkiksi 5/2-kohteesta onnekas vedonlyöjä saa panoksensa 3,5-kertaisena: rahansa
takaisin ja 2,5 kertaa panoksen verran lisää.
Suunnitellaan Odds
-luokkaamme vielä voittopotin suuruuden laskemiseenkin sopiva metodi
winnings
. Sillä voi laskea vaikkapa, paljonko voittaa, jos sijoittaa 20 rahaa Norjan
voitolle ja osuu oikeaan:
norwayWin.winnings(20.0)res16: Double = 70.0
(Tulos on siis panos 20 € kertaa decimal
-metodinkin palauttama kerroin 3.5.)
Alla oleva viimeinen esimerkkimme osoittaa, että ensimmäinen konstruktoriparametri voi tietysti mainiosti olla toista pienempikin, jos tapahtuma on todennäköinen:
val threeOutOfFive = new Odds(2, 3)threeOutOfFive: o1.Odds = o1.Odds@dfdd0c threeOutOfFive.probabilityres17: Double = 0.6 threeOutOfFive.fractionalres18: String = 2/3 threeOutOfFive.decimalres19: Double = 1.6666666666666667 threeOutOfFive.winnings(123.45)res20: Double = 205.75
Tehtävänanto
Nouda moduuli Odds ja tutustu siellä olevaan Odds
-luokan määrittelyyn. (Projektin
muuhun sisältöön ei vielä tarvitse.)
Huomaat, että luokka on vajavainen: vähän alkua löytyy, ja metodi probability
on
tehty, mutta yllä esitellyt metodit fractional
, decimal
ja winnings
puuttuvat.
Toteuta ne.
Ohjeita ja vinkkejä
- Huomaa, että sinulle on valmiiksi määritelty pari ilmentymämuuttujaa:
wont
jawill
. Ne saavat arvonsa suoraan konstruktoriparametreista (tavalla, joka esiteltiin ylempänä kohdassa Kätevämpi tapa kirjoittaa luokkia).- Voit hyödyntää näitä ilmentymämuuttujia metodeita laatiessasi.
- Et tarvitse tässä muita ilmentymämuuttujia.
probability
-metodia ei siis tarvitse muuttaa; se toimii. Voit kuitenkin katsoa, miten se on tehty.- Kunhan varmistat, että ymmärrät mitä kunkin metodin pitäisi saada aikaan, niin niiden toteuttaminen yksinkertaisia laskutoimituksia yhdistelemällä ei liene suuri homma.
- Varmista REPLissä kokeilemalla, että metodisi toimivat.
- Huomaa valita Odds-moduuli REPLiin.
- Muista REPLin uudelleenkäynnistys muutosten jälkeen.
- Palauta ratkaisusi vasta, kun olet toimintaan tyytyväinen.
fractional
-metodissa ei kuulu tehdä mitään sievennyksiä, vaan esimerkiksi konstruktoriparametrien ollessa 6 ja 2 palautetaan6/2
eikä3/1
.- Jos merkkijonon muodostamisessa tulee
vaikeuksia, muista suorakaidetehtävän
kuvaus
-metodi.
- Jos merkkijonon muodostamisessa tulee
vaikeuksia, muista suorakaidetehtävän
- Osaatko toteuttaa
decimal
jawinnings
-metodit niin, että kutsut näiden metodien sisältä jotakin toista samassa luokassa olevaa metodia? (Ei pakollista, mutta kätevää.)
A+ esittää tässä kohdassa tehtävän palautuslomakkeen.
Ilmentymän metodien räätälöintiä
Pöhköillään lopuksi vähän yhdellä esimerkkiluokalla. Pöhköillessä opitut temput tulevat käyttöön seuraavissa luvuissa.
class Henkilo(val nimi: String) {
def lausu(lause: String) = this.nimi + ": " + lause
def reagoiSrirachaan = this.lausu("Onpa hyvä kastike.")
def reagoiKryptoniittiin = this.lausu("Onpa kumma mineraali.")
}
lausu
-metodia. Henkilöolio ikään kuin lähettää
itselleen viestin: "Kun reagointimetodiani kutsutaan, komenna
this
-olio eli minut itseni suorittamaan lausu
-metodi tietyllä
parametriarvolla."Käyttöesimerkkejä:
val eka = new Henkilo("Jimmy")eka: Henkilo = Henkilo@5cf6635a eka.lausu("Super-Duper!")res21: String = Jimmy: Super-Duper! eka.reagoiSrirachaanres22: String = Jimmy: Onpa hyvä kastike. eka.reagoiKryptoniittiinres23: String = Jimmy: Onpa kumma mineraali. val toka = new Henkilo("Lois")toka: Henkilo = Henkilo@645b797d toka.reagoiKryptoniittiinres24: String = Lois: Onpa kumma mineraali.
Kaikki Henkilo
t siis pitävät srirachasta ja kummastelevat kryptoniittia, koska heidän
luokkansa niin määrää. Juuri mitään muuta he eivät osaa tehdä, esimerkiksi lentää. Mutta
samat lait eivät päde kaikkiin:
Metodin lisääminen ilmentymäkohtaisesti
val terasmies = new Henkilo("Clark") { def lenna = "WOOSH!" }terasmies: Henkilo{def lenna: String} = $anon$1@25ba32e0
Teräsmies osaa lentää mutta myös kertoa nimensä ja reagoida asioihin kuten muutkin henkilöt.
terasmies.lennares25: String = WOOSH! terasmies.nimires26: String = Clark terasmies.reagoiKryptoniittiinres27: String = Clark: Onpa kumma mineraali.
Saimme räätälöityä tietylle ilmentymälle uuden metodin. Entä luokalle yleisesti kirjoitetun metodimäärittelyn muokkaaminen ilmentymäkohtaisesti?
Metodin korvaaminen
Vihreä kryptoniitti on Teräsmiehen heikkous. Jos haluamme kuvata hänet Henkilo
-luokkaa
käyttäen, voimmeko kuitenkin huomioida tämän seikan? Meidän pitäisi siis saada
teräsmiestä kuvaava Henkilo
-olio poikkeamaan tältä osin siitä, miten muut saman luokan
ilmentymät käyttäytyvät.
val realistinenTerasmies = new Henkilo("Clark") { def lenna = "WOOSH!" override def reagoiKryptoniittiin = "GARRRRGH!" }realistinenTerasmies: Henkilo{def lenna: String} = $anon$1@47bd09ef realistinenTerasmies.reagoiKryptoniittiinres28: String = GARRRRGH!
Henkilo
-luokan jo määrittelemä metodi uusiksi
tälle yhdelle erikoisilmentymälle.override
-avainsanalla. (Ilman tulisi virheilmoitus.)Yhteenvetoa
- Luokkien ohjelmakoodiin kirjataan ne ilmentymämuuttujat ja metodit, joita luokan ilmentymillä (olioilla) halutaan olevan.
- Oliota luotaessa se saa omat kopionsa ilmentymämuuttujista, joille tallennetaan oliokohtaiset arvot.
- Koodissa määritellään myös se, mitä konstruktoriparametreja on annettava, kun uusi ilmentymä luodaan. Usein konstruktoriparametrien arvoja tallennetaan sellaisenaan uuden olion ilmentymämuuttujiin.
- On mahdollista ja silloin tällöin perusteltuakin laatia luokan tietylle ilmentymälle lisämetodeita. Voidaan myös korvata luokan määräämä toteutus ilmentymäkohtaiseksi räätälöidyllä toteutuksella.
- Lukuun liittyviä termejä sanastosivulla: luokka, ilmentymä,
ilmentymämuuttuja, instantioida, konstruktoriparametri;
this
; korvata.
Tärkeää asiaa!
Ilmentymämuuttujat ja niiden suhde konstruktoriparametreihin ovat monesti hankalanpuoleisia asioita olio-ohjelmoinnin oppimisessa. Aihe on kuitenkin luokkiin perustuvan olio-ohjelmoinnin ja tämän kurssin jatkon kannalta aivan keskeinen, ja siksi sitä on tässä luvussa selitetty eri tavoin. Jos suinkin mahdollista, jatka eteenpäin vasta sitten, kun sinusta tuntuu, että olet ymmärtänyt mainitut asiat. Kertaa esimerkkejä ja animaatioita tarpeen mukaan ja juttele assarien ja toisten opiskelijoiden kanssa.
Konstruktori-termi
Tässä luvussa puhuttiin useasti konstruktoriparametreista. Tämä termi, jota olemme käyttäneet olion luomisen yhteydessä annettavista parametreista, juontuu konstruktorin (constructor; suomeksi myös rakentaja) käsitteestä: konstruktori on aliohjelma, joka suoritetaan oliota luotaessa ja jolla alustetaan olion tila. Käskyt, joilla sijoitetaan ilmentymämuuttujille arvoja ovat konstruktoreissa tyypillisiä.
Scala-ohjelman tapauksessa konstruktorina toimivat luokkamäärittelyyn metodien ulkopuolelle kirjatut käskyt. Monissa muissa ohjelmointikielissä kirjoitetaan konstruktori erilliseksi määrittelyksi luokan sisään muiden metodien tapaan.
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 ja Nikolas Drosdek yhteistyössä Juha Sorvan, Otto Seppälän, Arto Hellaksen ja muiden kanssa.
Kurssin tämänhetkinen henkilökunta löytyy luvusta 1.1.
Joidenkin lukujen lopuissa on lukukohtaisia lisäyksiä tähän tekijäluetteloon.
object
, luokan sanallaclass
.