Eevert Saukkokoski, 20.9.2008 Creative Commons License

Luokat ja oliot Javassa

Sisällysluettelo

  1. Luokat ja oliot
  2. Attribuutit ja metodit
  3. Näkyvyysmääreet
  4. Luontimetodi
  5. Luokkakohtaiset attribuutit ja metodit
  6. Perintä
  7. Pääohjelmametodi
  8. Lähdeluettelo

Luokat ja oliot

Java on vahvasti oliopohjainen kieli. Javaa ei voi kirjoittaa joutumatta käsittelemään luokkia ja olioita, ja siten niiden toimintalogiikan täysi ymmärtäminen on ensiarvoisen tärkeää. Luokan ja olion lisäksi tärkeimpiä käsitteitä ovat niiden rakennusosat eli metodit ja attribuutit.

Olio (object) on jonkin luokan ilmentymä. Luokka (class) määrittelee, mitä siitä muodostetut ilmentymät eli instanssit voivat tehdä ja millaisia ominaisuuksia niillä on. Luokat voidaan mieltää Platonin ideaopin mukaisiksi ideoiksi, joiden ruumiillistumia ovat oliot.

Olio-ohjelmoinnissa yhdistetään ohjelmassa käsiteltävä tieto siihen, millaisia operaatioita sille voidaan suorittaa. Ihmisen ajattelun kannalta on usein helpompaa tarkastella ohjelmaa yhteistyötä tekevien olioiden kuin puhtaiden alkeistyyppien ja funktioiden, perinteisten ohjelmoinnin välineiden, muodostamina järjestelminä. Oliopohjainen ajattelu mahdollistaa laajojen asiakokonaisuuksien hahmottamisen yksityiskohdat piilottavien abstraktioiden avulla, ja on siten tärkeä suunnitteluväline. [1]

Attribuutit ja metodit

Luokan määrittely voi sisältää attribuutteja ja metodeja. Attribuutit määrittelevät, mitä olio voi tietää; ne ovat muuttujia, ja muodostavat siten tavan jolla yksi olio voi poiketa toisesta. Metodit kertovat, mitä toimintoja oliolla on ja miten ne suoritetaan.

Metodi on luokan sisällä määritelty nimetty koodilohko, jota voidaan kutsua muualta koodista luokan instanssin kautta. Metodille voidaan määrittää parametreja, jotka on annettava aina metodia kutsuessa. Niiden avulla metodia kutsuva koodi voi välittää metodille tietoa, jonka perusteella se voi toimia.

Attribuutti on luokan sisällä (muttei kuitenkaan metodissa) määritelty muuttuja. Kuten tavallisten muuttujien kohdalla, attribuuteilla voi olla oletusarvo, mutta sellaista ei ole pakko asettaa.

Javassa kaikilla ilmauksilla on hyvin määritelty tyyppi, ja sellainen pitää määritellä myös attribuuteille, metodin parametreille ja metodien paluuarvoille. Tyyppi on joko alkeistyyppi (esimerkiksi totuusarvo tai kokonaisluku) tai luokka, jonka edustaja arvon tulee olla. Metodien tapauksessa tyyppi voi olla myös tyhjä (void), jolloin metodi ei palauta mitään.

class Tervehtija {
    String tervehdys = "Hei";
    void tervehdi(String nimi) {
        System.out.println(this.tervehdys + ", " + nimi + "!");
    }
}

Esimerkissä määritellään luokka Tervehtija, jonka instanssit tietävät jonkin tervehdyksen ja osaavat tulostaa konsoliin tervehdyksen kun niiden tervehdi-nimistä metodia kutsutaan tervehdittävän nimen kanssa. Instanssin attribuuttia käsitellään instanssiin itseensä viittaavan this-avainsanan avulla.

Tervehtija tervehtija = new Tervehtija();
tervehtija.tervehdi("Teemu Teekkari");

Esimerkissä esitellään muuttuja tervehtija, luodaan uusi Tervehtija-instanssi new-avainsanalla ja sijoitetaan instanssi muuttujaan. Tämän jälkeen sen käsketään tervehtiä Teemu Teekkaria.

Näkyvyysmääreet

Luokka tai olio ei välttämättä halua paljastaa kaikkia tietojaan muille, eikä muita vastaavasti välttämättä kiinnosta kaikki tämä tieto. Yleisesti luokan sisällä halutaan olla myös varmoja siitä, ettei attribuutin sisältö muutu yllättäen: mikäli attribuutti on julkinen, mikä tahansa taho jolla on pääsy instanssiin voi vapaasti muuttaa sen sisältöä. Vastaavasti julkista staattista attribuuttia voidaan muuttaa mistä tahansa käsin.

Ongelmia voi ilmetä myös silloin, kun luokan implementaatiota halutaan muuttaa. Kaikkia julkisia attribuutteja ja metodeja on voitu käyttää muualla sijaitsevassa koodissa, joten niiden poistaminen tai nimen vaihtaminen ei ole turvallista.

Tällaisten ongelmien vähentämiseksi Javassa on näkyvyysmääreet, joilla voidaan määrittää mille tahoille attribuutit ja metodit näkyvät. Tärkeimmät ja useimmin käytetyt ovat public, joka antaa pääsyn kaikille sekä private, joka estää käsittelyn muualta kuin luokasta itsestään.

Hyvänä käytäntönä pidetään sitä, että attribuutit ja metodit ovat vain niin julkisia kuin on ohjelman toiminnan kannalta välttämätöntä. [2] Tällöin luokkien sisäiset implementaatiodetaljit eivät ole ulkoa päin hämmästeltävissä ja luokkaa käyttävä koodi voi olla varma siitä, ettei voi sotkea luokan tai olion toimintaa tekemällä asioita, joita ei ole tarkoitus tehdä.

