Osa 4

Johdatus olio-ohjelmointiin

Aloitamme nyt matkan olio-ohjelmoinnin pariin. Aluksi keskiössä on käsitteiden ja tiedon kuvaaminen luokkien ja olioiden avulla, jonka jälkeen tutustumme toiminnallisuuden eli metodien lisäämiseen.

Olio-ohjelmoinnissa on kyse ratkaistavassa ongelmassa esiintyvien käsitteiden eristämisestä omiksi kokonaisuuksikseen sekä näiden kokonaisuuksien käyttämistä ongelman ratkaisemisessa. Kun ongelmaan liittyvät käsitteet on tunnistettu, niistä voidaan myös keskustella. Toisin ajatellen, ratkaistavasta ongelmasta muodostetaan abstraktioita, joiden avulla ongelmaa on helpompi käsitellä.

Kun ongelmasta tunnistetaan käsitteitä, voidaan niitä vastaavia rakenteita luoda myös ohjelmaan. Näitä rakenteita ja niistä luotavia yksittäisiä ilmentymiä eli olioita käytetään ongelman ratkaisemisessa. Nyt ehkä käsittämättömältä tuntuva lausahdus ohjelma rakennetaan pienistä selkeistä yhteistoiminnassa olevista olioista alkaa hiljalleen kurssin edetessä tuntua järkeenkäyvältä ja jopa itsestäänselvältä.

Luokka ja Olio

Olemme käyttäneet jo joitakin Javan tarjoamia luokkia ja olioita. Luokka määrittelee olioiden ominaisuudet eli niihin liittyvät tiedot eli oliomuuttujat ja niiden tarjoamat komennot eli metodit. Oliomuuttujien arvot määrittelevät yksittäisen olion sisäisen tilan ja metodit taas olion tarjoamat toiminnallisuudet. Olio luodaan luokkaan kirjoitetun määrittelyn perusteella. Samasta luokasta voidaan luoda useampia olioita, joilla jokaisella on eri tila eli jokaisella on omat oliomuuttujien arvot. Jokaisella oliolla on myös metodit, jotka olion luomiseen käytetyssä luokassa on määritelty.

Metodi on luokkaan kirjoitettu lähdekoodista koostuva kokonaisuus, jolle on annettu nimi, ja jota voidaan kutsua. Metodi liittyy aina tiettyyn luokkaan, ja sitä käytetään usein luokasta tehdyn olion sisäisen tilan muokkaamiseen.

Esimerkiksi ArrayList on Javan tarjoama luokka, josta luotuja olioita olemme hyödyntäneet ohjelmissamme. Alla ohjelmassa luodaan ArrayList-olio nimeltä luvut, johon lisätään kokonaislukuja.

// luodaan ArrayList-luokasta olio, jonka nimeksi tulee luvut
ArrayList<Integer> luvut = new ArrayList<>();

// lisätään luvut-olioon arvot 15, 34, 65, 111
luvut.add(15);
luvut.add(34);
luvut.add(65);
luvut.add(111);

// tulostetaan luvut-olion koko
System.out.println(luvut.size());

Luokasta luodaan olio aina kutsumalla olion luovaa metodia eli konstruktoria komennon new avulla. Esimerkiksi yllä ArrayList-luokasta luodaan uusi kokonaislukuja hyväksyvä ilmentymä eli olio kun kutsutaan new ArrayList<Integer>(). Konstruktorit voivat saada parametreja kuten muutkin metodit.

Loading
Loading

Luokan luominen

Luokka määrittelee minkälaisia luokasta luotavat oliot ovat:

  • olion muuttujat määrittelevät minkälainen olion sisäinen tila on
  • olion metodit määrittelevät mitä toiminnallisuuksia olio tarjoaa

Tutustutaan nyt oman luokan luomiseen sekä luokkaan liittyvien oliomuuttujien määrittelyyn.

Luokka määritellään kuvaamaan jotain mielekästä kokonaisuutta. Usein "mielekäs kokonaisuus" kuvaa jotain reaalimaailman asiaa tai käsitettä. Jos tietokoneohjelman pitää käsitellä henkilötietoja, voisi olla mielekästä määritellä erillinen luokka Henkilo joka kokoaa yhteen henkilöön liittyvät metodit ja ominaisuudet.

