Javan pakkauksista¶
Aiemmin Javan lähdekooditiedoston rakennetta kuvaavassa osiossa mainittiin, että tiedoston
alussa voi olla muotoa “package pakkauksen_nimi
” oleva pakkausmääritys. Pakkausmääritys
ilmaisee, että kaikki kyseisessä tiedostossa märitetyt luokat kuuluvat pakkausmäärityksessä
nimettyyn pakkaukseen. Tarkastellaan esimerkiksi seuraavaa yksinkertaista lähdekoodia:
package fi.tuni.programming3;
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello World!");
}
}
Edellä luokka HelloWorld
määritettin kuuluvaksi pakkaukseen fi.tuni.programming3
.
Tämän efekti on, että luokka HelloWorld
onkin oikeastaan tarkemmin ottaen luokka
fi.tuni.programming3.HelloWorld
. Pakkaus määrittää nimiavaruuden, johon luokka kuuluu (ja
tämä mekanismi muistuttaa esim. C++:n nimiavaruuksia). Javassa pakkauksen sisältämiin luokkiin
ja rajapintoihin viitataan samanlaisella pistenotaatiolla kuin luokkien jäseniin. Näin ollen
toisaalta fi.tuni.programming3.HelloWorld
on pakkauksessa fi.tuni.programming3
määritetty luokka HelloWorld
, ja toisaalta fi.tuni.programming3.HelloWorld.main
on
kyseisessä luokassa määritetty main
-funktio.
Edellä pakkauksen nimeksi oli määritetty “fi.tuni.programming3
”, joka on esimerkki
vallitsevasta käytännöstä, että pakkausten nimet pohjautuvat koodin kirjoittajan
(taustaorganisaation) internet-osoitteeseen, jonka osat luetellaan käänteisessä järjestyksessä.
Tässä pakkauksen nimen pohjana toimi Tampereen yliopiston internet-osoite “tuni.fi”, jonka
perään oli lisätty tarkenne programming3
kuvaamaan koodin liittymistä tähän kurssiin.
Ns. oikeassa maailmassa esimerkiksi Googlen julkaisemat Java-kirjastot sijaitsevat
com.google
-alkuisissa pakkauksissa, Microsoftin com.microsoft
-alkuisissa, jne.
Pakkausten avulla voidaan välttää keskenään samannimisten luokkien nimien yhteentörmäys.
Esimerkiksi Javan omassa luokkakirjastossa on kaksi eri luokkaa, joiden nimi on Date
:
toinen on pakkauksessa java.sql
ja toinen pakkauksessa java.util
. Ilman pakkauksia niitä
ei voisi käyttää samassa ohjelmassa, koska Java-kääntäjä ei tietäisi, kumpaan luokkaan nimi
Date
viittaa. Luokkiin kuitenkin voidaan viitata koodissa myös pakkauksen nimen kera, jolloin
edellämainittuihin Date
-luokkiin voidaan viitata yksiselitteisesti muodoissa
java.sql.Date
ja java.util.Date
. Samaan pakkaukseen ei luonnollisestikaan pidä
määrittää kahta keskenään samannimistä luokkaa, koska silloin päädyttäisiin jälleen nimien
yhtentörmäykseen.
Pakkaukset vs import
Aiemmin on mainittu, että lähdekooditiedoston alussa olevilla import
-lauseilla voidaan
tuoda käyttöön muualla määritettyjä luokkia. Tarkemmin ottaen import
-lauseiden roolina
on mahdollistaa jossain muussa pakkauksessa määritettyyn luokkaan viittaaminen luokan
pelkistetyllä nimellä. Jos luokkaan viittaa pakkauksen kera, ei import
-lausetta tarvita.
Esimerkiksi seuraavat kaksi pakkauksessa java.util
määritetyn luokan Arrays
sort
-funktiota käyttävää koodia ovat efektiivisesti identtisiä:
import java.util.Arrays;
public class SortedParameters {
public static void main(String[] args) {
Arrays.sort(args);
for(String arg : args) {
System.out.println(arg);
}
}
}
public class SortedParameters {
public static void main(String[] args) {
java.util.Arrays.sort(args);
for(String arg : args) {
System.out.println(arg);
}
}
}
Kuten edellä mainittiin, import
koskee nimenomaan eri pakkauksissa määritettyjä luokkia.
Kooditiedostosta voi viitata sen kanssa samassa pakkauksessa määritettyihin muihin luokkiin
suoraan pelkällä luokan nimellä tarvitsematta import
-lausetta.
Toinen motiivi pakkausten käyttöön on koodin organisointi niin, että samantapaisiin tehtäviin
liittyvät luokat on määritetty keskenään samaan pakkaukseen. Tämä osaltaan helpottaa koodin
ylläpitoa suurissa ohjelmistoissa, joissa voi olla satoja tai tuhansiakin luokkia. Pakkausten
käyttö itse asiassa suoranaisesti pakottaakin organisoimaan tiedostot hierarkkiseen
hakemistorakenteeseen: Java tulkitsee pakkauksen nimen pisteillä erotelluksi alihakemistopoluksi,
joka kertoo, missä alihakemistossa kyseisen pakkauksen tiedostot sijaitsevat. Esimerkiksi ylempänä
esitetty pakkaukseen fi.tuni.programming3
määritetty luokka HelloWord
pitäisi sijoittaa
alihakemistoon “fi/tuni/programming3
” (tai Windowsissa “fi\tuni\programming3
”). Ohjelmaa
ajettaessa Java-virtuaalikoneelle tulisi antaa luokan nimi pakkauksen kera eli ajo tehtäisiin
tapaan java fi.tuni.programming3.HelloWorld
. Tällöin virtuaalikone aloittaisi suorituksen
luokkatiedostosta fi/tuni/programming3/HelloWorld.class
(tai Windowsissa
fi\tuni\programming3\HelloWorld.class
).
Kun käytetään aiemmin mainittua periaatetta, että pakkaukset nimetään käänteisesti internet-osoitteen mukaan, tulee koodi organisoitua hakemistorakenteeseen, jossa esimerkiksi kaikki saman organisaation koodit ovat keskenään samassa alihakemistossa.
Tällä kurssilla aletaan käyttämään pakkausmäärityksiä vasta siinä vaiheessa, kun koodi organisoidaan irrallisten kooditiedostojen sijaan projektiksi (tarkemmin ottaen tulemme käyttämään ns. Maven-projekteja, missä Maven on eräs käännöstyökalu).