Näkyvyysmääreiden avulla voidaan esimerkiksi piilottaa attribuutti ulkoisilta tahoilta, mutta mahdollistaa sen lukeminen julkiseksi määritellyn saantimetodin avulla. Vastaavasti voidaan sallia myös muokkaaminen asetusmetodilla.

class Koira {
    private String nimi;
    public String annaNimi() {
        return this.nimi;
    }
    public void asetaNimi(String nimi) {
        this.nimi = nimi;
    }
}

Mikäli Koiraa myöhemmin halutaan muokata siten, että sillä on sekä etu- että sukunimi, voidaan saanti- ja asetusmetodeita muokata ottamaan asia huomioon: jos muualla koodissa olisi käsitelty suoraan nimi-attribuuttia, muutoksien tekeminen olisi vaikeampaa.

Luontimetodi

Jos olion luomistapahtuman yhteyteen tarvitaan jonkinlaista logiikkaa tai ulkopuolista tietoa, voidaan luokalle määritellä luontimetodi (constructor). Se on aina saman niminen kuin luokka itse, eikä sille julisteta paluuarvoa.

class Koira {
    private String nimi;
    public Koira(String nimi) {
        this.nimi = nimi;
    }
    public String annaNimi() {
        return this.nimi;
    }
}

Esimerkissä määritellään Koira, jonka instanssia luodessa on pakko antaa myös Koiran nimi. Ei siis epähuomiossa voida luoda uutta Koiraa, jolla ei ole nimeä.

Luokkakohtaiset attribuutit ja metodit

Sekä metodi että attribuutti voidaan julistaa luokkakohtaisiksi eli staattisiksi, jolloin niitä käytetään viittaamalla luokkaan eikä yksittäiseen siitä muodostettuun olioon. Tällöin attribuutin taltioima tieto on kaikille luokan instansseille yhteistä. Metodista taas pääsee käsiksi vain luokan muihin staattisiin metodeihin ja attribuutteihin.

class Koira {
    private String nimi;
    private static String vari = "ruskea";
    public static String annaVari() {
        return Koira.vari;
    }
}

Esimerkissä määritellään Koiralle staattinen attribuutti vari ja sen arvo sekä staattinen metodi, joka palauttaa varin. Ei-staattista attribuuttia nimi ei voida käsitellä annaVari-metodista.

Perintä

Usein on mielekästä muodostaa luokista erikoistuneempia versioita, jotka osaavat samat asiat kuin alkuperäinen mutta tekevät ehkä jotakin ylimääräistä tai jotakin jo olemassa ollutta eri tavalla. Tällöin voidaan käyttää perintää (inheritance).

Yläluokan perivä lapsiluokka saa käyttöönsä kaikki yläluokan metodit ja ominaisuudet, joita ei ole piilotettu yläluokassa asettamalla ne yksityisiksi. Alaluokan instanssi voi siis toimia myös yläluokkansa tyypin edustajana: esimerkiksi Koira on myös Elain. Jos yläluokkaa ei erikseen ilmoiteta, peritään Javan Object-luokka. Kaikki oliot ovat siis Objectin edustajia.

class Elain {
    private String nimi;
    public Elain(String nimi) {
        this.nimi = nimi;
    }
    public String annaNimi() {
        return this.nimi;
    }
}
class Koira extends Elain {
    public void hauku() {
        System.out.println("Woof!");
    }
}

Esimerkissä määritellään Elain ja sen perivä Koira. Kuten Elain, Koira tietää nimensä ja osaa antaa sen, mutta osaa lisäksi haukkua.

On helppo erehtyä käyttämään perintää myös silloin, kun halutaan käyttää jossakin toisessa luokassa määriteltyjä ominaisuuksia hyödyksi luokassa, joka ei käsitteellisesti tai toiminnallisesti ole perityn luokan alatyyppi. Tällöin tulisi kuitenkin välttää perintää ja pyrkiä ratkaisemaan ongelma muulla tavalla. [3]

Pääohjelmametodi

Jotta Java voisi suorittaa luodun ohjelman, täytyy sen tietää mistä aloittaa. Koska ohjelmakoodia ei voi sijoittaa luokkien ulkopuolelle, pitää ohjelman suorittavankin koodin olla luokan sisällä. Tarvitaan pääohjelmametodi, joka Javassa on muotoa public static void main(String[] args). Argumentiksi tulee merkkijonotaulukko, jonka sisältönä ovat ohjelmaa kutsuttaessa annetut komentoriviargumentit.

class Koira {
    public void hauku() {
        System.out.println("Woof!");
    }
    public static void main(String[] args) {
        Koira k = new Koira();
        k.hauku();
    }
}

Esimerkissä määritellään Koira ja pääohjelmametodi, joka luo uuden Koiran ja käskee sen haukkua. Koira siis muodostaa nyt valmiin ohjelman, jonka Java-virtuaalikone osaa suorittaa.

Lähdeluettelo

[1]: John Paul Fullerton, "Benefits of Object-Oriented Design", 1998, Texas A&M University, USA, haettu 20.9.2008

[2]: "Controlling Access to Members of a Class", 2008, Sun Microsystems, USA, haettu 20.9.2008

[3]: Paul John Rajlich, "Object Composition vs. Inheritance", 1998, "Object Oriented Design", University of Illinois, USA, haettu 20.9.2008