Aloitetaan. Oletetaan että meillä on projektirunko jossa on tyhjä pääohjelma:

public class Main {

    public static void main(String[] args) {

    }
}

Luodaan luokka nimeltä Henkilo. Luokkaa varten luodaan erillinen tiedosto nimeltä Henkilo.java. Ohjelmamme koostuu nyt siis kahdesta erillisestä tiedostosta, sillä myös pääohjelma on omassa tiedostossaan. Aluksi Henkilo.java -tiedosto sisältää luokan määrittelyn public class Henkilo sekä luokan sisällön rajaavat aaltosulut.

public class Henkilo {

}

NetBeansissa uuden tiedoston luomisen jälkeinen tilanne näyttää seuraavalta. Alla olevassa kuvassa Hiekkalaatikkotehtävään on lisätty luokka Henkilo.

luokka luotuna

Luokkaa kuvaamaan voi piirtää myös luokkakaavion, jonka merkintätekniikkaan tutustutaan tässä samalla. Henkilo-niminen luokka, jossa ei ole mitään sisällä näyttää seuraavalta:

luokkakaavio henkilo

Luokka määrittelee luokasta luotavien olioiden ominaisuudet ja toiminnallisuudet. Päätetään, että jokaisella henkilöoliolla on nimi ja ikä. Nimi on luonnollista esittää merkkijonona, eli Stringinä, ja ikä taas kokonaislukuna. Lisätään nämä rakennuspiirustuksiimme:

public class Henkilo {
    private String nimi;
    private int ika;
}

Määrittelemme yllä että jokaisella Henkilo-luokasta luotavalla oliolla on nimi ja ika. Luokan sisälle määriteltyjä muuttujia kutsutaan oliomuuttujiksi tai olion kentiksi tai olion attribuuteiksi. Muitakin nimiä tuntuu löytyvän.

