Balíčky

<< Interface-y (rozhrania) | Obsah | Modifikátory viditeľnosti >>

V doterajšom kurze sme používali len triedy s jednoduchými, zvyčajne slovenskými názvami. Mali sme korytnačky Turtle, ale mali sme aj Filmy, či FilmVPocitaci. Java je však stavaná na budovanie obrovských systémov s tisíckami tried rozložených cez desiatky projektov a knižníc. V takýchto megaprojektoch nie je nič výnimočné, keď nastane kolízia v názvoch tried.

Vezmime si reálny príklad: do projektu si zavedieme triedu reprezentujúcu atribút nejakého tovaru (Predávame televízory, ktoré majú atribút farba) a nazveme si ju správne po anglicky Attribute. Lenže beda! Taká trieda už existuje, pretože niekde v hlbinách Javy sa nachádza trieda s rovnakým názvom, ktorou sa nastavujú atribúty tlačovej zostavy. A keď sa rozhodneme zaviesť do projektu knižnicu na spracovanie webových stránok, kde Attribute reprezentuje syntaktický prvok jazyka HTML, máme problém.

Jedným z riešení by bola dohoda a premenovanie tried: náš atribút by sme premenovali na ObjectAttribute, a druhý konflikt vyriešili dohodou s autorom knižnice pre spracovanie HTML. Problém však tkvie v nemožnosti dohodnúť sa s autormi Javy.

Našťastie, takýmto problémom možno ľahko predchádzať, pretože v Jave existujú balíčky.

Balíčky ako analógia súborového systému

V každom rozumnom operačnom systéme existuje adresárová štruktúra, ktorá udržiava poriadok v súboroch. Príklad z Windowsu:

c:\Users\Public\Music\Sample Music\Kalimba.mp3

hovorí, že máme súbor Kalimba.mp3 na disku C v adresári Users\Public\Music\Sample Music. Inak povedané, máme adresár Users s podadresárom Public, ktorý má podadresár Music, atď. (Kto neverí, nech tam beží, príklad je prevzatý z Windows 7.)

Keby adresáre neexistovali (čo bol stav v praoperačných systémoch na 8-bitových počítačoch), veľmi rýchlo by nastal na disku chaos.

Balíčky plnia presne úlohu organizovanosti jednotlivých tried. Každá javovská trieda patrí do niektorého balíčka (tak ako každý súbor je v nejakom adresári) a samotné balíčky sú organizované v hierarchickej štruktúre (práve tak, ako adresár môže obsahovať ďalšie podadresáre). Každá trieda v Jave má úplný názov (fully qualified name), ktorý predstavuje ,,úplnú cestu" v hierarchii balíčkov.

Keď sme napríklad spomínali triedu File, ktorá reprezentuje súbor, v skutočnosti sme sa rozprávali o triede s úplným názvom java.io.File. Balíček java má podbalíček io, ktorý obsahuje triedu File.

Asi je zjavné, že takáto štruktúra predchádza problémom z úvodu. Už nemusíme premenovávať triedy s konfliktnými názvami, stačí, že majú triedy rozličné úplné názvy. A naozaj:

  • trieda z útrob Javy má názov javax.print.attribute.Attribute
  • trieda pre spracovávanie HTML má plný názov org.htmlparser.Attribute
  • a našu triedu môžeme zaradiť napr. do balíčka sk.upjs.ics.paz, čiže bude mať názov sk.upjs.ics.paz.Attribute

Panuje úzus, že názvy balíčkov sa odvodzujú od webových adries vývojára, čím sa zaručí ich jednoznačnosť. (Keďže spadáme pod Ústav informatiky, ktorý má web ics.upjs.sk, je to vhodný kandidát na názov balíčka). Na rozdiel od webových adries sa cesta udáva v obrátenom poradí.

Hierarchia balíčkov

Každá trieda v Jave patrí do nejakého balíčka. Dokonca i úplne triviálna trieda

public class HlúpaVec {
  // tu nie je nič, žiadne metódy!
}

patrí do tzv. implicitného balíčka, ktorý sa nachádza na vrchole hierarchie (je analógiou koreňového adresára).

Ako zaradiť triedu do balíčka

Ak chceme zaradiť triedu do balíčka, potrebujeme spraviť dve veci:

  1. do úplne prvého riadku zadať klauzulu s menom balíčka, v ktorom je trieda
  2. zaradiť ju do správneho adresára

Klauzula package

Vezmime si triedu Film z predošlých príkladov a zaraďme ju do balíčka sk.upjs.ics.paz.filmy. To dosiahneme klauzulou

package sk.upjs.ics.paz.filmy;

Výsledná trieda bude vyzerať:

package sk.upjs.ics.paz.filmy;

public class Film {
...
}

Zaradenie do správneho adresára

O vytvorenie adresárov a zaradenie do správneho balíčka a aj adresára (teda v našom prípade do adresára src/sk/upjs/ics/paz/filmy) sa nám postará Eclipse.

