Luku esittelee lyhyesti luokan Object, vähän uutta geneerisyyttä
sekä uudistetut tietorakenneluokat Vector<E> ja Hashtable<K,V>.
(Tämä luku on vanha versio! Uudistettu luku 5.3
esittelee "uudenaikaisemmat" vastaavat luokat
ArrayList
Vector on vaihtelevan
kokoinen taulukko, jonne voi tallettaa mitä tahansa Javan olioita.
Hashtable on rakenne, jolla voi luoda ns. assosiaatiolistoja:
olioon voidaan liittää jokin toinen olio ja ensimmäisellä
assosioda toinen, esimerkiksi "avaimella voidaan hakea arvoa".
Uudistetuissa luokissa Vector<E> ja Hashtable<K,V>
on käytetty ns. geneerisyyttä,
käännösaikaista tyyppiparametrointia. Tähän hyödylliseen ja
voimakkaaseen Javan uutuuteen ei peruskursseilla ole kovin paljon
aikaa käytettävissä. Esimerkit kuitenkin antavat mallia välineiden
käyttöön omissa ohjelmissa.
Luokassa Object on joitakin metodeita, jotka siis periytyvät kaikille
muille luokille. Vanha tuttu on toString(), joka on tapana
aliluokissa korvata (override) sopivasti. Object-luokan toString()
muodostaa oliosta kuin oliosta tekstiesityksen, joka kertoo
olion luokan ja heksadesimaaliluvun, joka antaa tietoa olion
sijainnista tulkin tietorakenteissa, esim. Pikkuvarasto@80cb94a.
Object-tyyppiselle muuttujalle voi sijoittaa minkä tahansa olioarvon,
mutta tällöin tuolle oliolle voi soveltaa vain Object-luokan metodeita.
Jos näitä on polymorfisesti korvattu aliluokissa
(ks. luku 4.4), valitaan kuitenkin olion
oman luokan metodi.
Kun Object-tyyppisen muuttujan arvona on jonkin aliluokan ilmentymä ja
tuolle oliolle halutaan soveltaa metodia, jota Object-luokasta ei löydy,
olioarvo on muunnettava explisiittisellä tyyppimuunnoksella olion oman
luokkan tyyppiin, esim:
Olion luokkaa voi kysellä instanceof-operaatiolla:
Uudessa Javassa tyyppiin voidaan liittää käännösaikaisia
tyyppiparametretreja:
Koska jo kääntäjä tietää todellisen tyyppiparametrin, se osaa generoida
itse tarvittavat tyyppimuunnokset! Alkeistyypit (esim. int, double,
boolean, ...) eivät kuitenkaan kelpaa todelliseksi
tyyppiparametriksi, vaan niiden sijalla on käytettävä vastaavia
luokkia, ns. "käärepaperiluokkia" ("wrapper") Integer, Double, Boolean, ...
Näiden "käärepaperiluokkin" käyttäminen on tehty helpoksi ns.
"autoboxing"-menettelyn avulla. Koska kääntäjä tietää
tietorakenteeseen vietyjen alkeistyyppisten arvojen tyypin,
se osaa automaattisesti generoida tarvittavat tyyppimuunnokset
tietoja rakenteeseen vietäessä ja niitä sieltä otettaessa.
Esimerkki nähdään Vector-luokan esittelyn lopussa.
Tällä peruskurssilla geneerisyydestä opetellaan vain geneeristen
luokkien Vector<E> ja Hashtable<K,V> käyttöä.
Omien geneeristen luokkien ohjelmointi on melko monitahoista ja
vaatii tietoa enemmän kuin kurssille mahtuu. Pieni esimerkki erääseen
harjoitustehtävään liittyen saattaa silti olla mielenkiintoista
luettavaa:
KokeileGenVertTaulukkoa.java.
[Lisätietoa Javan uudesta geneerisyydestä löytyy tietenkin
Sunin sivuilta sekä erityisesti
Angelika Langerin
erinomaisilta sivuilta
"Java
Generics FAQs - Frequently Asked Questions".
Uusi geneerisyys ei ole välttämättä vielä lopullisessa asussaan;
Langer toteaa eräässä tiedotteessaan:
"My conclusion from looking into the issues is: refrain from
overloading, unless you are sure you fully understand overload
resolution in Java, which is a mystery even to me".
Jos geneerisyyden erikoisasiantuntijallakin on ongelmia ymmärtää
metodien kuormittamisen ja geneerisyyden suhdetta, on luultavaa,
että jossakin on vielä vikaa...]
Joitakin
Vector-luokan
välineitä:
Esimerkki: Ohjelma lukee ensin joukon lukuja. Sitten lukuja
voi poistaa. Lopuksi ohjelma tulostaa jäljelle jääneiden lukujen
summan:
Joku voisi sanoa, että Hashtable-olio on kuin taulukko,
jota "indeksoidaan" K-tyyppisillä arvoilla, ei kokonaisluvuilla..
Luokan Vector tapaan Hashtable on aina "riittävän" suuri.
Avaimena käytettävien arvojen luokan K on toteutettava hashCode-
ja equals-metodit
(tästä ei tässä enempää; mm. String, Integer, Double, ..., kelpaavat
tyyppiparametria K vastaavaksi todelliseksi tyypiksi).
Joitakin Hashtable-luokan välineitä:
public Hashtable<K,V>() luo tyhjän Hashtable-olion
Tässä pikku esimerkissä Hashtable<K,V>-olioon talletetaan
avaimiksi sanoja ja assosioitaviksi arvoiksi sanojen luetteloita
Vector<E>-olioina:
Luokka Object
Luokka Object on ainoa Javan luokka, jolla ei ole yliluokkaa. Kaikki
muut luokat ovat sen välillisiä tai välittömiä aliluokkia.
Object otus = new Pikkuvarasto();
// ...
otus.vieVarastoon(3.14); // väärin!
((Pikkuvarasto)otus).vieVarastoon(3.14); // sallittu
//...
otus = "BÖÖ!";
int i;
i = otus.length(); // väärin!
i = ((String)otus).length(); // sallittu
Huom: Komponenttiin viittaus (".") sitoo tiukemmin
kuin eksplisiittinen tyyppimuunnos, siksi nuo sulkeet
tyyppimuunnoksen ympärillä ovat välttämättömät, ks.
"Operaatioiden sitovuus".
Object otus = new Pikkuvarasto();
// ...
if (otus instanceof Pikkuvarasto)
((Pikkuvarasto)otus).vieVarastoon(3.14);
else if (otus instanceof String)
i = ((String)otus).length();
Vähän geneerisyydestä, hyvin vähän...
Ennen geneeristen tyyppien käyttömahdollisuutta yleisten
tietorakenteiden käyttäminen oli vähän vaivalloista, koska
alkiot täytyi itse muuntaa sopivan tyyppisiksi
sekä rakenteeseen vietäessä, että sieltä otettaessa
- aivan samaan tapaan kuin yllä meneteltiin Object-tyyppisen
muuttujan kohdalla:
((Pikkuvarasto)otus).vieVarastoon(3.14);
double määrä = ((Pikkuvarasto)otus).otaVarastosta(30);
public class GenTyyppi<T> {
private T kenttä;
public GenTyyppi() { }
public GenTyyppi(T kenttä) {
this.kenttä = kenttä;
}
public void printtaa() {
System.out.println(kenttä);
}
}
Ja ilmentymiä voidaan luoda antamalla konstruktorille jokin tyyppi parametriksi:
public class GenEsim {
public static void main(String[] args) {
GenTyyppi<String> x = new GenTyyppi<String>();
GenTyyppi<String> xx = new GenTyyppi<String>("BÖÖ");
x.printtaa();
xx.printtaa();
GenTyyppi<Pikkuvarasto> y = new GenTyyppi<Pikkuvarasto>();
GenTyyppi<Pikkuvarasto> yy = new GenTyyppi<Pikkuvarasto>(
new Pikkuvarasto(10, "Mehu"));
y.printtaa();
yy.printtaa();
}
}
Ohjelma tulostaa:
null
BÖÖ
null
(Mehu: 10.0)
Vector<E>
Luokka Vector<E> määrittelee näennäisesti "vaihtelevanmittaiset"
taulukot, joiden komponenttityyppi annetaan Vector-oliota luotaessa.
Komponentit on tavallisen taulukon
tapaan numeroitu indeksein nollasta alkaen. Komponenttien
lisääminen ja poistaminen muuttaa lisäys- ja poistokohtaa seuraavien
komponenttien indeksejä.
import java.util.*;
public class VectorEsim {
private static Scanner lukija = new Scanner(System.in);
public static void main(String[] args) {
Vector<Integer> taulu = new Vector<Integer>();
int luku;
System.out.println("Syötä lukuja, nolla lopettaa.");
luku = lukija.nextInt();
while (luku != 0) {
taulu.addElement(luku);
luku = lukija.nextInt();
}
System.out.println(taulu);
System.out.println("Syötä poistettavia lukuja, nolla lopettaa.");
luku = lukija.nextInt();
while (luku != 0) {
boolean oli = taulu.removeElement(luku);
if (oli)
System.out.println(taulu);
else
System.out.println("Ei löydy");
luku = lukija.nextInt();
}
System.out.println(taulu);
System.out.println("Alkioiden summa = " + summa(taulu));
}
private static int summa(Vector<Integer> tau) {
int summa = 0;
for (int i=0; i < tau.size(); ++i)
summa += tau.elementAt(i);
return summa;
}
}
Ilman edellä jo mainostettua "autoboxingia" esimerkiksi lauseet
taulu.addElement(luku);
...
summa += tau.elementAt(i);
joutuisi kirjoittamaan paljon kömpelömmin:
taulu.addElement(new Integer(luku));
...
summa += ((Integer)(tau.elementAt(i))).intValue();
Hashtable<K,V>
Luokan
Hashtable
ilmentymä on rakenne, jolla
K-tyyppisistä arvoista, avaimista ("key"), voi assosioida
V-tyyppisiä arvoja ("value").
"Avaimella siis haetaan avaimeen liitettyä arvo".
Esimerkki: "Kielenkäännösohjelma".
Ohjelma opiskelee ensin sanapareja
"sana alkukielelellä" - "sana käännettynä toiselle kielelle".
Sitten ohjelma tarjoaa sanojen käännöspalvelun:
import java.util.*;
public class HashtableEsim {
private static Scanner lukija = new Scanner(System.in);
public static void main(String[] args) {
Hashtable<String,String> taulu = new Hashtable<String,String>();
String sana1, sana2;
do {
System.out.print("Sana alkukielellä? (tyhjä lopettaa) ");
sana1 = lukija.nextLine();
if (sana1.equals(""))
break;
if (taulu.containsKey(sana1)) {
System.out.println("Sana \"" + sana1 + "\" löytyy jo!");
System.out.println("Vanha käännös " + "\"" +taulu.get(sana1) +
"\" jää voimaan!");
continue; // uusi alkukielinen sana
}
System.out.print("Sana käännettynä? (tyhjä lopettaa) ");
sana2 = lukija.nextLine();
if (sana2.equals(""))
break; // syöttö loppui!
// nyt molemmat sanat ovat ei-tyhjiä ja ensimmäinen on uusi
taulu.put(sana1, sana2);
} while (true); // loppuu jomman kumman sanan ollessa ""
System.out.println(taulu);
do {
System.out.print("Minkä sanan käännöksen haluat tietää? " +
"(tyhjä sana lopettaa) ");
sana1 = lukija.nextLine();
if (sana1.equals(""))
break; // kysely loppui!
if (!taulu.containsKey(sana1))
System.out.println("Sanan \"" + sana1 +"\" käännös on tuntematon!");
else
System.out.println("Sanan \"" + sana1 + "\" käännös on \"" +
taulu.get(sana1) +"\"");
} while (true); // loppuu tyhjään sanaan
}
}
Esimerkki Vector<E>-olioista Hashtable<K,V>-rakenteessa
Geneerisiä rakenteita voidaan tietenkin myös yhdistellä ihan samaan
tapaan kuin kurssilla aiemmin on tehty vaikkapa olioiden taulukoita
ja olioita, joiden
sisällä on taulukoita. On siis mahdollista esimerkiksi luoda
Vector<E>-oliota, joiden komponentit ovat
Hashtable<K,V>-oliota. Ja päinvastoin.
import java.util.*;
public class HVEsimerkki {
public static void main(String[] args) {
Hashtable<String, Vector<String>> seuraajat =
new Hashtable<String,Vector<String>>();
Vector<String> sanoja = new Vector<String>();
sanoja.addElement("kissa");
sanoja.addElement("hiiri");
sanoja.addElement("hevonen");
seuraajat.put("eläimiä", sanoja);
Vector<String> lukuja = new Vector<String>();
lukuja.addElement("134");
lukuja.addElement("-23");
lukuja.addElement("9871");
seuraajat.put("lukuja", lukuja);
System.out.println(seuraajat);
}
}
Takaisin luvun 5 sisällysluetteloon.
Java and all Java-based marks and logos are trademarks or registered
trademarks of Sun Microsystems, Inc. in the U.S. and other countries.
University of Helsinki is independent of Sun Microsystems, Inc.