Olioiden monimuotoisuus
Olemme aiemmissa osissa törmänneet tilanteisiin, joissa viittaustyyppisillä muuttujilla on oman tyyppinsä lisäksi muita tyyppejä. Esimerkiksi kaikki oliot ovat tyyppiä Object
, eli mikä tahansa olio voidaan oman tyyppinsä lisäksi esittää Object
-tyyppisenä muuttujana.
String merkkijono = "merkkijono";
Object merkkijonoString = "toinen merkkijono";
String merkkijono = "merkkijono";
Object merkkijonoString = merkkijono;
Yllä olevissa esimerkeissä merkkijonomuuttuja esitetään sekä String-tyyppisenä että Object-tyyppisenä, jonka lisäksi String-tyyppinen muuttuja asetetaan Object-tyyppiseen muuttujaan. Asetus toiseen suuntaan, eli Object-tyyppisen muuttujan asettaminen String-tyyppiseksi ei kuitenkaan onnistu. Tämä johtuu siitä, että Object
-tyyppiset muuttujat eivät ole tyyppiä String
Object merkkijonoString = "toinen merkkijono";
String merkkijono = merkkijonoString; // EI ONNISTU!
Mistä tässä oikein on kyse?
String-luokan API-dokumentaatio alkaa yleisellä otsakkeella jota seuraa luokan pakkaus (java.lang
). Pakkauksen jälkeen tulee luokan nimi (Class String
), jota seuraa luokan perintähierarkia.
java.lang.Object java.lang.String
Perintähierarkia listaa luokat, jotka luokka on perinyt. Perityt luokat listataan perimisjärjestyksessä, tarkasteltava luokka aina alimpana. String-luokan perintähierarkiasta näemme, että String
-luokka perii luokan Object
. Javassa jokainen luokka voi periä korkeintaan yhden luokan. Toisaalta, perittävä luokka on voinut periä toisen luokan, joten välillisesti luokka voi periä useampia luokkia.
Perintähierarkiaa voi ajatella myös listana tyypeistä, joita olio toteuttaa.
Tieto siitä, että oliot voivat olla montaa eri tyyppiä — esimerkiksi tyyppiä Object — suoraviivaistaa ohjelmointia. Jos tarvitsemme metodissa vain Object-luokassa määriteltyjä metodeja kuten toString
, equals
ja hashCode
, voimme käyttää metodin parametrina tyyppiä Object
. Tällöin metodille voi antaa parametrina minkä tahansa olion. Tarkastellaan tätä metodin tulostaMonesti
avulla. Metodi saa parametrinaan Object
-tyyppisen muuttujan ja tulostusten lukumäärän.
public class Tulostin {
public void tulostaMonesti(Object object, int kertaa) {
int i = 0;
while (i < kertaa) {
System.out.println(object.toString());
// tai System.out.println(object);
i = i + 1;
}
}
}
Metodille voi antaa parametrina minkä tahansa olion. Metodin tulostaMonesti
sisällä oliolla on käytössään vain Object
-luokassa määritellyt metodit, koska olio tunnetaan metodissa Object
-tyyppisenä. Todellisuudessa olio voi olla myös toisen tyyppinen.
Tulostin tulostin = new Tulostin();
String merkkijono = " o ";
List<String> sanat = new ArrayList<>();
sanat.add("polymorfismi");
sanat.add("perintä");
sanat.add("kapselointi");
sanat.add("abstrahointi");
tulostin.tulostaMonesti(merkkijono, 2);
tulostin.tulostaMonesti(sanat, 3);
o o [polymorfismi, perintä, kapselointi, abstrahointi] [polymorfismi, perintä, kapselointi, abstrahointi] [polymorfismi, perintä, kapselointi, abstrahointi]
Jatketaan String
-luokan API-kuvauksen tarkastelua. Kuvauksessa olevaa perintähierarkiaa seuraa listaus luokan toteuttamista rajapinnoista.
All Implemented Interfaces: Serializable, CharSequence, Comparable<String>
Luokka String
toteuttaa rajapinnat Serializable
, CharSequence
, ja Comparable<String>
. Myös rajapinta on tyyppi. Luokan String API-kuvauksen mukaan String-olion tyypiksi voi asettaa seuraavat rajapinnat.
Serializable serializableString = "merkkijono";
CharSequence charSequenceString = "merkkijono";
Comparable<String> comparableString = "merkkijono";
Koska metodeille voidaan määritellä metodin parametrin tyyppi, voimme määritellä metodeja jotka vastaanottavat tietyn rajapinnan toteuttavan olion. Kun metodille määritellään parametrina rajapinta, sille voidaan antaa parametrina mikä tahansa olio, joka toteuttaa kyseisen rajapinnan.
Täydennetään Tulostin
-luokkaa siten, että sillä on metodi CharSequence
-rajapinnan toteuttavien olioiden merkkien tulostamiseen. Rajapinta CharSequence
tarjoaa muunmuassa metodit int length()
, jolla saa merkkijonon pituuden, ja char charAt(int index)
, jolla saa merkin tietyssä indeksissä.
public class Tulostin {
public void tulostaMonesti(Object object, int kertaa) {
int i = 0;
while (i < kertaa) {
System.out.println(object);
i = i + 1;
}
}
public void tulostaMerkit(CharSequence charSequence) {
int i = 0;
while (i < charSequence.length()) {
System.out.println(charSequence.charAt(i));
i = i + 1;
}
}
}
Metodille tulostaMerkit
voi antaa minkä tahansa CharSequence
-rajapinnan toteuttavan olion. Näitä ovat muun muassa String
ja merkkijonojen rakentamisessa usein Stringiä tehokkaampi StringBuilder
. Metodi tulostaMerkit
tulostaa annetun olion jokaisen merkin omalle rivilleen.
Tulostin tulostin = new Tulostin();
String mjono = "toimii";
tulostin.tulostaMerkit(mjono);
t o i m i i
Muistathan tarkistaa pistetilanteesi materiaalin oikeassa alareunassa olevasta pallosta!