Oliomuuttujat kirjoitetaan luokan määrittelyä public class Henkilo { seuraaville riveille. Jokaisen muuttujan eteen asetetaan avainsana private. Avainsana private tarkoittaa sitä, että muuttujat ovat "piilossa" olion sisällä. Tätä kutsutaan kapseloinniksi.

Luokkaakaaviossa luokkaan liittyvät muuttujat määritellään muodossa "muuttujanNimi: muuttujanTyyppi". Miinusmerkki ennen muuttujan nimeä kertoo, että muuttuja on kapseloitu (sillä on avainsana private).

luokkakaavio henkilo ika ja nimi

Olemme nyt määritelleet rakennuspiirustukset — luokan — henkilöoliolle. Jokaisella uudella henkilöolioilla on muuttujat nimi ja ika, joissa voi olla oliokohtainen arvo. Henkilöiden "tila" koostuu niiden nimeen ja ikään asetetuista arvoista.

Loading

Konstruktorin määrittely

Luotavalle oliolle halutaan asettaa alkutila. Itse määritellyn olion luominen tapahtuu hyvin samaan tapaan kuin olioiden luominen Javan valmiista luokista kuten ArrayLististä. Olio luodaan new-komennolla. Olion luomisen yhteydessä on kätevää pystyä antamaan arvot luotavan olion muuttujille. Esimerkiksi uutta henkilö-oliota luotaessa olisi kätevää pystyä antamaan oliolle nimi:

public static void main(String[] args) {
    Henkilo ada = new Henkilo("Ada");
    // ...
}

Tämä onnistuu määrittelemällä olion luova metodi eli konstruktori. Konstruktori määritellään oliomuuttujien jälkeen. Seuraavassa esimerkissä Henkilo-luokalle on määritelty konstruktori, jota voidaan käyttää uuden Henkilo-olion luomiseen. Konstruktori asettaa luotavan olion iäksi 0 ja nimeksi konstruktorin parametrina annettavan merkkijonon:

public class Henkilo {
    private String nimi;
    private int ika;

    public Henkilo(String nimiAlussa) {
        this.ika = 0;
        this.nimi = nimiAlussa;
    }
}

Konstruktorin nimi on aina sama kuin luokan nimi. Yllä luokka (class) on Henkilo, joten konstruktorin nimeksi tulee Henkilo. Konstruktorille annetaan lisäksi parametrina luotavan henkilööolion nimi. Parametri asetetaan sulkuihin konstruktorin nimen perään. Parametreja mahdollisesti sisältävien sulkujen jälkeen tulee aaltosulut, joiden sisälle määritellään lähdekoodi, jonka ohjelma suorittaa konstruktorikutsun (esim. new Henkilo("Ada")) yhteydessä.

Oliot luodaan aina konstruktorin avulla.

Muutama huomio: konstruktorin sisällä on lauseke this.ika = 0. Lausekkeessa asetetaan juuri luotavan olion (eli "tämän" olion) oliomuuttujan ika arvoksi 0. Toinen lauseke this.nimi = nimiAlussa; taas asettaa juuri tämän olion sisäiselle muuttujalle nimi arvoksi parametrina annetun merkkijonon.

Koska oliomuuttujat on määritelty konstruktorin aaltosulkujen ulkopuolella, voi niitä käyttää myös konstruktorin sisällä.

Nyt luokkakaavioon on merkitty luokan nimen ja muuttujien lisäksi myös konstruktori. Konstruktori saa public-näkyvyysmääreen takia eteen plussan, jonka lisäksi siitä merkitään sen nimi ja parametrin tyypit (tässä + Henkilo(String)).

luokkakaavio henkilo ika ja nimi ja konstruktori
Loading

Metodien määrittely olioille

Osaamme luoda olion ja alustaa olion muuttujat. Toimintaan pystyäkseen olioilla on oltava myös metodeja. Kuten olemme jo oppineet, metodi on luokkaan kirjoitettu lähdekoodista koostuva kokonaisuus, jolle on annettu nimi, ja jota voidaan kutsua.

Tehdään luokalle Henkilo metodi, jota käytetään olion tietojen tulostamiseen.

public class Henkilo {
    private String nimi;
    private int ika;

    public Henkilo(String nimiAlussa) {
        this.ika = 0;
        this.nimi = nimiAlussa;
    }

    public void tulostaHenkilo() {
        System.out.println(this.nimi + ", ikä " + this.ika + " vuotta");
    }
}

Metodi kirjoitetaan luokan sisälle konstruktorin alapuolelle. Metodin nimen eteen tulee public void sillä metodin on tarkoitus näkyä ulkomaailmalle (public) ja metodi ei palauta arvoa (void).

Luokkakaavioon on merkitty luokan nimen, oliomuuttujien ja konstruktorin lisäksi nyt myös metodi tulostaHenkilo. Koska metodilla on public-määre, tulee sille alkuun plus, jota seuraa metodin nimi. Metodille ei ole määritelty parametreja, joten ei myöskään piirretä metodin sulkujen sisälle. Metodille merkitään myös tieto siitä, että se ei palauta arvoa, tässä void.

luokkakaavio henkilo ika ja nimi ja konstruktori ja tulosta

Metodin tulostaHenkilo sisällä on yksi koodirivi joka käyttää hyväkseen oliomuuttujia nimi ja ika — luokkakaavio ei kerro sisäisestä toteutuksesta. Olion sisäisiin muuttujiin viitataan etuliitteellä this. Kaikki olion muuttujat ovat siis näkyvillä ja käytettävissä metodin sisällä.

Luodaan pääohjelmassa kolme henkilöä ja pyydetään niitä tulostamaan itsensä:

public class Main {

    public static void main(String[] args) {
        Henkilo ada = new Henkilo("Ada");
        Henkilo antti = new Henkilo("Antti");
        Henkilo martin = new Henkilo("Martin");

        ada.tulostaHenkilo();
        antti.tulostaHenkilo();
        martin.tulostaHenkilo();
    }
}

Tulostuu:

Esimerkkitulostus

Ada, ikä 0 vuotta Antti, ikä 0 vuotta Martin, ikä 0 vuotta

Sama screencastina:

 

Loading
Loading
Loading

Oliomuuttujan arvon muuttaminen metodissa

Lisätään aiemmin rakentamallemme Henkilo-luokalle metodi, joka kasvattaa henkilön ikää vuodella:

public class Henkilo {
    private String nimi;
    private int ika;

    public Henkilo(String nimiAlussa) {
        this.ika = 0;
        this.nimi = nimiAlussa;
    }

    public void tulostaHenkilo() {
        System.out.println(this.nimi + ", ikä " + this.ika + " vuotta");
    }

    // lisätty metodi vanhene
    public void vanhene() {
        this.ika = this.ika + 1;
    }
}

Metodi kirjoitetaan tulostaHenkilo-metodin tapaan luokan Henkilo sisälle. Metodissa kasvatetaan oliomuuttujan ika arvoa yhdellä.

Myös luokkakaavio päivittyy.

[Henkilo|-nimi:String;-ika:int|+Henkilo(String);+tulostaHenkilo():void;+vanhene():void]

Kutsutaan metodia ja katsotaan mitä tapahtuu:

public class Main {

    public static void main(String[] args) {
        Henkilo ada = new Henkilo("Ada");
        Henkilo antti = new Henkilo("Antti");

        ada.tulostaHenkilo();
        antti.tulostaHenkilo();

        System.out.println("");

        ada.vanhene();
        ada.vanhene();

        ada.tulostaHenkilo();
        antti.tulostaHenkilo();
    }
}

Ohjelman tulostus on seuraava:

Esimerkkitulostus

Ada, ikä 0 vuotta Antti, ikä 0 vuotta

Ada, ikä 2 vuotta Antti, ikä 0 vuotta

Eli "syntyessään" molemmat oliot ovat nollavuotiaita (konstruktorissa suoritetaan mm. rivi this.ika = 0;). Olion ada metodia vanhene kutsutaan kaksi kertaa. Kuten tulostus näyttää, tämä saa aikaan sen että Adan ikä on vanhenemisen jälkeen 2 vuotta. Kutsumalla metodia Adaa vastaavalle oliolle, toisen henkilöolion ikä ei muutu, sillä jokaiselle luokasta luotavalle oliolle luodaan myös omat oliomuuttujat.

Metodin sisään voi lisätä myös ehto- ja toistolauseita. Alla olevaa vanhene-metodia käytettäessä kenestäkään ei tulisi yli 30-vuotiasta.

public class Henkilo {
    private String nimi;
    private int ika;

    public Henkilo(String nimiAlussa) {
        this.ika = 0;
        this.nimi = nimiAlussa;
    }

    public void tulostaHenkilo() {
        System.out.println(this.nimi + ", ikä " + this.ika + " vuotta");
    }

    // kukaan ei tule olemaan yli 30-vuotias
    public void vanhene() {
        if (this.ika < 30) {
            this.ika = this.ika + 1;
        }
    }
}
Loading
Loading

Arvon palauttaminen metodista

Metodi voi palauttaa arvon. Tähän mennessä olioihin luomamme metodit eivät palauttaneet mitään. Tämä on merkitty kirjoittamalla metodin määrittelyyn avainsana void.

public class Ovi {
    public void koputa() {
        // ...
    }
}

Avainsana void tarkoittaa että metodi ei palauta arvoa.

Jos haluamme, että metodi palauttaa arvon, tulee avainsanan void paikalle asettaa palautettavan muuttujan tyyppi. Seuraavassa esimerkissä näkyvälle luokalle Opettaja on määritelty metodi arvostele, joka palauttaa aina kokonaislukutyyppisen (int) muuttujan (tässä arvo 10). Arvon palauttaminen tapahtuu aina komennolla return:

public class Opettaja {
    public int arvostele() {
        return 10;
    }
}

Ylläoleva metodi siis palauttaa sitä kutsuttaessa int-tyyppisen arvon 10. Jotta metodin palauttamaa arvoa voisi käyttää, tulee se ottaa talteen muuttujaan. Tämä tapahtuu samalla tavalla kuin normaali muuttujan arvon asetus, eli yhtäsuuruusmerkillä:

public static void main(String[] args) {
    Opettaja opettaja = new Opettaja();

    int arvostelu = opettaja.arvostele();

    System.out.println("Arvosanaksi tuli " + arvostelu);
}
Esimerkkitulostus

Arvosanaksi tuli 10

Metodin paluuarvo sijoitetaan int-tyyppiseen muuttujaan aivan kuin mikä tahansa muukin int-arvo. Paluuarvo voi toimia myös osana mitä tahansa lauseketta:

public static void main(String[] args) {
    Opettaja eka = new Opettaja();
    Opettaja toka = new Opettaja();
    Opettaja kolmas = new Opettaja();

    double keskiarvo = (eka.arvostele() + toka.arvostele() + kolmas.arvostele()) / 3.0;

    System.out.println("Arvostelujen keskiarvo " + keskiarvo);
}
Esimerkkitulostus

Arvostelujen keskiarvo 10.0

Kaikki tähän mennessä näkemämme muuttujatyypit voidaan myös palauttaa metodista. Yhteenveto:

  • Metodilla, joka ei palauta mitään, on void-määre palautettavan muuttujan tyyppinä.
public void metodiJokaEiPalautaMitaan() {
    // metodin runko
}
  • Metodilla, joka palauttaa kokonaislukutyyppisen muuttujan, on int-määre palautettavan muuttujan tyyppinä.
public int metodiJokaPalauttaaKokonaisLuvun() {
    // metodin runko, tarvitsee return-komennon
}
  • Metodilla, joka palauttaa merkkijonotyyppisen muuttujan, on String-määre palautettavan muuttujan tyyppinä.
public String metodiJokaPalauttaaTekstin() {
    // metodin runko, tarvitsee return-komennon
}
  • Metodilla, joka palauttaa liukulukutyyppisen muuttujan, on double-määre palautettavan muuttujan tyyppinä.
public double metodiJokaPalauttaaLiukuluvun() {
    // metodin runko, tarvitsee return-komennon
}

Jatketaan nyt henkilön parissa ja lisätään henkilölle iän palauttava metodi palautaIka.


public class Henkilo {
    private String nimi;
    private int ika;

    public Henkilo(String nimiAlussa) {
        this.ika = 0;
        this.nimi = nimiAlussa;
    }

    public void tulostaHenkilo() {
        System.out.println(this.nimi + ", ikä " + this.ika + " vuotta");
    }

    public void vanhene() {
        if (this.ika < 30) {
            this.ika = this.ika + 1;
        }
    }

    // juuri lisätty metodi
    public int palautaIka() {
        return this.ika;
    }
}

Luokka kokonaisuudessaan:

[Henkilo|-nimi:String;-ika:int|+Henkilo(String);+tulostaHenkilo():void;+vanhene():void;+palautaIka():int]

Havainnollistetaan metodin toimintaa:

public class Main {

    public static void main(String[] args) {
        Henkilo pekka = new Henkilo("Pekka");
        Henkilo antti = new Henkilo("Antti");

        pekka.vanhene();
        pekka.vanhene();

        antti.vanhene();

        System.out.println("Pekan ikä: " + pekka.palautaIka());
        System.out.println("Antin ikä: " + antti.palautaIka());

        int yht = pekka.palautaIka() + antti.palautaIka();

        System.out.println("Pekka ja Antti yhteensä " + yht + " vuotta");
    }
}
Esimerkkitulostus

Pekan ikä 2 Antin ikä 1

Pekka ja Antti yhteensä 3 vuotta

Loading
Loading

Kuten aiemmin huomasimme, metodit voivat sisältää lähdekoodia aivan samalla tavalla kuin muutkin ohjelmamme osat. Metodeissa voi olla ehtolauseita tai toistolauseita, ja metodeista voi kutsua myös muita metodeja.

Tehdään seuraavaksi henkilölle metodi, jonka avulla voidaan selvittää onko henkilö täysi-ikäinen. Metodi palauttaa totuusarvon — joko true tai false:

public class Henkilo {
    // ...

    public boolean taysiIkainen() {
        if (this.ika < 18) {
            return false;
        }

        return true;
    }

    /*
    huom. metodin voisi kirjoittaa lyhyemmin seuraavasti:

    public boolean taysiIkainen() {
        return this.ika >= 18;
    }
    */
}

Ja testataan:

public static void main(String[] args) {
    Henkilo pekka = new Henkilo("Pekka");
    Henkilo antti = new Henkilo("Antti");

    int i = 0;
    while (i < 30) {
        pekka.vanhene();
        i = i + 1;
    }

    antti.vanhene();

    System.out.println("");

    if (antti.taysiIkainen()) {
        System.out.print("täysi-ikäinen: ");
        antti.tulostaHenkilo();
    } else {
        System.out.print("alaikäinen: ");
        antti.tulostaHenkilo();
    }

    if (pekka.taysiIkainen()) {
        System.out.print("täysi-ikäinen: ");
        pekka.tulostaHenkilo();
    } else {
        System.out.print("alaikäinen: ");
        pekka.tulostaHenkilo();
    }
}
Esimerkkitulostus

alaikäinen: Antti, ikä 1 vuotta täysi-ikäinen: Pekka, ikä 30 vuotta

Viritellään ratkaisua vielä hiukan. Nyt henkilön pystyy "tulostamaan" ainoastaan siten, että nimen lisäksi tulostuu ikä. On tilanteita, joissa haluamme tietoon pelkän olion nimen. Eli tehdään tarkoitusta varten oma metodi:

public class Henkilo {
    // ...

    public String getNimi() {
        return this.nimi;
    }
}

Metodi getNimi palauttaa oliomuuttujan nimi kutsujalle. Metodin nimi on hieman erikoinen. Javassa on usein tapana nimetä oliomuuttujan palauttava metodi juuri näin, eli getMuuttujanNimi. Tälläisiä metodeja kutsutaan usein "gettereiksi".

Luokka kokonaisuudessaan:

[Henkilo|-nimi:String;-ika:int|+Henkilo(String);+tulostaHenkilo():void;+vanhene():void;+palautaIka():int;+taysiIkainen():boolean;+getNimi():String]

Muotoillaan pääohjelma käyttämään uutta "getteri"-metodia:

public static void main(String[] args) {
    Henkilo pekka = new Henkilo("Pekka");
    Henkilo antti = new Henkilo("Antti");

    int i = 0;
    while (i < 30) {
        pekka.vanhene();
        i = i + 1;
    }

    antti.vanhene();

    System.out.println("");

    if (antti.taysiIkainen()) {
        System.out.println(antti.getNimi() + " on täysi-ikäinen");
    } else {
        System.out.println(antti.getNimi() + " on alaikäinen");
    }

    if (pekka.taysiIkainen()) {
        System.out.println(pekka.getNimi() + " on täysi-ikäinen");
    } else {
        System.out.println(pekka.getNimi() + " on alaikäinen ");
    }
}

Tulostus alkaa olla jo aika siisti:

Esimerkkitulostus

Antti on alaikäinen Pekka on täysi-ikäinen

Loading

Olion merkkijonoesitys ja toString-metodi

Olemme syyllistyneet osittain huonoon ohjelmointityyliin tekemällä metodin jonka avulla olio tulostetaan, eli metodin tulostaHenkilo. Suositeltavampi tapa on määritellä oliolle metodi jonka palauttaa olion "merkkijonoesityksen". Merkkijonoesityksen palauttavan metodin nimi on Javassa aina toString. Määritellään seuraavassa henkilölle tämä metodi:

public class Henkilo {
    // ...

    public String toString() {
        return this.nimi + ", ikä " + this.ika + " vuotta";
    }
}

Metodi toString toimii kuten tulostaHenkilo, mutta se ei itse tulosta mitään vaan palauttaa merkkijonoesityksen, jota metodin kutsuja voi halutessaan suorittaa tulostamisen.

Metodia käytetään hieman yllättävällä tavalla:

public static void main(String[] args) {
    Henkilo pekka = new Henkilo("Pekka");
    Henkilo antti = new Henkilo("Antti");

    int i = 0;
    while (i < 30) {
        pekka.vanhene();
        i = i + 1;
    }

    antti.vanhene();

    System.out.println(antti); // sama kun System.out.println(antti.toString());
    System.out.println(pekka); // sama kun System.out.println(pekka.toString());
}

Periaatteena on, että System.out.println-metodi pyytää olion merkkijonoesityksen ja tulostaa sen. Merkkijonoesityksen palauttavan toString-metodin kutsua ei tarvitse kirjoittaa itse, sillä Java lisää sen automaattisesti. Ohjelmoijan kirjoittaessa:

System.out.println(antti);

Java täydentää suorituksen aikana kutsun muotoon:

System.out.println(antti.toString());

Kutsu System.out.println(antti) kutsuu siis antti-olion toString-metodia ja tulostaa sen palauttaman merkkijonon.

Voimme nyt poistaa luokasta Henkilo turhaksi käyneen tulostaHenkilo-metodin.

Olioscreencastin toinen osa:

 

Loading

Metodin parametrit

Jatketaan taas Henkilo-luokan parissa. Päätetään että haluamme laskea henkilöiden painoindeksejä. Tätä varten teemme henkilölle metodit pituuden ja painon asettamista varten, sekä metodin joka laskee painoindeksin. Henkilön uudet ja muuttuneet osat seuraavassa:

public class Henkilo {
    private String nimi;
    private int ika;
    private int paino;
    private int pituus;

    public Henkilo(String nimiAlussa) {
        this.ika = 0;
        this.paino = 0;
        this.pituus = 0;
        this.nimi = nimiAlussa;
    }

    public void setPituus(int uusiPituus) {
        this.pituus = uusiPituus;
    }

    public void setPaino(int uusiPaino) {
        this.paino = uusiPaino;
    }

    public double painoindeksi() {
        double pituusPerSata = this.pituus / 100.0;
        return this.paino / (pituusPerSata * pituusPerSata);
    }

    // ...
}

Eli henkilölle lisättiin oliomuuttujat pituus ja paino. Näille voi asettaa arvon metodeilla setPituus ja setPaino. Jälleen käytössä Javaan vakiintunut nimeämiskäytäntö, eli jos metodin tehtävänä on ainoastaan asettaa arvo oliomuuttujaan, on metodi tapana nimetä setMuuttujanNimi:ksi. Arvon asettavia metodeja kutsutaan usein "settereiksi". Seuraavassa käytämme uusia metodeja:

public static void main(String[] args) {
    Henkilo matti = new Henkilo("Matti");
    Henkilo juhana = new Henkilo("Juhana");

    matti.setPituus(180);
    matti.setPaino(86);

    juhana.setPituus(175);
    juhana.setPaino(64);

    System.out.println(matti.getNimi() + ", painoindeksisi on " + matti.painoindeksi());
    System.out.println(juhana.getNimi() + ", painoindeksisi on " + juhana.painoindeksi());
}

Tulostus:

Esimerkkitulostus

Matti, painoindeksisi on 26.54320987654321 Juhana, painoindeksisi on 20.897959183673468

Parametrilla ja oliomuuttujalla sama nimi!

Edellä metodissa setPituus asetetaan oliomuuttujaan pituus parametrin uusiPituus arvo:

public void setPituus(int uusiPituus) {
    this.pituus = uusiPituus;
}

Parametrin nimi voisi olla myös sama kuin oliomuuttujan nimi, eli seuraava toimisi myös:

public void setPituus(int pituus) {
    this.pituus = pituus;
}

Nyt metodissa pituus tarkottaa nimenomaan pituus-nimistä parametria ja this.pituus saman nimistä oliomuuttujaa. Esim. seuraava ei toimisi sillä koodi ei viittaa ollenkaan oliomuuttujaan pituus — koodi käytännössä asettaa parametrina saadulle pituus-muuttujalle siinä jo olevan arvon:

public void setPituus(int pituus) {
    // ÄLÄ TEE NÄIN!!!
    pituus = pituus;
}
public void setPituus(int pituus) {
    // VAAN NÄIN!!!
    this.pituus = pituus;
}
Loading

Oman metodin kutsu

Olio voi kutsua myös omia metodeitaan. Jos esim. halutaan, että toString-metodin palauttama merkkijonoesitys kertoisi myös henkilön painoindeksin, kannattaa toString:istä kutsua olion omaa metodia painoIndeksi:

public String toString() {
    return this.nimi + ", ikä " + this.ika + " vuotta, painoindeksini on " + this.painoindeksi();
}

Eli kun olio kutsuu omaa metodiaan, riittää etuliite this ja pelkkä metodin nimi. Vaihtoehtoinen tapa on tehdä oman metodin kutsu muodossa painoindeksi() jolloin ei korosteta, että kutsutaan "olion itsensä" metodia painoindeksi:

public String toString() {
    return this.nimi + ", ikä " + this.ika + " vuotta, painoindeksini on " + painoindeksi();
}

Olioscreencastin kolmas osa:

 

Loading
Loading
Pääsit aliluvun loppuun! Jatka tästä seuraavaan osaan:

Muistathan tarkistaa pistetilanteesi materiaalin oikeassa alareunassa olevasta pallosta!