Osa 5

Toistuvan koodin vähentäminen: Metodien ja konstruktorien kuormittaminen

Palataan jälleen henkilöitä käsittelevän luokan pariin. Luokka Henkilo näyttää tällä hetkellä seuraavalta:

public class Henkilo {

    private String nimi;
    private int ika;
    private int pituus;
    private int paino;

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

    public void tulostaHenkilo() {
        System.out.println(this.nimi + " olen " + this.ika + " vuotta vanha");
    }

    public void vanhene() {
        this.ika++;
    }

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

        return true;
    }

    public double painoindeksi() {
        double pituusMetreina = this.pituus / 100.0;

        return this.paino / (pituusMetreina * pituusMetreina);
    }

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

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

    public int getPituus() {
        return this.pituus;
    }

    public int getPaino() {
        return this.paino;
    }

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

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

Kaikki henkilöoliot ovat luontihetkellä 0-vuotiaita, sillä konstruktori asettaa uuden henkilön ika-oliomuuttujan arvoksi 0:

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

Konstruktorin kuormittaminen

Haluaisimme luoda henkilöitä myös siten, että konstruktorin parametrina annettaisiin ikä nimen lisäksi. Tämä onnistuu, sillä konstruktoreja voi olla useita. Tehdään vaihtoehtoinen konstruktori. Vanhaa konstruktoria ei tarvise poistaa.

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

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

Nyt olioiden luonti onnistuu kahdella vaihtoehtoisella tavalla:

public static void main(String[] args) {
    Henkilo pekka = new Henkilo("Pekka", 24);
    Henkilo ada = new Henkilo("Ada");

    System.out.println(pekka);
    System.out.println(ada);
}
Esimerkkitulostus

Pekka, ikä 24 vuotta Ada, ikä 0 vuotta

Tekniikkaa jossa luokalla on kaksi konstruktoria, kutsutaan konstruktorin kuormittamiseksi. Luokalla voi siis olla useita konstruktoreja, jotka poikkeavat toisistaanparametriensa määrältä tai tyypeiltä. Ei kuitenkaan ole mahdollista tehdä kahta erilaista konstruktoria joilla on täysin saman tyyppiset parametrit. Emme siis voi edellisten lisäksi lisätä konstruktoria public Henkilo(String nimi, int paino) sillä Javan on mahdoton erottaa tätä kaksiparametrisesta konstruktorissa, jossa luku tarkoittaa ikää.

Oman konstruktorin kutsuminen

Mutta hetkinen, aiemmin todettiin että "copy-paste"-koodi ei ole hyvä idea. Kun tarkastellaan edellä tehtyjä kuormitettuja konstruktoreita, niissä on aika paljon samaa. Emme ole oikein tyytyväisiä tilanteeseen.

Konstruktoreista ylempi, eli nimen parametrinaan saava konstruktori, on oikeastaan alemman, eli nimen ja iän parametrinaan saavan konstruktorin, erikoistapaus. Entä jos ylempi konstruktori voisi "kutsua" alempaa konstruktoria?

Tämä onnistuu, sillä konstruktorin sisältä voi kutsua toista konstruktoria juuri tähän olioon liittyvän this-ilmauksen avulla!

Muutetaan ylempää konstruktoria siten, että se ei itse tee mitään vaan ainoastaan kutsuu alempaa konstruktoria ja pyytää sitä asettamaan iäksi 0:

public Henkilo(String nimi) {
    this(nimi, 0);  // suorita tässä toisen konstruktorin koodi ja laita ika-parametrin arvoksi 0
}

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

Oman konstruktorin kutsu this(nimi, 0); saattaa vaikuttaa erikoiselta. Asiaa voi vaikka ajatella siten, että kutsun kohdalle tulee "copy-pastena" automaattisesti alemman konstruktorin koodi, siten että ika parametrin arvoksi tulee 0. Huom! Jos konstruktorista kutsutaan toista konstruktoria, tulee konstruktorin kutsun olla ensimmäinen toiminto konstruktorin sisällä.

Olioiden luonti onnistuu aivan kuten edellisessä esimerkissä:

public static void main(String[] args) {
    Henkilo pekka = new Henkilo("Pekka", 24);
    Henkilo esko = new Henkilo("Esko");

    System.out.println(pekka);
    System.out.println(esko);
}
Esimerkkitulostus

Pekka, ikä 24 vuotta Esko, ikä 0 vuotta

Loading

Metodin kuormittaminen

Konstruktorien tapaan myös metodeja voi kuormittaa, eli samannimisestä metodista voi olla useita versioita. Jälleen eri versioiden parametrien tyyppien on oltava erilaiset. Tehdään vanhene-metodista toinen versio, joka mahdollistaa henkilön vanhentamisen parametrina olevalla vuosimäärällä:

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

public void vanhene(int vuodet) {
    this.ika = this.ika + vuodet;
}

Seuraavassa "Pekka" syntyy 24-vuotiaana, vanhenee ensin vuoden ja sitten 10 vuotta:

public static void main(String[] args) {
    Henkilo pekka = new Henkilo("Pekka", 24);
    System.out.println(pekka);

    pekka.vanhene();
    System.out.println(pekka);

    pekka.vanhene(10);
    System.out.println(pekka);
}

Tulostuu:

Esimerkkitulostus

Pekka, ikä 24 vuotta Pekka, ikä 25 vuotta Pekka, ikä 35 vuotta

Henkilöllä on nyt siis käytännössä kaksi kappaletta vanhene-nimisiä metodeja. Se kumpi metodeista valitaan suoritettavaksi, riippuu metodikutsussa käytettyjen parametrien määrästä.

Ohjelmaa voi muokata myös niin, että parametriton metodi vanhene toteutetaan metodin vanhene(int vuodet) avulla:

public void vanhene() {
    this.vanhene(1);
}

public void vanhene(int vuodet) {
    this.ika = this.ika + vuodet;
}
Loading
Pääsit aliluvun loppuun! Jatka tästä seuraavaan osaan:

Muistathan tarkistaa pistetilanteesi materiaalin oikeassa alareunassa olevasta pallosta!