Vytvorme si najprv nový package cez pravý klik na názov projektu > new > package, kde zadáme názov package-u sk.upjs.ics.paz.filmy . Následne si označíme všetky java súbory, ktoré chceme presunúť a metódou drag&drop ich presunieme na nové miesto. Môžeme sa pozrieť, že vo všetkých presunutých súboroch pribudol nový riadok

package sk.upjs.ics.paz.filmy;

Použitie tried z iných balíčkov

V bežnom kóde platí zásada, že vždy keď použijeme názov triedy (napr. v deklarácii dátového typu premennej), tak kompilátor predpokladá, že ide o meno triedy, ktorá sa nachádza v rovnakom balíčku.

V predošlých príkladoch s dedičnosťou filmov sme mali šesť tried: Film, FilmNaDvd, FilmNaPaske, FilmVPocitaci, ZoznamFilmov a Spustac. Skúsme zaradiť všetky triedy do balíčka sk.upjs.ics.paz.filmy.

Ak potom v metóde main() triedy Spustac (teda triedy s plným menom sk.upjs.ics.paz.filmy.Spustac) použijeme triedu FilmNaDvd, kompilátor bude predpokladať, že chceme používať triedu sk.upjs.ics.paz.filmy.FilmNaDvd. Inak povedané, pred názov triedy FilmNaDvd si domyslí plnú cestu v balíčkoch. Kód

package sk.upjs.ics.paz.filmy;

public class Spustac {
        public static void main(String[] args) {
                FilmNaDvd matrix = new FilmNaDvd();
        }
}

bude teda ekvivalentný kódu

package sk.upjs.ics.paz.filmy;

public class Spustac {
        public static void main(String[] args) {
                sk.upjs.ics.paz.filmy.FilmNaDvd matrix = new sk.upjs.ics.paz.filmy.FilmNaDvd();
        }
}

Je však asi zjavné, že takýto kód by bol veľmi rýchlo neprehľadný a teda domýšľanie mien má svoju logiku a zmysel.

Používanie tried z iných balíčkov a importy

Ak chceme používať triedu z iného balíčka, nie je nič jednoduchšie, stačí ju uviesť pomocou jej plného názvu. Tento prístup je ľahký na pochopenie, ale nik to tak nerobí:

package sk.upjs.ics.paz.filmy;

public class Spustac {
        public static void main(String[] args) {
                java.io.File súbor = new java.io.File("data.txt");
        }
}

V triede sk.upjs.ics.paz.filmy.Spustac používame triedu java.io.File a teda sa na ňu musíme odkázať plným menom.

Tento prístup je prostý, pretože sa drží jasnej zásady: Ak chceš používať triedu z iného balíčka, musíš sa na ňu odkázať plným menom. Existujú však projekty, ktoré používajú mimoriadne dlhé názvy, a tento spôsob je pre nich fatálny:

org.springframework.aop.framework.autoproxy.metadata.AttributesThreadLocalTargetSourceCreator c
  = new org.springframework.aop.framework.autoproxy.metadata.AttributesThreadLocalTargetSourceCreator();

Našťastie existuje idea importov, ktoré tento problém odstraňujú.

Importy

Pomocou importov je možné skrátiť zápis kilometrových názvov tried. Dajme si príklad:

package sk.upjs.ics.paz.filmy;

import java.io.File;

public class Spustac {
        public static void main(String[] args) {
                File súbor = new File("data.txt");
        }
}

Klauzula import java.io.File hovorí, že akonáhle sa v kóde spomenie trieda File, znamená to v skutočnosti triedu java.io.File. Import dramaticky skracuje a sprehľadňuje zápisy využívajúce triedy v iných balíčkov.

Importy možno zoskupovať: ak trieda používa viacero importov z jedného balíčka:

import java.io.File;
import java.io.PrintWriter;
import java.io.IOException;

možno zápis skrátiť na

import java.io.*;

Takýto zápis hovorí "importni všetky triedy z balíčka java.io".

Pozor ale: hromadný import sa týka len tried z konkrétneho balíčka. Neimportuje ani triedy z nadradených balíčkov, ani z podbalíčkov!

Rovnako platí, že v importe sa dá použiť len jediná hviezdička: snaha o import všetkých balíčkov Javy zlyhá: java.*.*.*

Automatické importy

Existujú dve situácie, keď import tried prebieha automaticky. Prvú sme si už spomínali: trieda, ktorá sa nachádza v rovnakom balíčku, nemusí byť importovaná.

Druhým prípadom sú triedy z balíčka java.lang. Nemusíme teda importovať bežné Java triedy, teda String (java.lang.String), System (java.lang.System) a pod.

Importy a implicitné balíčky

Trieda, ktorá nemá žiadnu deklaráciu package patrí do implicitného balíčka na vrchole hierarchie. V rozumnej knižnici a projekte by sa však takéto triedy nemali nachádzať, a ak sa tak dialo, tak to bolo kvôli jednoduchosti a z didaktických dôvodov.

Implicitný balíček je síce zdanlivo jednoduchý, ale môže viesť k neporiadku a najmä ku konfliktom v názvoch, ktoré sme spomínali na začiatku. Trieda v tomto balíčku má ešte jednu nevýhodu: nemôže byť importovaná z tried zaradených v balíčkoch.