8. Perintä

Perintä

Perintä tarkoittaa, että luokan toteutuksen lähtökohtana on toisen luokan toteutus.

Luokan määrittelyssä perintä ilmoitetaan sanan extends avulla. Lähtökohtana olevaa luokkaa sanotaan yliluokaksi, ja uutta luokkaa sanotaan aliluokaksi.

Esimerkki

Luokka Henkilo on määritelty seuraavasti:

public class Henkilo {
    private String nimi;

    public Henkilo(String nimi) {
	this.nimi = nimi;
    }
    
    public void esittaydy() {
	System.out.println("Minun nimeni on " + nimi + ".");
    }

    public String toString() {
	return nimi;
    }
}

Seuraava luokka Opiskelija perii luokan Henkilo:

public class Opiskelija extends Henkilo {
    private int opintopisteet;

    public Opiskelija(String nimi) {
	super(nimi);
	this.opintopisteet = 0;
    }

    public void esittaydy() {
	super.esittaydy();
	System.out.println("Olen saanut kokoon " + opintopisteet + " op.");
    }

    public void opiskele() {
	opintopisteet++;
    }
}

Luokkia voidaan käyttää seuraavasti pääohjelmassa:

public class Main {
    public static void main(String[] args) {
	Henkilo aapeli = new Henkilo("Aapeli");	
	System.out.println(aapeli);

	aapeli.esittaydy();

	Opiskelija maija = new Opiskelija("Maija");
	maija.opiskele();
	maija.opiskele();

	System.out.println(maija);
	maija.esittaydy();
    }
}

Ohjelman tulostus on seuraava:

Aapeli
Minun nimeni on Aapeli.
Maija
Minun nimeni on Maija.
Olen saanut kokoon 2 op.

Tässä siis luokka Henkilo on yliluokka ja luokka Opiskelija on aliluokka. Seuraavaksi katsomme tarkemmin, mitä perinnässä tapahtuu.

Mitä peritään?

Perintä tuo aliluokan osaksi yliluokan julkiset (public) muuttujat ja metodit. Konstruktorit eivät kuitenkaan periydy, vaan ne täytyy määritellä aina erikseen. Lisäksi perittyjä metodeja on mahdollista korvata uusilla toteutuksilla.

Yllä olevassa esimerkissä luokassa Opiskelija on luokasta Henkilo peritty metodi toString. Sen sijaan metodi esittaydy on korvattu omalla toteutuksella. Lisäksi luokassa on uusina asioina muuttuja opintopisteet ja metodi opiskele.

super viittaa yliluokkaan

Sanalla super voi viitata yliluokan muuttujiin ja metodeihin.

Yllä olevassa esimerkissä luokan Opiskelija konstruktori suorittaa ensin yliluokan konstruktorin. Lisäksi metodi esittaydy suorittaa ensin yliluokan metodin esittaydy.

protected-merkintä

Toisinaan yliluokasta olisi hyödyllistä periä asioita, joita ei kuitenkaan haluaisi päästää julkisiksi. Tämän mahdollistaa sana protected, jota käytetään sanan private tai public tilalla. Tällaista muuttujaa tai metodia sanotaan suojatuksi.

Esimerkiksi luokan Henkilo muuttuja nimi voisi olla suojattu:

public class Henkilo {
    protected String nimi;

    // luokan muu sisältö
}

Nyt luokassa Opiskelija voisi tehdä näin:

public class Opiskelija {
    // luokan muu sisältö

    public void esittaydy() {
	System.out.print("Minun nimeni on " + nimi + " ja olen ");
	System.out.println("saanut kokoon " + opintopisteet + " op.");
    }
}

Perintähierarkia

Luokka voi periä toisen luokan, joka taas on perinyt toisen luokan jne. Perintää voi siis tapahtua monella tasolla. Rajoituksena on kuitenkin, että luokka voi periä suoraan vain yhden luokan eli se ei voi yhdistellä monen luokan aineksia.

Javassa jokaisen luokan perimmäisenä yliluokkana on luokka Object. Kaikki uudet luokat perivät sen automaattisesti, jos ne eivät peri mitään muuta luokkaa.

Esimerkissä tilanne on seuraava:

Object -> Henkilo -> Opiskelija

Huomaa, että luokkaan voi viitata sen minkä tahansa yliluokan nimellä. Kaikki seuraavat määrittelyt ovat siis mahdollisia:

Opiskelija maija = new Opiskelija("Maija");
Henkilo maija = new Opiskelija("Maija");
Object maija = new Opiskelija("Maija");

Muuttujan tyyppi määrittää, mitä luokan ominaisuuksia on käytettävissä. Esimerkiksi jos muuttujan tyyppinä on Henkilo, metodia opiskele ei voi käyttää, vaikka muuttuja viittaisi Opiskelija-olioon.

Abstrakti luokka

Abstrakti luokka on luokka, jonka kaikkia metodeja ei ole toteutettu, vaan niistä on annettu vain runko. Abstraktia luokkaa ei voi käyttää sellaisenaan, vaan toisen luokan täytyy ensin periä se ja toteuttaa puuttuvat metodit.

Seuraavassa on abtrakti luokka Saveltaja:

public abstract class Saveltaja {
    private String nimi;

    public Saveltaja(String nimi) {
	this.nimi = nimi;
    }

    public void esittaydy() {
	System.out.println("Hei, nimeni on " + nimi);
    }

    public abstract void savella();
}

Tässä metodi esittaydy on toteutettu täydellisesti, mutta metodista savella on annettu vain runko.

Seuraavat luokat täydentävät abstraktin luokan:

public class Beethoven extends Saveltaja {
    public Beethoven() {
	super("Beethoven");
    }

    public void savella() {
	// Kohtalonsinfonia
	System.out.println("aaa f ggg e");
    }
}
public class Sibelius extends Saveltaja {
    public Sibelius() {
	super("Sibelius");
    }

    public void savella() {
	// Finlandia
	System.out.println("e d e f e d e c d d e");
    }
}

Luokkien toimintaa esittelee seuraava pääohjelma:

public class Main {
    public static void main(String[] args) {
	Beethoven beethoven = new Beethoven();
	beethoven.esittaydy();
	beethoven.savella();

	Sibelius sibelius = new Sibelius();
	sibelius.esittaydy();
	sibelius.savella();
    }
}

Ohjelman tulostus on seuraava:

Hei, nimeni on Beethoven
aaa f ggg e
Hei, nimeni on Sibelius
e d e f e d e c d d e

Rajapinnat vs. abstraktit luokat

Rajapinnat ja abstraktit luokat ovat melko samanlaisia. Erot ovat: