You also want an ePaper? Increase the reach of your titles
YUMPU automatically turns print PDFs into web optimized ePapers that Google loves.
Sveu£ili²te u Zagrebu<br />
Fakultet elektrotehnike i ra£unarstva<br />
Marko Topolnik<br />
Uvod u programski jezik Java<br />
<strong>Skripta</strong> uz kolegij Seminar Java+XML<br />
Zagreb, 2005.
Sadrºaj<br />
Predgovor<br />
ix<br />
Uvod 1<br />
1 Osnovni elementi jezika Java 3<br />
1.1 Objektna paradigma, enkapsulacija . . . . . . . . . . . . . . 3<br />
1.2 Pokretanje programa pisanih u Javi . . . . . . . . . . . . . . 5<br />
1.2.1 Rad u naredbenom retku . . . . . . . . . . . . . . . . 6<br />
1.2.2 Rad u okruºju Eclipse . . . . . . . . . . . . . . . . . 6<br />
1.3 Pojam klase . . . . . . . . . . . . . . . . . . . . . . . . . . . 7<br />
1.4 Povezivanje objekata, izgradnja objektnog sustava . . . . . . 8<br />
1.5 Inicijalizacija objekta konstruktorom . . . . . . . . . . . . . 9<br />
1.6 Povezivanje objekata agregacijom . . . . . . . . . . . . . . . 11<br />
1.7 Polimorzam i Javino su£elje . . . . . . . . . . . . . . . . . . 13<br />
1.8 Izbjegavanje ponavljanja koda naslježivanjem . . . . . . . . 18<br />
1.9 Klasna hijerarhija, apstraktna klasa . . . . . . . . . . . . . . 20<br />
1.10 Paketi, imenovanje klasa i su£elja . . . . . . . . . . . . . . . 23<br />
1.10.1 Koncepcija paketa . . . . . . . . . . . . . . . . . . . 23<br />
1.10.2 Pravila imenovanja paketa . . . . . . . . . . . . . . . 24<br />
1.10.3 Pravila za raspored datoteka po kazalima . . . . . . . 25<br />
1.10.4 Uloga paketno-privatne razine dostupnosti . . . . . . 25<br />
1.11 Refaktorizacija . . . . . . . . . . . . . . . . . . . . . . . . . 26<br />
1.11.1 Modikator final kontrola polimorzma . . . . . . 26<br />
1.12 Uvoženje vi²e varijanti iste metode preoptere¢enje . . . . . 28<br />
1.13 Prijelaz s agregacije na kompoziciju kopiranje objekata . . 30<br />
2 Razrada detalja jezi£nih svojstava 33<br />
2.1 Varijabla, referenca, objekt . . . . . . . . . . . . . . . . . . . 33<br />
2.1.1 Varijabla primitivne vrste . . . . . . . . . . . . . . . 33<br />
i
ii<br />
SADRšAJ<br />
2.1.2 Varijabla referentne vrste . . . . . . . . . . . . . . . . 34<br />
2.1.3 Opseg vidljivosti (scope) lokalne varijable . . . . . . . 35<br />
2.2 Primitivne vrste podataka . . . . . . . . . . . . . . . . . . . 36<br />
2.3 Numeri£ki i logi£ki izrazi . . . . . . . . . . . . . . . . . . . . 37<br />
2.3.1 Numeri£ki operatori . . . . . . . . . . . . . . . . . . 37<br />
2.3.2 Logi£ki operatori . . . . . . . . . . . . . . . . . . . . 38<br />
2.3.3 Uvjetni (ternarni) operator . . . . . . . . . . . . . . . 39<br />
2.3.4 Operatori sloºene dodjele . . . . . . . . . . . . . . . . 39<br />
2.3.5 Pretvorbe izmežu numeri£kih vrsta . . . . . . . . . . 39<br />
2.4 Enumeracija . . . . . . . . . . . . . . . . . . . . . . . . . . . 40<br />
2.5 Konstrukcije za upravljanje slijedom izvr²avanja . . . . . . . 43<br />
2.6 Sve razine dostupnosti . . . . . . . . . . . . . . . . . . . . . 45<br />
2.6.1 Model klasne enkapsulacije . . . . . . . . . . . . . . . 46<br />
2.7 Stati£ka metoda izuzeta od polimorzma . . . . . . . . . . 46<br />
2.8 Stati£ki atribut, konstanta . . . . . . . . . . . . . . . . . . . 48<br />
2.8.1 Modikator final rekapitulacija . . . . . . . . . . 49<br />
2.9 Pravila oblikovanja identikatora . . . . . . . . . . . . . . . 49<br />
2.10 Upcast i downcast . . . . . . . . . . . . . . . . . . . . . . . . 50<br />
2.10.1 Operator instanceof . . . . . . . . . . . . . . . . . 51<br />
2.11 Implicitni parametar metode this . . . . . . . . . . . . . . 52<br />
2.11.1 Parametar this i polimorzam . . . . . . . . . . . . 53<br />
2.12 Detalji o hijerarhiji klasa . . . . . . . . . . . . . . . . . . . . 55<br />
2.12.1 Korijenska klasa Object . . . . . . . . . . . . . . . . 55<br />
2.12.2 Denicije izraza vezanih uz hijerarhiju . . . . . . . . 55<br />
2.12.3 Ulan£avanje poziva konstruktora . . . . . . . . . . . 56<br />
2.13 Java kao softverska platforma . . . . . . . . . . . . . . . . . 57<br />
3 Najvaºnije ugražene klase 59<br />
3.1 Uvodne upute za snalaºenje u Javinoj dokumentaciji API-ja 59<br />
3.2 Klasa java.lang.Object . . . . . . . . . . . . . . . . . . . 62<br />
3.2.1 Metoda equals . . . . . . . . . . . . . . . . . . . . . 62<br />
3.2.2 Metoda hashCode . . . . . . . . . . . . . . . . . . . . 64<br />
3.2.3 Metoda toString . . . . . . . . . . . . . . . . . . . . 66<br />
3.3 Klasa java.lang.String . . . . . . . . . . . . . . . . . . . 66<br />
3.3.1 Provjera jednakosti znakovnih nizova . . . . . . . . . 67<br />
3.3.2 Nepromjenjivost instanci String-a . . . . . . . . . . 68<br />
3.4 Klasa System . . . . . . . . . . . . . . . . . . . . . . . . . . 69
SADRšAJ<br />
iii<br />
3.5 Klase-omota£i primitivnih vrsta, autoboxing . . . . . . . . . 69<br />
3.6 Konverzija izmežu podatka i njegovog znakovnog zapisa . . . 70<br />
4 Iznimke 73<br />
4.1 Bacanje iznimke prijava iznimne situacije . . . . . . . . . . 74<br />
4.2 Hvatanje iznimke obrada iznimne situacije . . . . . . . . . 76<br />
4.3 Zavr²ne radnje prije bacanja iznimke blok finally . . . . 78<br />
4.4 Vi²estruki blokovi catch . . . . . . . . . . . . . . . . . . . . 79<br />
4.5 Najava iznimke; provjeravana i neprovjeravana iznimka . . . 81<br />
4.5.1 Najava vi²e iznimki . . . . . . . . . . . . . . . . . . . 82<br />
4.6 Klasna hijerarhija iznimki . . . . . . . . . . . . . . . . . . . 82<br />
4.7 Kori²tenje ugraženih i vlastitih iznimki . . . . . . . . . . . . 83<br />
5 Kolekcije objekata 87<br />
5.1 Su£elja Collection, List i Set . . . . . . . . . . . . . . . . 87<br />
5.2 Prelazak preko svih £lanova kolekcije . . . . . . . . . . . . . 90<br />
5.3 Kolekcije i primitivne vrste podataka . . . . . . . . . . . . . 91<br />
5.4 Napredni prelazak po £lanovima kolekcije . . . . . . . . . . . 92<br />
5.5 Mapa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93<br />
5.6 ƒesto kori²teni algoritmi nad kolekcijama . . . . . . . . . . . 94<br />
5.6.1 Implementiranje su£elja java.lang.Comparable . 94<br />
5.6.2 Poredak razli£it od prirodnog su£elje<br />
java.util.Comparator . . . . . . . . . . . . . . 95<br />
5.6.3 Primjer kori²tenja metode Collections.sort . . . . 96<br />
6 Polja 99<br />
6.1 Osnovni elementi sintakse . . . . . . . . . . . . . . . . . . . 99<br />
6.2 Polje kao objekt . . . . . . . . . . . . . . . . . . . . . . . . . 101<br />
6.3 Interakcija s kolekcijama . . . . . . . . . . . . . . . . . . . . 101<br />
7 Komunikacija s okolinom 103<br />
7.1 Tokovi podataka . . . . . . . . . . . . . . . . . . . . . . . . 103<br />
7.2 Pisa£i i £ita£i . . . . . . . . . . . . . . . . . . . . . . . . . . 104<br />
7.3 Komunikacija s korisnikom . . . . . . . . . . . . . . . . . . . 106<br />
7.4 Rad s datotekama . . . . . . . . . . . . . . . . . . . . . . . . 107<br />
7.5 Rad s internetskim konekcijama . . . . . . . . . . . . . . . . 107
iv<br />
SADRšAJ<br />
8 Uzorci u dizajnu koda 109<br />
8.1 Template method . . . . . . . . . . . . . . . . . . . . . . . . 109<br />
8.2 Observer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110<br />
8.3 Adapter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113<br />
8.4 Decorator . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115<br />
9 Savjeti za rad u Eclipse-u 119<br />
9.1 Automatizacija rada na kodu . . . . . . . . . . . . . . . . . 119<br />
9.2 Povijest datoteke . . . . . . . . . . . . . . . . . . . . . . . . 120<br />
9.3 Seljenje projekta s ra£unala na ra£unalo . . . . . . . . . . . 121<br />
9.3.1 Tipi£an problem prilikom selidbe . . . . . . . . . . . 121<br />
9.3.2 Savjeti za uspje²nu selidbu . . . . . . . . . . . . . . . 121<br />
Literatura 123
Popis ispisa<br />
1.1 Klasa SimpleCounter . . . . . . . . . . . . . . . . . . . . . . . 3<br />
1.2 Klasa ModuloCounter s druga£ijom metodom increment . . . . 4<br />
1.3 Izvr²na klasa . . . . . . . . . . . . . . . . . . . . . . . . . . 5<br />
1.4 Program koji koristi dvije instance iste klase . . . . . . . . . 7<br />
1.5 Klasa DifferentialCounter . . . . . . . . . . . . . . . . . . . 8<br />
1.6 Pro²irena varijanta klase SimpleCounter . . . . . . . . . . . . 9<br />
1.7 Kori²tenje konstruktora . . . . . . . . . . . . . . . . . . . . . 10<br />
1.8 Analizator teksta . . . . . . . . . . . . . . . . . . . . . . . . 11<br />
1.9 Analiza vi²edijelnog dokumenta . . . . . . . . . . . . . . . . 12<br />
1.10 Nadomjesna implementacija klase TextFile . . . . . . . . . . 12<br />
1.11 Polimorfni analizator teksta . . . . . . . . . . . . . . . . . . 13<br />
1.12 Su£elje Counter . . . . . . . . . . . . . . . . . . . . . . . . . 14<br />
1.13 Polimorzam na djelu . . . . . . . . . . . . . . . . . . . . . 14<br />
1.14 Jo² jedan primjer kori²tenja polimorzma . . . . . . . . . . . 16<br />
1.15 Obvezivanje na implementaciju su£elja . . . . . . . . . . . . 17<br />
1.16 Naslježivanje osnovna klasa . . . . . . . . . . . . . . . . . 18<br />
1.17 Naslježivanje izvedena klasa . . . . . . . . . . . . . . . . . 19<br />
1.18 Apstraktna klasa . . . . . . . . . . . . . . . . . . . . . . . . 21<br />
1.19 Konkretne klase izvedene iz apstraktne . . . . . . . . . . . . 22<br />
1.20 Nova verzija apstraktne klase . . . . . . . . . . . . . . . . . 27<br />
1.21 Nove konkretne klase . . . . . . . . . . . . . . . . . . . . . . 27<br />
1.22 Pro²ireno su£elje Counter . . . . . . . . . . . . . . . . . . . . 29<br />
1.23 Pro²irenje apstraktne klase . . . . . . . . . . . . . . . . . . . 29<br />
1.24 Dodatak metode za kopiranje u su£elje Counter . . . . . . . . 30<br />
1.25 Klasa SimpleCounter s dodanom podr²kom za kopiranje . . . 30<br />
1.26 Analizator teksta koji koristi kompoziciju . . . . . . . . . . . 31<br />
2.1 Implicitna pretvorba vrste cjelobrojnih podataka . . . . . . . 39<br />
2.2 Eksplicitna pretvorba vrste cjelobrojnih podataka . . . . . . 40<br />
v
vi<br />
POPIS ISPISA<br />
2.3 Jednostavna enumeracija . . . . . . . . . . . . . . . . . . . . 40<br />
2.4 Osnovno kori²tenje enumeracije . . . . . . . . . . . . . . . . 41<br />
2.5 Kori²tenje enumeracije u bloku switch . . . . . . . . . . . . . 41<br />
2.6 Oboga¢ena enumeracija . . . . . . . . . . . . . . . . . . . . . 42<br />
2.7 Kratki primjeri uporabe upravlja£kih konstrukcija . . . . . . 43<br />
2.8 Izjave break i continue koje koriste oznake . . . . . . . . . . . 44<br />
2.9 Model klasne enkapsulacije . . . . . . . . . . . . . . . . . . . 46<br />
2.10 Poziv stati£ke metode . . . . . . . . . . . . . . . . . . . . . . 47<br />
2.11 Poziv stati£ke metode sintaksom kao da je instancina metoda 48<br />
2.12 Stati£ki atribut, javna konstanta . . . . . . . . . . . . . . . . 48<br />
2.13 ModuloCounter s dodatnom metodom setModulus . . . . . . . . 50<br />
2.14 Metoda koja upravlja ModuloCounter-om . . . . . . . . . . . . 51<br />
2.15 Doražena metoda useCounter . . . . . . . . . . . . . . . . . . 52<br />
2.16 Implicitni parametar this . . . . . . . . . . . . . . . . . . . 52<br />
2.17 Neuspio poku²aj uvoženja eksplicitnog parametra this . . . 53<br />
2.18 Neuspio poku²aj kori²tenja metode s eksplicitnim this . . . . 54<br />
2.19 Eksplicitan poziv konstruktora natklase . . . . . . . . . . . . 56<br />
2.20 Poziv konstruktora iste klase . . . . . . . . . . . . . . . . . . 57<br />
3.1 Implementacija metode equals . . . . . . . . . . . . . . . . . 62<br />
3.2 Tipi£na pogre²ka s preoptere¢enjem umjesto nadja£avanjem 63<br />
3.3 Predloºak za generiranje he²-koda . . . . . . . . . . . . . . . 65<br />
3.4 Implementacija metode toString . . . . . . . . . . . . . . . . 66<br />
3.5 Posljedice pogre²nog kori²tenja == za usporedbu String-ova . 67<br />
4.1 ModuloCounter s provjerom parametra . . . . . . . . . . . . . 73<br />
4.2 Bacanje i ponovno bacanje iznimke . . . . . . . . . . . . . . 75<br />
4.3 Izvje²taj o ba£enoj iznimci . . . . . . . . . . . . . . . . . . . 75<br />
4.4 Hvatanje iznimke . . . . . . . . . . . . . . . . . . . . . . . . 76<br />
4.5 Blok finally . . . . . . . . . . . . . . . . . . . . . . . . . . . 78<br />
4.6 Vi²estruki blokovi catch . . . . . . . . . . . . . . . . . . . . 79<br />
4.7 Najava iznimke . . . . . . . . . . . . . . . . . . . . . . . . . 81<br />
4.8 Deniranje vlastite iznimke . . . . . . . . . . . . . . . . . . 84<br />
5.1 Osnovno baratanje kolekcijom . . . . . . . . . . . . . . . . . 87<br />
5.2 Lista i skup . . . . . . . . . . . . . . . . . . . . . . . . . . . 88<br />
5.3 Prolaz po kolekciji petljom enhanced for . . . . . . . . . . . 90<br />
5.4 Kolekcija i primitivne vrste podataka . . . . . . . . . . . . . 91<br />
5.5 Prolaz po kolekciji eksplicitnim kori²tenjem iteratora . . . . 92<br />
5.6 Kori²tenje iteratora za mijenjanje liste . . . . . . . . . . . . 92
POPIS ISPISA<br />
vii<br />
5.7 Klasa Student koja demonstrira mapu . . . . . . . . . . . . . 93<br />
5.8 Metoda compareTo . . . . . . . . . . . . . . . . . . . . . . . . 95<br />
5.9 Komparator za studente koji name¢e poredak po ID-ju . . . 95<br />
6.1 Rad s jednodimenzionalim poljem . . . . . . . . . . . . . . . 99<br />
6.2 Rad s dvodimenzionalnim poljem . . . . . . . . . . . . . . . 99<br />
6.3 Prolaz po svim £lanovima polja petljom enhanced for . . . . 100<br />
6.4 Trokutasto dvodimenzionalno polje . . . . . . . . . . . . . . 100<br />
6.5 Prolaz po polju kori²tenjem eksplicitnog indeksa . . . . . . . 101<br />
6.6 Pretvorbe izmežu polja i liste . . . . . . . . . . . . . . . . . 101<br />
7.1 Rad s izlaznim tokom . . . . . . . . . . . . . . . . . . . . . . 103<br />
7.2 Rad s ulaznim tokom . . . . . . . . . . . . . . . . . . . . . . 104<br />
7.3 Pisa£ i £ita£ . . . . . . . . . . . . . . . . . . . . . . . . . . . 105<br />
7.4 Omota£i podatkovnih tokova . . . . . . . . . . . . . . . . . . 105<br />
8.5 Su£elje oslu²kiva£a dolaznih paketa . . . . . . . . . . . . . . 111<br />
8.6 Jednostavna implementacija oslu²kiva£a dolaznih paketa . . 111<br />
8.7 Komunikacijski £vor s podr²kom za oslu²kiva£e . . . . . . . . 111<br />
8.8 Kori²tenje sustava . . . . . . . . . . . . . . . . . . . . . . . . 112<br />
8.9 Pokazna izvedba adaptera na znakovni tok . . . . . . . . . . 113<br />
8.10 Primjer primjene uzorka Decorator . . . . . . . . . . . . . . 116
Predgovor<br />
Svrha ovog kolegija (to£nije, njegovog dijela posve¢enog jeziku Java) je<br />
ne samo uputiti studente u sintaksu jezika Java, ve¢ prije svega podu£iti<br />
tehnike objektno-orijentiranog programiranja i dizajna sustava objekata.<br />
Na ovaj predmet uglavnom se dolazi s tek minimalnim predznanjem programiranja<br />
u imperativnoj paradigmi i bez razvijenih vje²tina u dizajnu<br />
programskog sustava. To minimalno predznanje naºalost £ak predstavlja i<br />
smetnju u savladavanju Jave i objektno-orijentiranog pristupa jer daje la-<br />
ºan dojam poznatosti jezi£nim konstrukcijama koje ustvari imaju druga£ije<br />
zna£enje i, ²to je jo² vaºnije, druga£iju ulogu u jeziku.<br />
Pri savladavanju nove, objektne paradigme student kao prvo treba dobro<br />
razumjeti osnovna svojstva i mogu¢nosti objekta, ali to jo² uvijek nije<br />
dovoljno za uspje²no rje²avanje zadataka. Jednako je bitno razviti vje²tinu<br />
organiziranja £itavog sustava objekata koji predstavlja rje²enje zadanog<br />
problema. Za mnoge elementarne probleme postoje standardna rje²enja<br />
koja je razvijateljska zajednica prihvatila kao najbolja. Sva takva rje²enja<br />
treba usvojiti umjesto otkrivati toplu vodu vlastitim pristupima. Po£etnik<br />
treba biti svjestan da jo² nije do²ao u poziciju da ima puni pregled nad<br />
razlozima za²to je neko rje²enje bolje od drugog. 1 Takožer, tendencija studenata<br />
je da se za rje²avanje nekih od problema oslanjaju na ve¢ poznate<br />
tehnike iz jezika C i imaju velik otpor pri usvajanju tehnika uobi£ajenih za<br />
Javu.<br />
U£enje programskog jezika u mnogo£emu podsje¢a na u£enje prirodnog<br />
jezika. Iz istog razloga za²to nitko jo² nije nau£io npr. engleski jezik isklju-<br />
£ivo £itaju¢i gramatiku i rje£nik, nitko nije i ne¢e nau£iti Javu isklju£ivo<br />
£itaju¢i ovu skriptu i slu²aju¢i predavanja. Neizostavna aktivnost jest tre-<br />
1 Detaljnija argumentacija ovoga krije se iza fraze shu-ha-ri (usvajanje pravilakr²enje<br />
pravila-napu²tanje pravila) o kojoj se moºe informirati na Webu, npr.<br />
c2.com/cgi/wiki?ThreeLevelsOfAudience<br />
ix
x<br />
Predgovor<br />
ning, a to u ovom kontekstu zna£i vlastoru£no pisanje koda iz nule, uz stalni<br />
nadzor asistenta koji zadaje zadatke, prou£ava va²a rje²enja i konstantno<br />
vas korigira i usmjerava. Dakle, nije ispravno preuzeti zadatak i rje²avati<br />
ga u izolaciji: kriterij uspjeha nije krajnji proizvod sadrºan u datotekama<br />
.java, nego vje²tina koju ste stekli. Zadatak je tek sredstvo, primjer na<br />
kojem se moºe najbolje utvrditi razina va²e vje²tine i, ponajprije, pratiti<br />
podizanje te razine tijekom semestra. U praksi, studenti koji bez interakcije<br />
s asistentom na kraju semestra dolaze s rje²enjem zadatka dijele se u dvije<br />
skupine: oni £iji kod je niske kvalitete i jasno je da nisu ni²ta nau£ili i oni<br />
£iji kod je kvalitetan jer se nadaju da asistent ne¢e shvatiti da taj kod nije<br />
njihovo djelo. U drugoj skupini ima dodu²e i ne²to onih koji zaista posjeduju<br />
potrebne vje²tine, ali tek vrlo malo onih koji su te vje²tine usvojili<br />
samostalno tijekom semestra.<br />
Iako nije dovoljna sama za sebe, ova skripta ima vaºnu ulogu u £itavom<br />
procesu u£enja: da biste uop¢e mogli komunicirati s nekim poznavateljem<br />
Jave, morate ve¢ razumjeti odreženi skup pojmova i stru£nih izraza. Ako<br />
asistent kaºe npr. ovaj atribut ne bi trebao biti stati£ki jer mu je vrijednost<br />
razli£ita za svaku instancu, onaj tko nije ni pro£itao skriptu ne¢e imati<br />
koristi od takvog savjeta jer ga naprosto ne¢e razumjeti. Jednako tako,<br />
prilikom ispitivanja dogodit ¢e vam se da ne razumijete postavljeno pitanje,<br />
iako biste moºda £ak i znali odgovor.<br />
Na kraju, ºelim se izjasniti i po pitanju jedne vrlo pro²irene predrasude:<br />
pisanje dobrog koda nije nikakvo mehanizirano ²trikanje, to je intelektualno<br />
zahtjevan i kreativan proces sa svojom vlastitom estetikom. Ako netko<br />
smatra druga£ije, to je najvjerojatnije stoga ²to nije jo² razvio estetske kriterije<br />
da bi uvidio razliku izmežu lijepog, kreativnog i ruºnog, nabacanog<br />
koda. Ta £injenica predstavlja zna£ajnu prepreku: u£enik prvo treba skupiti<br />
motivaciju da umo£i noge da bi uop¢e do²ao u poziciju da cijeni vlastiti<br />
rad, a tko ne cijeni ono ²to radi ne moºe ni imati dobro mi²ljenje o svojoj<br />
struci.<br />
dr.sc. Marko Topolnik<br />
Zagreb, 1. rujna 2005.
Uvod<br />
Nastava na kolegiju Seminar Java+XML sastoji se iz dva dijela: predavanja<br />
i laboratorijske vjeºbe, s time da je teºi²te na vjeºbama. Organizacija<br />
samih vjeºbi na kolegiju je speci£na. Glavnina termina (oko pet-²est) posve¢ena<br />
je izradi malog softverskog projekta. Termine vjeºbi treba prije<br />
svega koristiti za komunikaciju s asistentom koji ¢e nadzirati va² napredak<br />
na projektu. Ovaj moment je kriti£an jer se va²a ocjena prvenstveno formira<br />
upravo tijekom te komunikacije, a na kraju semestra bit ¢e posebno<br />
ispitivani samo oni studenti koji iz bilo kojeg razloga nisu suraživali s asistentom<br />
tijekom semestra. Kriterij za ocjenu nije ispravan rad aplikacije koju<br />
ste razvili, ve¢ razina vje²tine programiranja koju ste stekli. Izradi projekta<br />
prethode manji zadaci na kojima ¢ete savladati najosnovnije tehnike rada<br />
s razvojnim alatom i pisanja samog koda. Rje²enja zadataka ocjenjuju se<br />
neovisno o projektu.<br />
<strong>Skripta</strong> se odnosi na Javinu verziju 5 (Java 2 Platform Standard Edition<br />
5.0). Sadrºaj ove skripte bit ¢e izlagan na predavanjima, tako da se<br />
studentima preporu£a da unaprijed pro£itaju poglavlje £ija ¢e se materija<br />
izlagati na sljede¢em predavanju.<br />
U skripti je obražen samo osnovni podskup jezika Java. Neka od izostavljenih<br />
podru£ja:<br />
• ugnjeºžene klase nose zna£ajan dio ukupne kompleksnosti jezika i<br />
intenzivno se koriste u programiranju GUI-ja;<br />
• reeksija sluºi za zaobilaºenje ograni£enja Javinog stati£kog odreživanja<br />
vrste izraza;<br />
• model vi²enitnosti omogu¢uje kori²tenje vi²e izvr²nih niti (dretvi) i<br />
sinkroniziran pristup zajedni£kim resursima. Bit ¢e pokriven u predmetu<br />
Konkurentno programiranje.<br />
1
2 Uvod<br />
Takožer je iznimno vaºno imati na umu da je i materija koja se obražuje<br />
u skripti obražena samo sa svrhom uvoda u Javu za po£etnika nikako se ne<br />
smije shvatiti kao potpuna informacija. Gotovo sve ovdje re£eno predstavlja<br />
drasti£no pojednostavljenje u odnosu na preciznu i potpunu istinu, sve u<br />
svrhu lak²e razumljivosti po£etniku. Ako je £itatelj ve¢ poznavatelj Jave i<br />
takva ga pojednostavljenja smetaju, neka slobodno odloºi skriptu jer njemu<br />
ona i ne treba.<br />
Studentu se preporu£a da prvo poglavlje pro£ita u cijelosti jer se tamo<br />
spominje velik broj najvaºnijih pojmova, a tekst je pisan kao jedna pri£a<br />
u kojoj se pojmovi uvode gdje koji zatreba. Tekst daljnjih poglavlja nije<br />
toliko usko povezan pa £itanje po redu nije tako bitno.
Poglavlje 1<br />
Osnovni elementi jezika Java<br />
1.1 Objektna paradigma, enkapsulacija<br />
U objektno-orijentiranom programskom jeziku izrada programa moºe se u<br />
osnovi svesti na izgradnju sustava objekata. Objekti su mežusobno povezani<br />
(znaju jedan za drugog) i mežusobno komuniciraju razmjenom poruka,<br />
²to se svodi na to da jedan objekt poziva metode drugoga. Dakle, pri kretanju<br />
u rje²avanje zadanog problema prvo treba osmisliti koji objekti ¢e biti<br />
potrebni i kako ¢e biti povezani. ƒesto je mogu¢e posti¢i da programski<br />
objekti modeliraju stvarne objekte. Za svaki objekt najvaºnije je utvrditi<br />
²to ¢e se od njega o£ekivati da radi, tj. njegovo pona²anje. U praksi to se<br />
svodi na popis metoda koje ¢e on posjedovati. Za svaku metodu najbitnije<br />
je koje argumente prima i kakvu vrijednost vra¢a. Takožer je bitno na koji<br />
na£in poziv metode utje£e na stanje objekta. O stanju ovisi kako ¢e objekt<br />
odgovarati na budu¢e pozive metoda. Dakle, identi£an poziv metode u razli£itim<br />
trenucima moºe vratiti razli£itu vrijednost, ovisno o stanju u kojem<br />
je objekt zate£en. Stanje objekta u praksi se svodi na vrijednosti njegovih<br />
atributa (unutarnjih varijabli, na engleskom se u kontekstu Jave £esto<br />
koristi i izraz eld). Osnovno na£elo dizajna objekata enkapsulacija ili<br />
zatvaranje nalaºe da stanje objekta mora ostati skriveno, a izvana trebaju<br />
biti vidljive samo one posljedice tog stanja koje su zaista potrebne dakle<br />
utjecaj stanja na povratne vrijednosti metoda. Evo jednostavnog primjera<br />
opisa objekta u Javi:<br />
public class SimpleCounter<br />
Ispis 1.1: Klasa SimpleCounter<br />
3
4 POGLAVLJE 1. OSNOVNI ELEMENTI JEZIKA JAVA<br />
{<br />
private int counterState;<br />
}<br />
public void increment()<br />
{<br />
this.counterState++;<br />
}<br />
public int getState()<br />
{<br />
return this.counterState;<br />
}<br />
Na² objekt opisan klasom SimpleCounter raspolaºe dvjema metodama,<br />
od kojih jedna mijenja njegovo stanje (increment), a druga to stanje dojavljuje<br />
vanjskom svijetu (getState). Dakle, rezultat metode getState je ovisan<br />
o trenutnom stanju objekta. Stanje je pohranjeno u atributu counterState.<br />
To je obi£an cijeli broj (vrste int, poznat iz jezika C). Pri kreiranju objekta<br />
svi brojevni atributi se implicitno inicijaliziraju na vrijednost nula.<br />
UML-ov dijagram klase SimpleCounter prikazan je na slici 1.1.<br />
SimpleCounter<br />
-counterState : int<br />
+increment()<br />
+getState() : int<br />
Slika 1.1: UML-ov dijagram klase SimpleCounter<br />
Vaºno je da svi atributi imaju oznaku private, £ime se ostvaruje na£elo<br />
skrivenosti stanja odnosno enkapsulacije. U protivnom bi ih objektu iza<br />
leža mogli mijenjati drugi objekti. Recimo da imamo malo druga£iju klasu,<br />
ModuloCounter, £ija metoda increment ustvari izgleda ovako:<br />
Ispis 1.2: Klasa ModuloCounter s druga£ijom metodom increment<br />
public class ModuloCounter<br />
{<br />
public void increment()<br />
{<br />
if ( this.counterState == 9 )<br />
this.counterState = 0;<br />
else<br />
this.counterState++;<br />
}
POGLAVLJE 1. OSNOVNI ELEMENTI JEZIKA JAVA 5<br />
}<br />
... atribut counterState i metoda getState isti kao u SimpleCounter ...<br />
dakle, da broja£ cirkularno broji samo do devet i onda se vra¢a na nulu. Kad<br />
atribut counterState ne bi bio privatan, mogao bi se izvana promijeniti na<br />
proizvoljnu vrijednost, npr. 10. Time bi se naru²ila funkcionalnost objekta<br />
jer bi se, kao prvo, broja£ na²ao u nedozvoljenom stanju i, kao drugo, daljnji<br />
pozivi metode increment nastavili bi brojati prema beskona£nosti.<br />
Enkapsulacija, iako ju sam jezik Java ne name¢e, uglavnom je neizostavan<br />
dio dizajna objekta jer spre£ava velik broj programerskih pogre²aka uz<br />
naj£e²¢e malu ili nikakvu ºrtvu u eleganciji koda. Takožer daje programeru<br />
ve¢u slobodu u eventualnom redizajniranju objekta. Imena i podatkovne<br />
vrste (tipovi) atributa mogu se proizvoljno mijenjati dok god se zadrºava<br />
isto pona²anje objekta kakvo se o£ituje u vrijednostima koje vra¢aju metode.<br />
1.2 Pokretanje programa pisanih u Javi<br />
U jeziku Java program se pokre¢e tako da se prvo denira izvr²nu klasu<br />
koja sadrºi posebnu metodu main. Ona ¢e biti implicitno pozvana kad izvr-<br />
²imo klasu, tj. pokrenemo svoj program. Metoda prima parametar polje<br />
znakovnih nizova. U njemu su pobrojani argumenti zadani u naredbenom<br />
retku prilikom pokretanja programa.<br />
Ispis 1.3: Izvr²na klasa<br />
public class AppThatUsesTheCounter<br />
{<br />
public static void main( String[] args )<br />
{<br />
SimpleCounter cntr = new SimpleCounter();<br />
System.out.println( "Po£etno stanje broja£a: " + cntr.getState() );<br />
cntr.increment();<br />
System.out.println( "Stanje nakon poziva metode increment: "<br />
+ cntr.getState() );<br />
}<br />
}<br />
Prikazana metoda main stvara jedan objekt, koristi ga i o rezultatima<br />
izvje²¢uje korisnika ispisom teksta na zaslon. Izraz new SimpleCounter() kreira<br />
novi objekt klase SimpleCounter. Referenca na taj objekt zapisuje se u
6 POGLAVLJE 1. OSNOVNI ELEMENTI JEZIKA JAVA<br />
lokalnu varijablu cntr i nakon toga se preko te varijable mogu pozivati<br />
metode objekta. Ispis koji generira program bi trebao biti sljede¢i:<br />
Po£etno stanje broja£a: 0<br />
Stanje nakon poziva metode increment: 1<br />
1.2.1 Rad u naredbenom retku<br />
Nekim editorom teksta treba stvoriti datoteku s izvornim kodom. Ime datoteke<br />
mora biti identi£no imenu klase + suks .java. Datoteku se prevodi<br />
(kompajlira) Javinim alatom javac (Java compiler):<br />
$ javac AppThatUsesTheCounter.java<br />
Rezultat ovog koraka nije izvr²na datoteka koju se moºe izravno pokrenuti.<br />
Rezultat je klasna datoteka (eng. class le) koja sadrºi binarnu<br />
deniciju klase u posebnom Javinom mežukodu (eng. bytecode, vidjeti odjeljak<br />
2.13). To je izvr²ni kod za tzv. Javin virtualni stroj (eng. Java Virtual<br />
Machine odnosno JVM) posebnu hardversku platformu koja postoji samo<br />
na papiru, a svaka realna hardverska platforma ju emulira. Dakle, u naredbenom<br />
retku treba pokrenuti emulator, tj. JVM, i proslijediti mu ime<br />
na²e izvr²ne klase:<br />
$ java AppThatUsesTheCounter<br />
Vaºno! Kao argument ove naredbe ne navodi se ime klasne datoteke, ve¢<br />
samo ime klase. To ¢e pogotovo biti vaºno kasnije, kad uvedemo pakete<br />
(odjeljak 1.10).<br />
1.2.2 Rad u okruºju Eclipse<br />
Kako pokrenuti ovaj primjer u Eclipse-u (vrijedi za verziju 3.1):<br />
1. File, New, Project... Java Project, Next<br />
2. Upisati ime projekta, uvjeriti se da je uklju£eno Create separate source<br />
and output folders, Finish<br />
3. U okviru Package Explorer na¢i projekt, ekspandirati ga, namjestiti<br />
oznaku na direktorij src.
POGLAVLJE 1. OSNOVNI ELEMENTI JEZIKA JAVA 7<br />
4. File, New, Class. Upisati ime klase. U editorskom okviru otvara se<br />
stvorena datoteka i u njoj ve¢ pi²e prvi redak koda. Upisati ostatak<br />
koda.<br />
5. Pokretanje klase: desni klik na izvr²nu klasu u Package Explorer-u,<br />
Run As, Java Application. Ili, dok je kurzor u editoru doti£ne klase,<br />
upotrijebiti kombinaciju tipaka Alt-Shift-X, J.<br />
6. U okviru Console vidi se ispis programa.<br />
7. Ako bilo koji od okvira nije vidljiv, moºe ga se otvoriti pomo¢u Window,<br />
Show view<br />
1.3 Pojam klase<br />
U ovom trenutku moºe se usvojiti i osnovna ideja klase. Moºemo ju smatrati<br />
predlo²kom za objekte koji prvenstveno propisuje koje atribute ¢e objekt<br />
imati za £uvanje stanja i sadrºi denicije metoda koje ¢e se nad njim mo¢i<br />
pozivati. Objekt se jo² naziva i instancom svoje klase. Sve instance iste klase<br />
imaju isti popis atributa, ali naravno svaka ima svoje vlastite vrijednosti<br />
tih atributa. To se moºe ilustrirati ako izmijenimo tekst gornje metode main<br />
na sljede¢i:<br />
Ispis 1.4: Program koji koristi dvije instance iste klase<br />
public static void main( String[] args )<br />
{<br />
SimpleCounter cntr1 = new SimpleCounter();<br />
SimpleCounter cntr2 = new SimpleCounter();<br />
System.out.println( "Po£etna stanja broja£a: " + cntr1.getState() +<br />
", " + cntr2.getState() );<br />
cntr1.increment();<br />
System.out.println( "Stanja nakon poziva metode increment nad cntr1: "<br />
+ cntr1.getState() + ", " + cntr2.getState() );<br />
}<br />
O£ekivani ispis:<br />
Po£etna stanja broja£a: 0, 0<br />
Stanja nakon poziva metode increment nad cntr1: 1, 0<br />
Klasa AppThatUsesTheCounter s ispisa 1.3 je druga£ija: ona ne opisuje<br />
nikakav smisleni objekt, ve¢ samo sadrºi metodu main koja je poseban slu£aj<br />
i nije normalna metoda objekta. O tome ¢e se vi²e raspravljati kasnije (u<br />
odjeljku 2.7).
8 POGLAVLJE 1. OSNOVNI ELEMENTI JEZIKA JAVA<br />
1.4 Povezivanje objekata, izgradnja objektnog<br />
sustava<br />
Dva objekta moºe se povezati tako da jedan objekt sadrºi atribut koji<br />
pokazuje na drugi objekt, npr.<br />
Ispis 1.5: Klasa DifferentialCounter<br />
public class DifferentialCounter<br />
{<br />
private SimpleCounter cntr1 = new SimpleCounter();<br />
private SimpleCounter cntr2 = new SimpleCounter();<br />
}<br />
public void increment1()<br />
{<br />
this.cntr1.increment();<br />
}<br />
public void increment2()<br />
{<br />
this.cntr2.increment();<br />
}<br />
public int getDifference()<br />
{<br />
return this.cntr2.getState() - this.cntr1.getState();<br />
}<br />
public void reset()<br />
{<br />
this.cntr1 = new SimpleCounter();<br />
this.cntr2 = new SimpleCounter();<br />
}<br />
UML-ovi klasni i objektni dijagram za ovaj slu£aj prikazani su na slikama<br />
1.2 i 1.3.<br />
cntr1<br />
diffCounter1<br />
cntr2<br />
Slika 1.2: Objektni dijagram minimalnog objektnog sustava
POGLAVLJE 1. OSNOVNI ELEMENTI JEZIKA JAVA 9<br />
DifferentialCounter<br />
+increment1()<br />
+increment2()<br />
+getState() : int<br />
-cntr1, cntr2<br />
1 2<br />
SimpleCounter<br />
-counterState : int<br />
+increment()<br />
+getState() : int<br />
Slika 1.3: Klasni dijagram minimalnog objektnog sustava<br />
Objekt klase DifferentialCounter povezan je s dva objekta klase Simple-<br />
Counter i komunicira s njima. Na primjer, kad se pozove metoda getDifference,<br />
objekt ¢e stupiti u kontakt sa svakim od broja£a i zatraºiti njihovo trenutno<br />
stanje. Time je izgražen minimalan sustav objekata. Asocijativni odnos izmežu<br />
DifferentialCounter i SimpleCounter naziva se kompozicijom. Klju£na<br />
karakteristika ovog odnosa je da podreženi objekti postoje isklju£ivo kao<br />
sastavni dio nadreženog i njihovo postojanje je uvjetovano tim objektom. U<br />
to se moºemo uvjeriti ako uvidimo da je DifferentialCounter taj koji stvara<br />
podrežene objekte i da nijedan vanjski objekt ne moºe do¢i do reference<br />
na njih (zapisane su u privatnim atributima, nijedna javna metoda ih ne<br />
vra¢a).<br />
Prikazani slu£aj u kojem se atributi cntr1 i cntr2 inicijaliziraju na mjestu<br />
gdje su i deklarirani samo je jedan od na£ina inicijalizacije atributa. Na<br />
primjer, metoda reset takožer ih (re)inicijalizira, stvaraju¢i nove objekte<br />
SimpleCounter (time se stari objekti implicitno odbacuju i bit ¢e automatski<br />
uklonjeni iz radne memorije postupkom zvanim garbage collection, sakupljanje<br />
sme¢a). Tipi£an na£in inicijalizacije atributa je unutar konstruktora,<br />
o kojem ¢e se pri£ati u sljede¢em odjeljku.<br />
1.5 Inicijalizacija objekta konstruktorom<br />
Svaki put kad upotrijebimo klju£nu rije£ new kojom zatraºimo kreiranje<br />
nove instance zadane klase, implicitno smo zatraºili i pozivanje posebne<br />
inicijalizacijske procedure opisane konstruktorom objekta. Svaka klasa ima<br />
bar jedan konstruktor, a ako ih ima vi²e, oni se razlikuju po broju i vrstama<br />
parametara. Pomo¢u parametara moºemo prenijeti dodatnu informaciju<br />
inicijalizacijskom postupku, £ime postiºemo da se objekt inicijalizira<br />
u onakvo stanje kakvo je potrebno u danom trenutku. Primjerice, moºemo<br />
pro²iriti na²u klasu SimpleCounter:
10 POGLAVLJE 1. OSNOVNI ELEMENTI JEZIKA JAVA<br />
Ispis 1.6: Pro²irena varijanta klase SimpleCounter<br />
public class SimpleCounter<br />
{<br />
private int counterState;<br />
public SimpleCounter( int initialState )<br />
{<br />
this.counterState = initialState;<br />
}<br />
public SimpleCounter()<br />
{<br />
//ne radi ni²ta dodatno; counterState se automatski<br />
//inicijalizira na nulu<br />
}<br />
}<br />
... deklaracije metoda increment i getState kao ranije ...<br />
U kodu 1.6 denirana su dva konstruktora: prvi prima jedan cijeli broj <br />
po£etnu vrijednost broja£a. Drugi je takozvani podrazumijevani konstruktor<br />
onaj koji ne prima nijedan parametar. U ranijem slu£aju (ispis 1.1),<br />
iako nije bio deklariran nijedan, klasi je implicitno dodan upravo ovakav,<br />
prazan podrazumijevani konstruktor. Mežutim, £im deklariramo bilo koji<br />
konstruktor eksplicitno, ni²ta se ne dodaje implicitno. Dakle, klasa ne mora<br />
imati podrazumijevani konstruktor, iako bi se to moglo zaklju£iti sude¢i po<br />
njegovom imenu.<br />
Primjer poziva konstruktora:<br />
Ispis 1.7: Kori²tenje konstruktora<br />
public static void main( String[] args )<br />
{<br />
SimpleCounter cntr1 = new SimpleCounter();<br />
SimpleCounter cntr2 = new SimpleCounter( 3 );<br />
System.out.println( "Po£etna stanja broja£a: " + cntr1.getState() +<br />
", " + cntr2.getState() );<br />
cntr1.increment();<br />
System.out.println( "Stanja nakon poziva metode increment nad cntr1: "<br />
+ cntr1.getState() + ", " + cntr2.getState() );<br />
}<br />
U ovom primjeru cntr1 se inicijalizira podrazumijevanim konstruktorom,<br />
a cntr2 konstruktorom koji putem parametra zaprima ºeljeno po£etno<br />
stanje. O£ekivani ispis:<br />
Po£etno stanje broja£a: 0, 3<br />
Stanje nakon poziva metode increment nad cntr1: 1, 3
POGLAVLJE 1. OSNOVNI ELEMENTI JEZIKA JAVA 11<br />
1.6 Povezivanje objekata agregacijom<br />
Uzmimo da imamo objekt koji sluºi za jednostavnu analizu tekstualne<br />
datoteke ustanovljuje koliko redaka teksta sadrºi. Datoteku predstavlja<br />
objekt vrste TextFile. Nad njim moºemo pozvati metodu readLine koja<br />
£ita jedan redak datoteke i metodu endReached koja nas izvje²tava ima li jo²<br />
redaka za pro£itati. Za £uvanje rezultata brojanja koristi se objekt vrste<br />
SimpleCounter. Objekt-analizator teksta je instanca klase TextAnalyzer:<br />
public class TextAnalyzer<br />
{<br />
private SimpleCounter cntr;<br />
private TextFile file;<br />
private boolean countDone;<br />
}<br />
Ispis 1.8: Analizator teksta<br />
public TextAnalyzer( SimpleCounter cntr, TextFile file )<br />
{<br />
this.cntr = cntr;<br />
this.file = file;<br />
}<br />
public int countLines()<br />
{<br />
if ( !this.countDone )<br />
{<br />
while ( !file.endReached() )<br />
{<br />
file.readLine();<br />
this.cntr.increment();<br />
}<br />
this.countDone = true;<br />
}<br />
return cntr.getState();<br />
}<br />
Najvaºnije je uo£iti da konstruktor klase prima kao parametre instance<br />
klasa SimpleCounter i TextFile. Moºemo u zapisu konstruktora vidjeti da<br />
se reference na te objekte izravno prepisuju u atribute cntr odnosno file.<br />
Klasa TextAnalyzer stoga ima asocijativnu vezu prema SimpleCounter-u i<br />
TextFile-u, ali ovdje se ne radi o kompoziciji, koju smo sreli ranije. To<br />
se vidi po tome ²to TextAnalyzer prima izvana stvorene objekte, tako da<br />
oni nisu njegovi privatni. Ovdje to moºe biti sasvim korisno: recimo da<br />
imamo jedan ve¢i dokument razbijen u vi²e datoteka, npr. jedna datoteka
12 POGLAVLJE 1. OSNOVNI ELEMENTI JEZIKA JAVA<br />
za svako poglavlje. Ono ²to nas zanima je ukupan broj redaka u £itavom<br />
dokumentu. To moºemo posti¢i kori²tenjem na²eg analizatora teksta ovako<br />
(primjer pretpostavlja da imamo dokument razbijen u dvije datoteke):<br />
Ispis 1.9: Analiza vi²edijelnog dokumenta<br />
public static void main( String[] args )<br />
{<br />
TextFile file1 = new TextFile( 15 ); // datoteka od 15 redaka<br />
TextFile file2 = new TextFile( 25 ); // datoteka od 25 redaka<br />
SimpleCounter cntr = new SimpleCounter();<br />
}<br />
TextAnalyzer ta1 = new TextAnalyzer( cntr, file1 );<br />
System.out.println(<br />
"Broj redaka u prvoj datoteci: " + ta1.countLines() );<br />
// Instanciramo analizator druge datoteke, ali s istim broja£em:<br />
TextAnalyzer ta2 = new TextAnalyzer( cntr, file2 );<br />
System.out.println( "Ukupan broj redaka: " + ta2.countLines() );<br />
O£ekivani ispis:<br />
Broj redaka u prvoj datoteci: 15<br />
Ukupan broj redaka: 40<br />
Iz primjera je jasno da je ºivotni vijek objekta broja£a neovisan o<br />
objektu analizatora. Objekt analizatora, nakon ²to je obavio svoj posao,<br />
moºe se ukloniti iz memorije a da to ne utje£e na objekt broja£a. Za takav<br />
slu£aj koristi se izraz agregacija to je op¢enito odnos izmežu cjeline i njenih<br />
sastavnih dijelova, bez obzira na to pripadaju li dijelovi isklju£ivo toj<br />
cjelini ili ne. Uo£imo da kompozicija takožer zadovoljava ovu deniciju, ali<br />
je njena denicija stroºa. Dakle, svaka kompozicija je ujedno i agregacija,<br />
ali obrat ne vrijedi. UML-om se agregacija prikazuje kao na slici 1.4.<br />
TextFile 1 1<br />
TextAnalyzer<br />
-cntr<br />
SimpleCounter<br />
+countLines() : int<br />
-file<br />
+countLines() : int<br />
1<br />
1<br />
+increment()<br />
+getState() : int<br />
Slika 1.4: UML-ov dijagram klasa u odnosu agregacije<br />
U kodu klase TextAnalyzer koristi se objekt vrste TextFile. Umjesto punog<br />
objekta koji radi s pravim datotekama radi isprobavanja koristimo ovaj<br />
jednostavan nadomjestak:
POGLAVLJE 1. OSNOVNI ELEMENTI JEZIKA JAVA 13<br />
Ispis 1.10: Nadomjesna implementacija klase TextFile<br />
public class TextFile<br />
{<br />
private int linesTotal;<br />
private int linesRead;<br />
}<br />
public TextFile( int lineCount )<br />
{<br />
this.linesTotal = lineCount;<br />
}<br />
public void readLine()<br />
{<br />
if ( this.linesRead < this.linesTotal )<br />
this.linesRead++;<br />
}<br />
public boolean endReached()<br />
{<br />
return this.linesRead == this.linesTotal;<br />
}<br />
1.7 Polimorzam i Javino su£elje<br />
U Javi moºemo razdvojeno opisati su£elje objekta prema okolini (dakle<br />
popis metoda s kojima raspolaºe) i implementaciju tog su£elja (sam kod<br />
metoda, atributi potrebni za £uvanje stanja). Posebno je bitno da za jednu<br />
deniciju su£elja moºemo imati mnogo razli£itih implementacija. Ovo je<br />
korisno iz mnogo razloga, a ovdje ¢emo opisati jedan od njih.<br />
Recimo da imamo gornji slu£aj analizatora teksta. šelimo da, ovisno o<br />
konkretnoj primjeni, moºemo dobiti ili ukupan broj redaka ili samo njegovu<br />
posljednju znamenku (brojanje modulo 10, kao u ispisu 1.2). Treba uvesti<br />
samo sitnu izmjenu u kod s ispisa 1.8:<br />
public class TextAnalyzer<br />
{<br />
private Counter cntr;<br />
private TextFile file;<br />
private boolean countDone;<br />
Ispis 1.11: Polimorfni analizator teksta<br />
public TextAnalyzer( Counter cntr, TextFile file )<br />
{<br />
this.cntr = cntr;
14 POGLAVLJE 1. OSNOVNI ELEMENTI JEZIKA JAVA<br />
}<br />
this.file = file;<br />
}<br />
public int countLines()<br />
{<br />
if ( !this.countDone )<br />
{<br />
while ( !file.endReached() )<br />
{<br />
file.readLine();<br />
this.cntr.increment();<br />
}<br />
this.countDone = true;<br />
}<br />
return cntr.getState();<br />
}<br />
Ovaj kod je identi£an kao ranije, osim ²to se umjesto SimpleCounter<br />
koristi vrsta Counter, £iju deniciju jo² nismo vidjeli. Radi se o Javinom<br />
su£elju:<br />
public interface Counter<br />
{<br />
void increment();<br />
int getState();<br />
}<br />
Ispis 1.12: Su£elje Counter<br />
U su£elju, kao ²to smo najavili, nema denicije metoda, ve¢ samo popis<br />
metoda koje objekt s tim su£eljem posjeduje, tj. deklaracije metoda. Konstruktor<br />
klase TextAnalyzer prihvatit ¢e bilo koji objekt koji implementira<br />
to su£elje, a metodi countLines bit ¢e svejedno o kakvoj se to£no implementaciji<br />
radi. Mežutim, razli£ite implementacije rezultirat ¢e razli£itim<br />
pona²anjem metode. Ova karakteristika objektno-orijentiranih jezika zove<br />
se polimorzam ili vi²eobli£nost. Metoda misli da uvijek radi s objektom<br />
iste vrste (Counter), koji se iz te perspektive doima kao da se mijenja od<br />
slu£aja do slu£aja jednom se moºe pona²ati kao SimpleCounter, a drugi<br />
put kao ModuloCounter. Uo£imo i jo² jednu sitnicu: metode u su£elju nisu<br />
ozna£ene kao public jer se to podrazumijeva nemogu¢e je u su£elju imati<br />
metodu koja ne bi bila javna. Klasu TextAnalyzer moºemo koristiti npr.<br />
ovako:<br />
Ispis 1.13: Polimorzam na djelu
POGLAVLJE 1. OSNOVNI ELEMENTI JEZIKA JAVA 15<br />
public static void main( String[] args )<br />
{<br />
TextAnalyzer ta1 =<br />
new TextAnalyzer( new SimpleCounter(), new TextFile( 37 ) );<br />
TextAnalyzer ta2 =<br />
new TextAnalyzer( new ModuloCounter(), new TextFile( 37 ) );<br />
}<br />
int count1 = ta1.countLines();<br />
int count2 = ta2.countLines();<br />
System.out.println( "SimpleCounter je izbrojao " + count1 );<br />
System.out.println( "ModuloCounter je izbrojao " + count2 );<br />
O£ekivani ispis:<br />
SimpleCounter je izbrojao 37<br />
ModuloCounter je izbrojao 7<br />
Prisutnost polimorzma moºemo uo£iti ve¢ u prvom retku koda. Konstruktoru<br />
klase TextAnalyzer, koji je deniran da prima argumente vrste<br />
Counter, proslježuje se referenca na instancu klase SimpleCounter. Jezi£na<br />
pravila to dopu²taju jer objekt SimpleCounter implementira su£elje Counter<br />
(uz sitan dodatak, prikazan u ispisu 1.15) i nad njim je mogu¢e pozvati sve<br />
metode deklarirane u su£elju. U drugom retku istom konstruktoru proslježuje<br />
se referenca na instancu druge klase ovaj put ModuloCounter. U tre¢em<br />
i £etvrtom retku najjasnije se vidi djelovanje polimorzma. Nad ta1 i ta2<br />
poziva se ista metoda s identi£nim argumentom. Time se pokre¢e identi£an<br />
kod metode countLines (ispis 1.11). Mežutim, ta dva poziva rezultiraju razli£itim<br />
povratnim vrijednostima, ²to se manifestira u ispisu u posljednja dva<br />
retka. Razlika nastupa u trenutku izvr²avanja retka this.cntr.increment(),<br />
gdje se donosi odluka koju to£no metodu increment() treba pozvati. U prvom<br />
slu£aju to ¢e biti metoda denirana u klasi SimpleCounter, a u drugom<br />
slu£aju u ModuloCounter. Dakle, taj redak propisuje samo ime metode koju<br />
¢e se pozvati (i parametre koje ona mora imati), ali ne veºe se ni uz jednu<br />
konkretnu metodu. Ovaj postupak zove se dinami£ko povezivanje za razliku<br />
od stati£kog, gdje se u trenutku prevoženja (kompajliranja) donosi<br />
kona£na odluka o tome koja metoda ¢e biti pozvana.<br />
Novu situaciju sa su£eljem moºemo vizualizirati UML-ovim dijagramom<br />
sa slike 1.5. Ovakav stil dizajna, u usporedbi s dizajnom sa slike 1.4, od<br />
presudne je vaºnosti u objektno-orijentiranim jezicima. Gotovo bi se moglo<br />
re¢i da je £injenica da omogu¢uju takav dizajn smisao njihovog postojanja.
16 POGLAVLJE 1. OSNOVNI ELEMENTI JEZIKA JAVA<br />
Uz malu izmjenu na ispisu 1.13 moºemo pokazati jo² jedan tipi£an na£in<br />
kori²tenja polimorzma:<br />
Ispis 1.14: Jo² jedan primjer kori²tenja polimorzma<br />
public static void main( String[] args )<br />
{<br />
Counter cntr1 = new SimpleCounter();<br />
Counter cntr2 = new ModuloCounter();<br />
TextAnalyzer ta1 = new TextAnalyzer( cntr1 );<br />
TextAnalyzer ta2 = new TextAnalyzer( cntr2 );<br />
}<br />
... ostatak identi£an kao gore ...<br />
Ovdje se u prva dva retka varijablama vrste Counter pridjeljuju reference<br />
na instance klasa SimpleCounter odnosno ModuloCounter. Zatim se s tim varijablama<br />
poziva konstruktor klase TextAnalyzer. Ovaj uzorak, gdje se za<br />
vrstu varijable koristi su£elje umjesto konkretne klase, vrlo je uobi£ajen u<br />
praksi. Isto vrijedi i za vrste atributa. Su£elje se redovito koristi i, kao ²to<br />
smo vidjeli u primjeru, za vrste parametara a takožer i za vrste povratnih<br />
vrijednosti metoda. Na£elno, uvijek treba koristiti najop¢enitiju vrsta koja<br />
dolazi u obzir a to je u na²im primjerima su£elje Counter.<br />
TextAnalyzer<br />
+countLines() : int<br />
1<br />
-cntr<br />
«interface»<br />
Counter<br />
1 +increment()<br />
+getState() : int<br />
SimpleCounter<br />
ModuloCounter<br />
+increment()<br />
+getState() : int<br />
+increment()<br />
+getState() : int<br />
Slika 1.5: UML-ov dijagram klasa za polimorfni analizator teksta (klasa<br />
TextFile je ovdje izostavljena)<br />
U Javi svaka klasa mora se unaprijed obvezati koja sve su£elja (moºe ih<br />
biti vi²e) ¢e implementirati. U na²em slu£aju bit ¢e potrebno dodati redak<br />
implements Counter u deklaracije obiju klasa. Puni kod tako izmijenjenih
POGLAVLJE 1. OSNOVNI ELEMENTI JEZIKA JAVA 17<br />
klasa prikazan je na ispisu 1.15. Primijetimo i mali dodatak u konstruktoru<br />
klase ModuloCounter kojim se osigurava od slu£aja zadavanja prevelikog<br />
po£etnog broja uzimanjem ostatka dijeljenja s 10.<br />
Ispis 1.15: Obvezivanje na implementaciju su£elja<br />
public class SimpleCounter<br />
implements Counter<br />
{<br />
private int counterState;<br />
}<br />
public SimpleCounter( int initialState )<br />
{<br />
this.counterState = initialState;<br />
}<br />
public SimpleCounter()<br />
{<br />
}<br />
public void increment()<br />
{<br />
this.counterState++;<br />
}<br />
public int getState()<br />
{<br />
return this.counterState;<br />
}<br />
public class ModuloCounter<br />
implements Counter<br />
{<br />
private int counterState;<br />
public ModuloCounter( int initialState )<br />
{<br />
this.counterState = initialState % 10;<br />
}<br />
public ModuloCounter()<br />
{<br />
}<br />
public void increment()<br />
{<br />
if ( this.counterState == 9 )<br />
this.counterState = 0;<br />
else<br />
this.counterState++;
18 POGLAVLJE 1. OSNOVNI ELEMENTI JEZIKA JAVA<br />
}<br />
}<br />
public int getState()<br />
{<br />
return this.counterState;<br />
}<br />
1.8 Izbjegavanje ponavljanja koda naslježivanjem<br />
Ono ²to nas u ispisu 1.15 moºe zasmetati, a u kompleksnijem kodu moºe<br />
prerasti i u puno ve¢i problem, je ponavljanje koda u dvije o£ito srodne<br />
klase. Obje klase koriste isti atribut, a i metoda getState im je identi£na.<br />
Java omogu¢uje tehniku kojom se takvo ponavljanje moºe izbje¢i naslježivanje<br />
(eng. inheritance). U na²em slu£aju mogli bismo postupiti tako<br />
da propi²emo da klasa ModuloCounter ne nastaje od nule, ve¢ da je izvedena<br />
(eng. derived) iz klase SimpleCounter. U tom slu£aju ona ¢e naslijediti<br />
sve njene metode i atribute. Nakon toga moºemo predenirati (nadja£ati,<br />
eng. override) one metode £ija implementacija vi²e ne odgovara (metodu<br />
increment). Izvodom se izmežu ModuloCounter i SimpleCounter uspostavlja<br />
odnos potklasa-natklasa.<br />
Konstruktori se ne naslježuju i moramo ih uvijek pisati iznova. Mežutim,<br />
u prvom retku konstruktora moºemo pozvati odgovaraju¢i konstruktor<br />
iz natklase i time prepustiti njemu odraživanje zajedni£kog dijela posla.<br />
public class SimpleCounter<br />
implements Counter<br />
{<br />
int counterState;<br />
Ispis 1.16: Naslježivanje osnovna klasa<br />
public SimpleCounter( int initialState )<br />
{<br />
this.counterState = initialState;<br />
}<br />
public SimpleCounter()<br />
{<br />
}<br />
public void increment()<br />
{
POGLAVLJE 1. OSNOVNI ELEMENTI JEZIKA JAVA 19<br />
}<br />
this.counterState++;<br />
}<br />
public int getState()<br />
{<br />
return this.counterState;<br />
}<br />
Ispis 1.17: Naslježivanje izvedena klasa<br />
public class ModuloCounter<br />
extends SimpleCounter<br />
{<br />
public ModuloCounter( int initialState )<br />
{<br />
super( initialState % 10 );<br />
}<br />
}<br />
public ModuloCounter()<br />
{<br />
}<br />
@Override<br />
public void increment()<br />
{<br />
super.increment();<br />
if ( this.counterState == 10 )<br />
this.counterState = 0;<br />
}<br />
U ovom slu£aju bili smo prisiljeni pro²iriti dostupnost atributu counter-<br />
State uklanjanjem modikatora private. Time je njegova dostupnost pro²irena<br />
na paketno-privatnu (eng. package-private) razinu: dostupan je iz svih<br />
klasa unutar istog paketa (o tome kasnije, u odjeljku 1.10), pa tako i iz<br />
potklase ModuloCounter. Prou£imo novu implementaciju metode increment<br />
u potklasi. Implementacija je izvedena tako da se prvo pozove izvorna implementacija<br />
iz natklase (super.increment), a zatim se korigira slu£aj kad<br />
broja£ dože do 10 i treba ga poni²titi. Ova tehnika je vrlo prikladna u u£estaloj<br />
situaciji iz prakse gdje je posao koji obavlja izvorna metoda ve¢inom<br />
ispravan, ali ga treba jo² malo korigirati ili pro²iriti. Op¢enito, nadja£avaju¢a<br />
metoda nema obvezu pozivati metodu iz natklase, a ako ju poziva,<br />
nije nuºno da to £ini u prvom retku koda. Poziv pomo¢u super koristi se i<br />
u zapisu konstruktora vi²e o ovome u odjeljku 2.12.3.
20 POGLAVLJE 1. OSNOVNI ELEMENTI JEZIKA JAVA<br />
Iznad denicije metode increment u ModuloCounter-u pojavljuje se i @Override.<br />
To je tzv. Javin annotation, primjedba uz metodu. Postoje razne vrste<br />
annotation-a, a ovdje kori²ten Override nazna£uje da ta metoda nadja£ava<br />
metodu iz natklase. Njegovo kori²tenje je opcionalno, ali korisno. Time prevoditelju<br />
dajemo do znanja da predvižamo da ta metoda nadja£ava metodu<br />
iz natklase pa ¢e nas on upozoriti ako pogrije²imo i npr. navedemo krivo<br />
ime metode, recimo inkrement. Takve pogre²ke ponekad je te²ko uo£iti jer<br />
¢emo time umjesto nadja£avanja denirati novu metodu a da toga nismo<br />
svjesni.<br />
Uo£imo jo² i sljede¢e: klasa ModuloCounter ne ponavlja izjavu implements<br />
Counter jer se to podrazumijeva potklasa automatski, samom prirodom<br />
naslježivanja, implementira sva su£elja koja implementira njena natklasa.<br />
1.9 Klasna hijerarhija, apstraktna klasa<br />
«interface»<br />
Counter<br />
+increment()<br />
+getState() : int<br />
SimpleCounter<br />
-counterState : int<br />
+increment()<br />
+getState() : int<br />
ModuloCounter<br />
+increment()<br />
Slika 1.6: UML-ov dijagram klasa iz ispisa 1.16<br />
UML-ov dijagram slu£aja iz ispisa 1.16 prikazan je na slici 1.6. Ova organizacija<br />
je rije²ila problem ponavljanja koda. Mežutim, sad se pojavio novi<br />
problem. Dosad je bilo nagla²eno da polimorzam omogu¢uje instancama<br />
razli£itih klasa da se pona²aju kao da su instance su£elja koje implementiraju.<br />
Isto se odnosi i na slu£aj instance izvedene klase ona se moºe koristiti<br />
i kao instanca svoje natklase. U ovom slu£aju to zna£i da sve instance klase
POGLAVLJE 1. OSNOVNI ELEMENTI JEZIKA JAVA 21<br />
ModuloCounter moºemo tretirati kao da je njihova vrsta SimpleCounter. Ako<br />
npr. negdje imamo deklariranu metodu<br />
void setSimpleCounter( SimpleCounter cntr )<br />
ona ¢e uredno prihvatiti i argument vrste ModuloCounter. Mežutim, najvjerojatnije<br />
je da metoda o£ekuje objekt s pona²anjem deniranim u Simple-<br />
Counter i ne¢e ispravno raditi s druga£ijim objektima. U protivnom je mogla<br />
npr. zahtijevati argument op¢enite vrste Counter. Ovu situaciju moºemo izbje¢i<br />
uz uporabu vi²e koda, ali jo² uvijek zadrºavaju¢i tu prednost da se ne<br />
ponavlja deklaracija atributa ni implementacija metode getState. Moºemo<br />
u hijerarhiju ubaciti apstraktnu klasu:<br />
abstract class AbstractCounter<br />
implements Counter<br />
{<br />
int counterState;<br />
Ispis 1.18: Apstraktna klasa<br />
public AbstractCounter( int initialState )<br />
{<br />
this.counterState = initialState;<br />
}<br />
public AbstractCounter()<br />
{<br />
}<br />
public int getState()<br />
{<br />
return this.counterState;<br />
}<br />
}<br />
public abstract void increment();<br />
Glavna novost ovdje je metoda increment, koja je sad postala apstraktna.<br />
Najavljeno je njeno postojanje, ali nema implementacije. Logi£no slijedi da<br />
se ovakvu klasu ne smije mo¢i instancirati jer je opis njenog pona²anja<br />
nepotpun ne zna se ²to bi se trebalo dogoditi prilikom poziva metode<br />
increment. Upravo tu zabranu uvodi modikator abstract nad klasom. Op-<br />
¢enito, svaku klasu smije se proglasiti apstraktnom i time zabraniti njeno<br />
instanciranje bez obzira ima li ona apstraktnih metoda ili ne. S druge<br />
strane, postojanje ijedne apstraktne metode prisiljava nas da takvu klasu<br />
proglasimo apstraktnom.
22 POGLAVLJE 1. OSNOVNI ELEMENTI JEZIKA JAVA<br />
Koji je smisao ovog zahvata? Pogledajmo preureženi kod dviju otprije<br />
poznatih klasa:<br />
Ispis 1.19: Konkretne klase izvedene iz apstraktne<br />
public class SimpleCounter<br />
extends AbstractCounter<br />
{<br />
public SimpleCounter( int initialState )<br />
{<br />
super( initialState );<br />
}<br />
}<br />
public SimpleCounter()<br />
{<br />
}<br />
@Override<br />
public void increment()<br />
{<br />
this.counterState++;<br />
}<br />
public class ModuloCounter<br />
extends AbstractCounter<br />
{<br />
public ModuloCounter( int initialState )<br />
{<br />
super( initialState % 10 );<br />
}<br />
}<br />
public ModuloCounter()<br />
{<br />
}<br />
@Override<br />
public void increment()<br />
{<br />
if ( this.counterState == 9 )<br />
this.counterState = 0;<br />
else<br />
this.counterState++;<br />
}<br />
Ovu situaciju prikazuje UML-ov dijagram na slici 1.7. Sad su obje konkretne<br />
klase izvedene iz AbstractCounter. Sav zajedni£ki kod nalazi se u apstraktnoj<br />
klasi, a metoda increment implementirana je u svakoj konkretnoj<br />
klasi posebno. Takva situacija bolje odgovara ulozi jedne i druge klase, koje
POGLAVLJE 1. OSNOVNI ELEMENTI JEZIKA JAVA 23<br />
su po svojoj prirodi dvije paralelne implementacije su£elja Counter. Uo£imo<br />
jo² na ispisu 1.18 da je za klasu AbstractCounter izostavljen modikator<br />
public. Ona ima pomo¢ni karakter i ne bi trebala sluºiti da se deklariraju<br />
varijable ili parametri £ija vrsta je AbstractCounter. Umjesto toga treba<br />
uvijek koristiti su£elje Counter. Stoga je njena vidljivost smanjena na samo<br />
unutar svog paketa.<br />
«interface»<br />
Counter<br />
+increment()<br />
+getState() : int<br />
AbstractCounter<br />
-counterState : int<br />
+increment()<br />
+getState() : int<br />
SimpleCounter<br />
ModuloCounter<br />
+increment()<br />
+increment()<br />
Slika 1.7: UML-ov dijagram klasa iz ispisa 1.18 i 1.19<br />
1.10 Paketi, imenovanje klasa i su£elja<br />
1.10.1 Koncepcija paketa<br />
Dosad se ve¢ vi²e puta spominjala koncepcija paketa. Vrijeme je da se<br />
razjasni taj pojam, a i da ga se stavi u potpunu uporabu u kodu. Jezik Java<br />
omogu¢ava grupiranje vi²e klasa u jednu cjelinu koja se naziva paketom.<br />
Pripadnost klase paketu objavljuje se u prvom retku datoteke u kojoj je<br />
denirana, npr. ovako:<br />
package hr.fer.tel.jsem;<br />
public interface Counter<br />
{<br />
void increment();<br />
int getState();<br />
}
24 POGLAVLJE 1. OSNOVNI ELEMENTI JEZIKA JAVA<br />
Time se su£elje Counter na²lo u paketu hr.fer.tel.jsem i sada je njegovo<br />
puno ime hr.fer.tel.jsem.Counter. U kodu svih klasa unutar tog istog<br />
paketa ne mora se navoditi puno ime, ali u bilo kojem drugom paketu to<br />
postaje potrebno. Da kod ipak ne bi postao previ²e zagu²en ovako duga£kim<br />
imenima, Java podrºava i uvoz pojedinih imena klasa/su£elja tako da<br />
se na njih moºe pozivati samo kratkim imenom. Za to sluºi klju£na rije£<br />
import koja se pojavljuje odmah ispod deklaracije paketa:<br />
package hr.fer.tel.jsem;<br />
import java.util.List;<br />
import java.util.ArrayList;<br />
import javax.swing.*;<br />
public class Xyz<br />
{<br />
...<br />
}<br />
Kori²enjem zvjezdice, kao u javax.swing.*, uvozi se kompletan paket.<br />
Postoji i poseban paket java.lang koji je automatski uvezen, kao da je uvijek<br />
navedeno import java.lang.*. U njemu su najvaºnije ugražene klase kao<br />
²to su String i Object.<br />
1.10.2 Pravila imenovanja paketa<br />
U praksi se paketi redovito koriste, tako da sav na² dosada²nji kod u tom<br />
smislu odudara od prakse i svaku datoteku treba dopuniti tom deklaracijom.<br />
Ve¢ je vjerojatno o£ito da su na snazi i posebna pravila imenovanja<br />
paketa: on se imenuje prema imenu internetske domene organizacije pod<br />
£ijom kapom se izražuje kod, i to tako da se kre¢e od segmenta najvi²e<br />
hijerarhijske razine. Na² Zavod ima domenu tel.fer.hr i stoga sva imena<br />
paketa izraženih na Zavodu trebaju po£injati na hr.fer.tel. Za sasvim<br />
male projekte bit ¢e dovoljan jedan paket, ali ve¢ za samo malo ve¢e trebat<br />
¢e ih obi£no vi²e. U tom slu£aju opet ¢emo imati glavno ime, kao u na²em<br />
primjeru hr.fer.tel.jsem, na koje ¢e se dodavati jo² segmenata po potrebi.<br />
Paketi se primjenjuju prije svega zato da ne dože do sudara izmežu<br />
imena klasa koje kreiraju razli£iti autori i imaju razli£ite namjene. Upravo<br />
zato su denirana gornja pravila imenovanja paketa koja osiguravaju jedinstvenost<br />
njegovog imena. Iz istog razloga treba dobro paziti koja imena
POGLAVLJE 1. OSNOVNI ELEMENTI JEZIKA JAVA 25<br />
se uvoze da se ne bi uvezlo pogre²no. Na primjer, u standardnu biblioteku<br />
klasa koja dolazi s Javom ugraženi su i java.awt.List i java.util.List, dvije<br />
klase vrlo razli£itih namjena. Uvozom pogre²nog imena do¢i ¢e do vrlo £udnih<br />
pogre²aka gdje se prevoditelj (kompajler) buni za svaku metodu koju<br />
pozivamo, iako sve izgleda ispravno.<br />
1.10.3 Pravila za raspored datoteka po kazalima<br />
Ime paketa u kojem je klasa povla£i za sobom i propis u kojem kazalu<br />
ona mora biti smje²tena u sustavu datoteka. Po£ev²i od nekog proizvoljnog<br />
kazala koje se progla²ava korijenskim potrebno je izgraditi hijerarhiju<br />
kazala identi£nu hijerarhiji segmenata u imenu paketa. Konkretno, datoteka<br />
Counter.java mora se na¢i u kazalu /hr/fer/tel/jsem. Ime<br />
same datoteke takožer je propisano i mora biti jednako imenu klase deklarirane<br />
u njoj. Bez obzira na ovakav hijerarhijski na£in organiziranja paketa<br />
po kazalima, formalno izmežu dva paketa razli£itog imena ne postoji nikakva<br />
povezanost. Dakle, paket hr.fer nije u ni²ta bliºem odnosu s paketom<br />
hr.fer.tel nego s npr. paketom com.sun.<br />
Svaki razvojni alat automatski se brine za po²tivanje svih ovih pravila<br />
i stoga je bitno koristiti tu njegovu podr²ku. Na primjer, u ovom trenutku<br />
trebalo bi sve datoteke, koje su dosad bile u neimenovanom, tzv. podrazumijevanom<br />
ili korijenskom paketu, preseliti u paket hr.fer.tel.jsem. Jedan<br />
zgodan na£in u okruºju Eclipse je da se u Package Explorer-u ozna£i sve<br />
klase i pritisne kombinacija tipaka Alt-Shift-V, £ime se zahtijeva preseljenje<br />
klasa u drugi paket. Doti£ni jo² ne postoji pa ga se u prozoru koji se<br />
pojavio stvori pomo¢u gumba New.<br />
1.10.4 Uloga paketno-privatne razine dostupnosti<br />
Sad se moºe bolje razumjeti ono ²to se ranije govorilo o razini dostupnosti<br />
atributa, metoda i klasa. Svaki razvojni projekt u Javi oslanja se na velik<br />
broj klasa i su£elja koja ve¢ postoje mnogo njih dolazi ve¢ kao dio samog<br />
jezika, odnosno dostupan je odmah nakon instaliranja Jave na ra£unalo.<br />
Ve¢ina samih softverskih proizvoda takožer su takve prirode da trebaju<br />
sluºiti drugim razvijateljima kao gradivni elementi. Korisniku svih tih paketa<br />
ne¢e biti dostupni oni dijelovi koji nisu ozna£eni kao javni (public).<br />
Time razvijatelj paketa moºe imati bolju kontrolu nad time kako ¢e se
26 POGLAVLJE 1. OSNOVNI ELEMENTI JEZIKA JAVA<br />
njegov kod koristiti: olak²ava korisniku ispravno kori²tenje, a i zadrºava<br />
slobodu kasnijih izmjena nad svim dijelovima koji nisu javni. Na primjer,<br />
uklanjanje oznake public s klase AbstractCounter sprije£it ¢e korisnka na²eg<br />
paketa da radi izravno s tom klasom. Kasnije ju moºemo npr. preimenovati<br />
ili moºda potpuno ukinuti bez utjecaja na korisnika na²eg paketa.<br />
1.11 Refaktorizacija<br />
Kako smo napredovali u izgradnji koda na²eg primjera, u vi²e navrata radili<br />
smo prepravke u kodu s ciljem njegove bolje organizacije (npr. izbjegavanje<br />
ponavljanja), a bez utjecaja na pona²anje objekata. Takve izmjene u£estale<br />
su u razvoju softvera i nazivaju se refaktorizacijom. Gledaju¢i trenutno<br />
stanje na²eg koda (ispisi 1.18 i 1.19), opet moºemo uo£iti jednu sitnicu<br />
koja bi se mogla popraviti. Klasa ModuloCounter na dva neovisna mjesta<br />
name¢e ciklus brojanja od nula do devet: posebno u konstruktoru i u metodi<br />
increment. Takva situacija otvara mogu¢nost za nekonzistentnost moºe se<br />
gre²kom dogoditi da se na jednom mjestu name¢e npr. ciklus od nula do 7,<br />
a na drugom do 9. Poºeljno je provesti jo² jednu refaktorizaciju koja ¢e ovo<br />
otkloniti. Vi²e je na£ina za to, ali ovdje ¢emo pokazati jedan koji sadrºi i<br />
neke novosti u dizajnu objekata.<br />
Dosad smo se susreli s enkapsulacijom atributa kao jednom od najosnovnijih<br />
tehnika dizajna. Mežutim, £esto je korisno enkapsulirati i metode. To<br />
¢emo ovdje iskoristiti: uvest ¢emo novu, paketno-privatnu metodu setState<br />
koja ¢e se pozivati prilikom svake promjene stanja i u kojoj ¢e se provoditi<br />
kontrola nailaska na gornju granicu ciklusa. Dakle, svaka od dviju na²ih<br />
konkretnih klasa imat ¢e svoju implementaciju setState, a time ¢e nestati<br />
potreba da metoda increment ima razli£ite implementacije ona ¢e se oslanjati<br />
na promjenjivo pona²anje metode setState. Za takvu metodu kaºe<br />
se da je polimorfna. Konstruktor ¢e takožer pozivati ovu metodu. Metoda<br />
setState nije dio javnog su£elja objekta i stoga se denicija Counter-a ne<br />
mijenja. Iz perspektive koda koji barata objektima vrste Counter nije se<br />
dogodila nikakva promjena.<br />
1.11.1 Modikator final kontrola polimorzma<br />
U novi kod apstraktne klase dodan je jo² jedan detalj: modikatorom final<br />
ozna£ene su metode koje je zabranjeno nadja£avati. Uvijek je dobro svaku
POGLAVLJE 1. OSNOVNI ELEMENTI JEZIKA JAVA 27<br />
metodu za koju je predviženo da bude ista za sve potklase ozna£iti kao<br />
final. Polimorzam, osim ²to daje eksibilnost, moºe i oteºati £itanje koda<br />
upravo zbog toga ²to pravila odabira metode koja ¢e biti pozvana postaju<br />
znatno sloºenija. Oznaka final na metodi garantira da nije potrebno<br />
pregledavati sve potklase da se ustanovi nije li neka od njih nadja£ala tu<br />
metodu. To je prakti£nije i za programera koji dodaje novu potklasu jer<br />
taj modikator isti£e ulogu metode u £itavoj organizaciji klasa i ukazuje<br />
na to kako ispravno uvesti novu klasu.<br />
package hr.fer.tel.jsem;<br />
abstract class AbstractCounter<br />
implements Counter<br />
{<br />
int counterState;<br />
Ispis 1.20: Nova verzija apstraktne klase<br />
public AbstractCounter( int initialState )<br />
{<br />
setState( initialState );<br />
}<br />
public AbstractCounter()<br />
{<br />
}<br />
public final int getState()<br />
{<br />
return this.counterState;<br />
}<br />
public final void increment()<br />
{<br />
setState( getState() + 1 );<br />
}<br />
}<br />
abstract void setState( int newState );<br />
U ispisima koji slijede modikatorom final ozna£ene su klase. Kad djeluje<br />
na klasu, ovaj modikator zabranjuje deklaraciju klase izvedene iz nje.<br />
Time se ponovo osigurava od polimorzma tamo gdje je on nepoºeljan <br />
ako npr. parametar neke metode ima vrstu SimpleCounter, garantirano je<br />
da ¢e prosliježeni objekt biti instanca upravo te klase, a ne neke potklase s<br />
izmijenjenim pona²anjem. Takav nepoºeljan slu£aj upravo smo imali ranije,<br />
prije uvoženja apstraktne klase.
28 POGLAVLJE 1. OSNOVNI ELEMENTI JEZIKA JAVA<br />
package hr.fer.tel.jsem;<br />
Ispis 1.21: Nove konkretne klase<br />
public final class SimpleCounter<br />
extends AbstractCounter<br />
{<br />
public SimpleCounter( int initialState )<br />
{<br />
super( initialState );<br />
}<br />
}<br />
public SimpleCounter()<br />
{<br />
}<br />
@Override<br />
void setState( int newState )<br />
{<br />
this.counterState = newState;<br />
}<br />
package hr.fer.tel.jsem;<br />
public final class ModuloCounter<br />
extends AbstractCounter<br />
{<br />
public ModuloCounter( int initialState )<br />
{<br />
super( initialState );<br />
}<br />
}<br />
public ModuloCounter()<br />
{<br />
}<br />
@Override<br />
void setState( int newState )<br />
{<br />
this.counterState = newState % 10;<br />
}<br />
1.12 Uvoženje vi²e varijanti iste metode preoptere¢enje<br />
Ovisno o tome za ²to nam sve treba broja£, mogao bi se pojaviti npr.<br />
zahtjev da treba omogu¢iti i ve¢e korake brojanja umjesto samo po jedan.
POGLAVLJE 1. OSNOVNI ELEMENTI JEZIKA JAVA 29<br />
U tom slu£aju mogli bismo poja£ati na²e objekte novom varijantom metode<br />
increment koja prima i jedan brojevni parametar. Tada ¢emo imati dvije<br />
metode istog imena, ali razli£itih parametara to se zove preoptere¢enjem<br />
metode (eng. overloading). ’to se ti£e jezika Java, te dvije metode jednako<br />
su razli£ite kao da imaju i razli£ita imena jer se potpuni identitet metode<br />
sastoji od njenog imena i popisa vrsta parametara koje prima. Ta cjelina<br />
naziva se potpisom metode (eng. signature). Konkretno, ovdje ¢emo imati<br />
metode increment() i increment( int ). Uo£imo da s druge strane povratna<br />
vrijednost ne ulazi u potpis metode i ne smije se denirati dvije metode<br />
koje su iste po svemu osim po povratnoj vrijednosti.<br />
public interface Counter<br />
{<br />
void increment();<br />
void increment( int step );<br />
int getState();<br />
}<br />
package hr.fer.tel.jsem;<br />
Ispis 1.22: Pro²ireno su£elje Counter<br />
Ispis 1.23: Pro²irenje apstraktne klase<br />
abstract class AbstractCounter<br />
implements Counter<br />
{<br />
.. po£etak koda identi£an kao ranije ...<br />
}<br />
public final void increment()<br />
{<br />
increment( 1 );<br />
}<br />
public final void increment( int step )<br />
{<br />
setState( getState() + step );<br />
}<br />
abstract void setState( int newState );<br />
Moºemo uo£iti tipi£an pristup koji se koristi u izgradnji sustava preoptere¢enih<br />
metoda: metoda koja prima cijeli broj je op¢enitija, a metoda<br />
bez parametara je u biti njen poseban slu£aj u kojem se podrazumijeva jedini£ni<br />
korak. Stoga se poziv metode bez parametara delegira na op¢enitiju<br />
metodu, koju se poziva s podrazumijevanom vrijednosti.
30 POGLAVLJE 1. OSNOVNI ELEMENTI JEZIKA JAVA<br />
Primjer kori²tenja preoptere¢enih metoda:<br />
public static void main( String[] args )<br />
{<br />
Counter cntr = new SimpleCounter();<br />
cntr.increment();<br />
System.out.println( "Stanje nakon poziva increment(): "<br />
+ cntr.getState() );<br />
cntr.increment( 2 );<br />
System.out.println( "Stanje nakon poziva metode increment( 2 ): "<br />
+ cntr.getState() );<br />
}<br />
1.13 Prijelaz s agregacije na kompoziciju kopiranje<br />
objekata<br />
U ispisu 1.11 denirana je klasa koja primjenjuje agregaciju, ²to je ilustrirano<br />
slikom 1.4. Konstruktor klase TextAnalyzer zaprima gotovu instancu<br />
broja£a i ugražuje ju u svoju instancu. U ve¢ini situacija iz prakse ovdje<br />
¢e ustvari isto biti potrebna prava kompozicija da bi se analizator teksta<br />
za²titio od neºeljenih intervencija izvana. Ako ºelimo imati oba svojstva <br />
i da se objekt dobiva izvana, i da se primjenjuje £ista kompozicija bit<br />
¢e potrebno omogu¢iti kopiranje objekata. Analizator teksta zaprimit ¢e<br />
objekt izvana i od njega napraviti obrambenu kopiju (eng. defensive copy)<br />
identi£an objekt, ali privatan. Daljnje promjene nad objektom dobivenim<br />
izvana ne¢e utjecati na njegov rad. U Javi postoji poseban mehanizam za<br />
ovo metoda clone mežutim, on se zbog odreženih tehni£kih problema<br />
po£eo izbjegavati. Ovdje ¢emo stoga prikazati preporu£enu alternativu <br />
kopiraju¢i konstruktor (eng. copy constructor) u kombinaciji s posebnom<br />
metodom getCopy. Su£elje broja£a moramo pro²iriti ovom metodom:<br />
Ispis 1.24: Dodatak metode za kopiranje u su£elje Counter<br />
public interface Counter<br />
{<br />
void increment();<br />
void increment( int step );<br />
int getState();<br />
Counter getCopy();<br />
}<br />
Implementacije ove metode koristit ¢e kopiraju¢e konstruktore. Ovdje se<br />
donosi primjer za SimpleCounter; za ModuloCounter postupa se analogno.
POGLAVLJE 1. OSNOVNI ELEMENTI JEZIKA JAVA 31<br />
Ispis 1.25: Klasa SimpleCounter s dodanom podr²kom za kopiranje<br />
package hr.fer.tel.jsem;<br />
public final class SimpleCounter<br />
extends AbstractCounter<br />
{<br />
public SimpleCounter( int initialState )<br />
{<br />
super( initialState );<br />
}<br />
}<br />
public SimpleCounter()<br />
{<br />
}<br />
// Kopiraju¢i konstruktor -- broja£ se postavlja na<br />
// istu vrijednost kao u izvorniku.<br />
// Privatan! Sluºi samo kao podr²ka metodi getCopy<br />
private SimpleCounter( SimpleCounter cntr )<br />
{<br />
this.counterState = cntr.counterState;<br />
}<br />
public Counter getCopy()<br />
{<br />
return new SimpleCounter( this );<br />
}<br />
void setState( int newState )<br />
{<br />
this.counterState = newState;<br />
}<br />
Metodu getCopy, naºalost, ne moºemo rije²iti op¢enito i staviti u apstraktnu<br />
klasu jer u svakoj klasi treba stvarati instancu upravo te klase.<br />
Klasu TextAnalyzer jednostavno je prepraviti da koristi mehanizam kopiranja.<br />
Potrebna je samo sitna izmjena u konstruktoru 1 :<br />
Ispis 1.26: Analizator teksta koji koristi kompoziciju<br />
public class TextAnalyzer<br />
{<br />
private TextFile file;<br />
private Counter cntr;<br />
public TextAnalyzer( TextFile file, Counter cntr )<br />
1 U konstruktoru se koristi i metoda getCopy iz klase TextFile. Njena implementacija<br />
nije prikazana u skripti.
32 POGLAVLJE 1. OSNOVNI ELEMENTI JEZIKA JAVA<br />
}<br />
{<br />
}<br />
this.file = file.getCopy();<br />
this.cntr = cntr.getCopy();<br />
public int countLines()<br />
{<br />
while ( !file.endReached() )<br />
{<br />
file.readLine();<br />
this.cntr.increment();<br />
}<br />
return cntr.getState();<br />
}
Poglavlje 2<br />
Razrada detalja jezi£nih<br />
svojstava<br />
2.1 Varijabla, referenca, objekt<br />
Jedan od najbitnijih elemenata potrebnih za precizno razumijevanje jezi£nih<br />
konstrukcija u Javi jest pojam varijable. Ono ²to ¢e ovdje biti re£eno<br />
za varijablu vrijedi i za parametre metode, uhva¢ene iznimke i atribute<br />
objekta. Jedna od razlika izmežu lokalne varijable i atributa koju treba<br />
imati na umu je da se atribut inicijalizira automatski, a lokalna varijabla<br />
ne. Prevoditelj (kompajler) ¢e analizirati izvorni kod i prijaviti pogre²ku<br />
ako kod omogu¢ava £itanje varijable prije inicijalizacije.<br />
Varijabla je spremnik u radnoj memoriji kojem je dodijeljeno ime (identikator)<br />
i vrsta podatka koji se u nju sprema. Jezik Java razlikuje varijable<br />
primitivne i referentne vrste.<br />
2.1.1 Varijabla primitivne vrste<br />
Varijabla primitivne vrste je jednostavniji od dva slu£aja i poznat je iz jezika<br />
C. Vrijednost takve varijable je podatak primitivne vrste, npr. boolean,<br />
int ili double. Sam podatak je izravno spremljen u varijablu i vrijednost varijable<br />
moºe se izmijeniti jednostavnom dodjelom:<br />
int i; //deklaracija varijable<br />
i = 2; //prva dodjela vrijednosti (inicijalizacija)<br />
i = 3; //druga dodjela vrijednosti ...<br />
Atributi primitivne vrste inicijaliziraju se na vrijednost nula odnosno, za<br />
logi£ku vrstu, na false. Varijable se usporežuju po vrijednosti na uobi£ajen<br />
33
34 POGLAVLJE 2. RAZRADA DETALJA JEZIƒNIH SVOJSTAVA<br />
na£in:<br />
int i = 0;<br />
int j = 0;<br />
if ( i == j )<br />
System.out.println( "Varijable imaju istu vrijednost" );<br />
else<br />
System.out.println( "Varijable imaju razli£ite vrijednosti" );<br />
(ispisat ¢e se "Varijable imaju istu vrijednost"). Ako varijablu proslijedimo<br />
kao argument metodi, metoda ¢e dobiti vlastitu kopiju vrijednosti varijable,<br />
tako da promjene na toj kopiji ne utje£u na original:<br />
private static void useInt( int i )<br />
{<br />
i++;<br />
}<br />
public static void main( String[] args )<br />
{<br />
int i = 0;<br />
useInt( i );<br />
System.out.println( i ); // ispisuje "0"<br />
}<br />
2.1.2 Varijabla referentne vrste<br />
U varijablu referentne vrste sprema se referenca na objekt. Vrijednost varijable<br />
nikad nije sam objekt, ve¢ uvijek referenca na njega. To je vaºno<br />
imati na umu, pogotovo u sljede¢im situacijama:<br />
• Dvije varijable mogu imati referencu na isti objekt. Manipulacija<br />
objektom putem jedne varijable manifestirat ¢e se i kad se objektu<br />
pristupa putem druge varijable:<br />
Counter cntr1 = new SimpleCounter();<br />
Counter cntr2 = cntr1;<br />
System.out.println( cntr1.getState() ); // ispisuje "0"<br />
cntr2.increment();<br />
System.out.println( cntr1.getState() ); // ispisuje "1"<br />
• Ako se metodi proslježuje objekt putem parametra referentne vrste,<br />
akcije nad objektom obavljene unutar metode ostat ¢e vidljive na<br />
objektu i nakon ²to metoda zavr²i:
POGLAVLJE 2. RAZRADA DETALJA JEZIƒNIH SVOJSTAVA 35<br />
private static void useCntr( Counter cntr )<br />
{<br />
cntr.increment();<br />
}<br />
public static void main( String[] args )<br />
{<br />
Counter cntr1 = new SimpleCounter();<br />
System.out.println( cntr1.getState() ); // ispisuje "0"<br />
useCntr( cntr1 );<br />
System.out.println( cntr1.getState() ); // ispisuje "1"<br />
}<br />
• Vrijednost dviju varijabli je jednaka samo ako sadrºe istu referencu, tj.<br />
pokazuju na jedan te isti objekt. Ako pokazuju svaka na svoj objekt,<br />
njihove vrijednosti ne¢e biti jednake bez obzira na to jesu li sami<br />
objekti mežusobno jednaki:<br />
public static void main( String[] args )<br />
{<br />
Counter cntr1 = new SimpleCounter();<br />
Counter cntr2 = new SimpleCounter();<br />
if ( cntr1 == cntr2 )<br />
System.out.println( "Varijable imaju istu vrijednost" );<br />
else<br />
System.out.println( "Varijable imaju razli£ite vrijednosti" );<br />
}<br />
(ispisat ¢e se Varijable imaju razli£ite vrijednosti). Za usporedbu<br />
samih objekata mora se koristiti posebnu metodu equals (odjeljak<br />
3.2.1).<br />
Varijabla referentne vrste moºe imati i posebnu vrijednost null, ²to<br />
zna£i da ne sadrºi nikakvu referencu. Atributi referentne vrste se automatski<br />
inicijaliziraju na ovu vrijednost.<br />
2.1.3 Opseg vidljivosti (scope) lokalne varijable<br />
Lokalna varijabla uobi£ajeno se deklarira unutar glavnog bloka metode.<br />
Mežutim, op¢enito njena vidljivost proteºe se uvijek od mjesta deklaracije<br />
do kraja bloka u kojem je deklarirana:<br />
public static void method()<br />
{<br />
int i = 0;
36 POGLAVLJE 2. RAZRADA DETALJA JEZIƒNIH SVOJSTAVA<br />
}<br />
if ( i >= 0 )<br />
{<br />
int j = i + 1;<br />
}<br />
j++; // prevoditelj prijavljuje pogre²ku da nije deklarirana<br />
Nijedna deklaracija lokalne varijable ne smije prekriti drugu lokalnu<br />
varijablu koja je na tom mjestu vidljiva:<br />
public static void method()<br />
{<br />
int i = 0;<br />
if ( i >= 0 )<br />
{<br />
int i = 3; // prevoditelj se tuºi da je varijabla 'i' ve¢ deklarirana<br />
}<br />
}<br />
Sintaksa petlje for omogu¢uje da se ve¢ prije otvorene vitice deklarira<br />
varijabla koja ¢e vrijediti unutar bloka petlje:<br />
public static void method()<br />
{<br />
for ( int i = 0; i < 33; i++ )<br />
{<br />
System.out.println( i % 7 );<br />
}<br />
i++; // prevoditelj prijavljuje pogre²ku da nije deklarirana<br />
}<br />
2.2 Primitivne vrste podataka<br />
Jezik Java raspolaºe ograni£enim skupom primitivnih vrsta podataka. Ne<br />
raspolaºe npr. brojevima bez predznaka. Denirane su sljede¢e vrste:<br />
• boolean: logi£ka vrijednost, poprima jednu od dvije konstante: true ili<br />
false.<br />
• byte: 8-bitni cijeli broj (zapisan u jednom oktetu).<br />
• short: 16-bitni cijeli broj.<br />
• char: 16-bitni Unicode-ov znak. Moºe se tretirati i kao broj. Podrºane<br />
su doslovne vrijednosti, npr. 'a', '\n' za znak prelaska u novi redak,<br />
'\t' za znak tabulatora.
POGLAVLJE 2. RAZRADA DETALJA JEZIƒNIH SVOJSTAVA 37<br />
• int: 32-bitni cijeli broj, koristi se za sve cijele brojeve op¢e namjene,<br />
kad nema posebno jakih razloga za kori²tenje nekog drugog.<br />
• long: 64-bitni cijeli broj.<br />
• float: 32-bitni broj s plivaju¢im zarezom. Preciznost je oko 6 decimalnih<br />
mjesta.<br />
• double: 64-bitni broj s plivaju¢im zarezom. Preciznost je oko 15 decimalnih<br />
mjesta. Najbolje je rutinski koristiti double za decimalne<br />
brojeve.<br />
2.3 Numeri£ki i logi£ki izrazi<br />
Jezik Java raspolaºe uobi£ajenim skupom unarnih i binarnih numeri£kih i<br />
logi£kih operatora. Ovdje se donosi njihov kratak pregled.<br />
2.3.1 Numeri£ki operatori<br />
Unarni operatori:<br />
• negacija broja, npr -i<br />
• komplement broja bit-po-bit, npr. £i<br />
• pre-increment, npr. ++i<br />
• pre-decrement, npr. --i<br />
• post-increment, npr. i++<br />
• post-decrement, npr. i--<br />
Binarni operatori:<br />
• zbroj, npr. i + j; isti znak koristi se i za operator ulan£avanja (vidjeti<br />
3.3)<br />
• razlika, npr. i - j<br />
• umnoºak, npr. i * j
38 POGLAVLJE 2. RAZRADA DETALJA JEZIƒNIH SVOJSTAVA<br />
• koli£nik, npr. i / j. Ako su i i j cijeli brojevi, rezultat ¢e biti cijeli<br />
broj puta koliko j stane u i. Drugim rije£ima, rezultat se zaokruºuje<br />
prema nuli odnosno na manju apsolutnu vrijednost.<br />
• ostatak dijeljenja, npr. i % j. Pravilo za rezultat operacije: (i/j)*j +<br />
(i%j) = i<br />
Binarni operatori koji djeluju na razini bitova:<br />
• pomak (shift) ulijevo, npr. i > j. Na ispraºnjena lijeva<br />
mjesta stavlja se vrijednost koju je imao najljeviji bit prije operacije.<br />
Koristi se kad se nad cijelim brojem, bio pozitivan ili negativan, ºeli<br />
posti¢i efekt dijeljenja s 2 j .<br />
• pomak udesno uz zero-extension, npr. i >>> j. Na ispraºnjena lijeva<br />
mjesta stavljaju se nule. Za negativne brojeve naru²ava efekt dijeljenja<br />
potencijom broja dva.<br />
• operacija i, npr. i & j<br />
• operacija ili, npr. i | j<br />
• operacija isklju£ivo ili, npr. i ¢ j<br />
Za usporedbu brojeva koriste se operatori == (jednakost), != (nejednakost),<br />
POGLAVLJE 2. RAZRADA DETALJA JEZIƒNIH SVOJSTAVA 39<br />
• isklju£ivo ili, npr. a ¢ b<br />
• uvjetno i, npr. a && b. Izraz b ¢e se izra£unavati jedino ako je izraz a<br />
bio istinit.<br />
• uvjetno ili, npr. a || b. Izraz b ¢e se izra£unavati jedino ako je izraz<br />
a bio laºan.<br />
Osim za posebne namjene treba uvijek koristiti uvjetne verzije operatora<br />
jer je to ra£unski ekonomi£nije. Jedino u slu£aju kad je nuºno da oba<br />
izraza uvijek budu izra£unata (zbog nekih dodatnih posljedica koje ti izrazi<br />
ostavljaju) treba koristiti bezuvjetne operatore.<br />
2.3.3 Uvjetni (ternarni) operator<br />
Ovaj operator sluºi za odabir izmežu dva izraza od kojih ¢e jedan biti<br />
rezultat £itave operacije ovisno o zadanom uvjetu. Tipi£an primjer uporabe:<br />
public static void showErrorMessage( String msg )<br />
{<br />
String fullMsg = "Error: " + ( msg != null? msg : "" );<br />
System.out.println( fullMsg );<br />
}<br />
Ternarni operator koristi se u izrazu msg != null? msg : "". Prvo se provjerava<br />
uvjet msg != null. Ako je uvjet zadovoljen, rezultat £itavog izraza<br />
je msg, a u protivnom je rezultat prazan niz, "".<br />
Ovaj operator nije primjereno koristiti kao op¢enitu zamjenu za<br />
konstrukciju if-then-else, ve¢ samo u slu£ajevima kao gore, gdje se radi o<br />
sasvim kratkim izrazima i gdje se time postiºe bolja £itljivost koda.<br />
2.3.4 Operatori sloºene dodjele<br />
Za sve binarne operatore za koje je to smisleno postoje i odgovaraju¢i operatori<br />
sloºene dodjele, npr. i += 3 postoje¢oj vrijednosti varijable i dodaje<br />
3, a &= b varijabli a pridjeljuje rezultat operacije a & b, itd.<br />
2.3.5 Pretvorbe izmežu numeri£kih vrsta<br />
Ako se u numeri£kom izrazu koriste podaci razli£itih vrsta npr. int i<br />
long, svi podaci niºe preciznosti bit ¢e implicitno pretvoreni u podatke vi²e<br />
preciznosti. Razmotrimo ovaj primjer kori²tenja cijelih brojeva:
40 POGLAVLJE 2. RAZRADA DETALJA JEZIƒNIH SVOJSTAVA<br />
Ispis 2.1: Implicitna pretvorba vrste cjelobrojnih podataka<br />
int int1 = 1;<br />
long long1 = 1;<br />
long long2 = int1;<br />
long2 = int1 + long1;<br />
int int2 = long1; // pogre²ka!<br />
int2 = int1 + long1; // pogre²ka!<br />
U tre¢em i £etvrtom retku vrijednost vrste int se pridjeljuje varijabli vrste<br />
long i to je ispravno. Mežutim, u petom i ²estom retku poku²ava se vrijednost<br />
vrste long pridijeliti varijabli vrste int. Ovdje prevoditelj prijavljuje<br />
pogre²ku Type mismatch: cannot convert from long to int.<br />
Java op¢enito po²tuje sljede¢e pravilo: ako zatreba pretvorba na ve¢u<br />
preciznost, odvija se automatski; ako zatreba pretvorba na manju preciznost,<br />
potrebno je ekplicitno zatraºiti konverziju. Pogre²ke iz gornjeg primjera<br />
ispravit ¢emo ovako:<br />
Ispis 2.2: Eksplicitna pretvorba vrste cjelobrojnih podataka<br />
int int2 = (int) long1;<br />
int2 = (int) ( int1 + long1 );<br />
Sad prevoditelj ne¢e prijaviti pogre²ku, ali naravno treba imati na umu<br />
opasnost od prekora£enja maksimalnog opsega int-a. Sve ovdje re£eno vrijedi<br />
i za odnose izmežu float i double, kao i izmežu float i double s jedne<br />
strane i cjelobrojnih vrsta s druge strane.<br />
2.4 Enumeracija<br />
U softverskom razvoju vrlo £est slu£aj je da neki podatak moºe poprimiti<br />
svega nekoliko razli£itih vrijednosti. Na primjer, ako razvijamo neku karta²ku<br />
igru, trebat ¢e podatak s £etiri mogu¢e vrijednosti (pik, karo, herc,<br />
tref ). Za takve slu£ajeve treba denirati posebnu klasu, tzv. enumeraciju.<br />
Najosnovniji slu£aj je vrlo jednostavan:<br />
package hr.fer.tel.jsem;<br />
public enum Suit<br />
Ispis 2.3: Jednostavna enumeracija
POGLAVLJE 2. RAZRADA DETALJA JEZIƒNIH SVOJSTAVA 41<br />
{<br />
}<br />
clubs, diamonds, hearts, spades;<br />
Enumeracija je ustvari obi£na klasa s javnim konstantama (vidjeti odjeljak<br />
2.8) £ija vrsta je upravo ta klasa. To moºemo vidjeti na sljede¢em<br />
primjeru kori²tenja enumeracije:<br />
Ispis 2.4: Osnovno kori²tenje enumeracije<br />
public static void main( String[] args )<br />
{<br />
Suit suit1, suit2;<br />
suit1 = Suit.clubs;<br />
suit2 = Suit.hearts;<br />
System.out.println( "Prva boja: " + suit1 + "; druga boja: " + suit2 );<br />
}<br />
Odmah moºemo uo£iti da £lanovi enumeracije imaju ispravno implementiranu<br />
metodu toString koja vra¢a ime £lana.<br />
Konstrukcija switch op¢enito se ne moºe koristiti za referentne vrste, ali<br />
posebno je omogu¢ena za enumeraciju, ²to znatno olak²ava zapis koda kao<br />
u sljede¢em primjeru:<br />
Ispis 2.5: Kori²tenje enumeracije u bloku switch<br />
static void printSuitInCroatian( Suit suit )<br />
{<br />
String suitName;<br />
switch ( suit )<br />
{<br />
case clubs: suitName = "tref"; break;<br />
}<br />
case diamonds: suitName = "karo"; break;<br />
case hearts: suitName = "herc"; break;<br />
case spades: suitName = "pik"; break;<br />
default: suitName = "";<br />
}<br />
System.out.println( "Boja karte je " + suitName );<br />
public static void main( String[] args )<br />
{<br />
printSuitInCroatian( Suit.clubs );<br />
printSuitInCroatian( Suit.hearts );<br />
}<br />
Da nema mogu¢nosti kori²tenja bloka switch, morali bismo pisati ovako:
42 POGLAVLJE 2. RAZRADA DETALJA JEZIƒNIH SVOJSTAVA<br />
if ( suit == Suit.clubs )<br />
suitName = "tref";<br />
else if ( suit == Suit.diamonds )<br />
suitName = "karo";<br />
else if ( suit == Suit.hearts )<br />
suitName = "herc";<br />
else if ( suit == Suit.spades )<br />
suitName = "pik";<br />
else<br />
suitName = "";<br />
Primijetimo da je £lanove enumeracije uvijek sigurno usporeživati po referenci,<br />
operatorom ==, jer je njihova priroda takva da su svaka dva distinktna<br />
£lana mežusobno razli£iti. Dodatna prednost konstrukcije switch je u tome<br />
²to omogu¢uje prevoditelju da nas upozori ako smo slu£ajno izostavili neki<br />
£lan enumeracije.<br />
Sve enumeracije implementiraju i su£elje Comparable (vidjeti odjeljak<br />
5.6.1). Izmežu £lanova enumeracije utvržen je poredak koji odgovara poretku<br />
kojim su navedeni u deniciji enumeracije. To nam omogu¢uje da<br />
napi²emo npr. ovakav kod:<br />
public static void main( String[] args )<br />
{<br />
Suit suit1, suit2;<br />
suit1 = Suit.clubs;<br />
suit2 = Suit.hearts;<br />
int difference = suit1.compareTo( suit2 );<br />
if ( difference == 0 )<br />
System.out.println( "Boje su iste" );<br />
else if ( difference > 0 )<br />
System.out.println( "Prva boja pobježuje!" );<br />
else<br />
System.out.println( "Druga boja pobježuje!" );<br />
}<br />
Kao i ostale klase, i enumeraciju je mogu¢e pro²irivati dodatnim atributima<br />
i metodama. U na²em slu£aju mogli bismo npr. dodati hrvatsko ime<br />
boje:<br />
package hr.fer.tel.jsem;<br />
public enum Suit<br />
{<br />
clubs( "tref" ),<br />
diamonds( "karo" ),<br />
Ispis 2.6: Oboga¢ena enumeracija
POGLAVLJE 2. RAZRADA DETALJA JEZIƒNIH SVOJSTAVA 43<br />
}<br />
hearts( "herc" ),<br />
spades( "pik" );<br />
private String croName;<br />
private Suit( String name )<br />
{<br />
this.croName = name;<br />
}<br />
public String getCroName()<br />
{<br />
return this.croName;<br />
}<br />
Ovdje smo dodali instancin atribut croName, odgovaraju¢i (privatni!)<br />
konstruktor i metodu getCroName. Uz ovako deniranu enumeraciju izvedba<br />
metode printSuitInCroatian postaje trivijalna:<br />
static void printSuitInCroatian( Suit suit )<br />
{<br />
System.out.println( "Boja karte je " + suit.getCroName() );<br />
}<br />
2.5 Konstrukcije za upravljanje slijedom izvr-<br />
²avanja<br />
Jezik Java raspolaºe upravlja£kim konstrukcijama poznatim iz jezika C: if,<br />
while, do-while, for i switch. Ovdje ¢e one biti prikazane samo u najkra¢im<br />
crtama, kroz minimalisti£ki primjer.<br />
Ispis 2.7: Kratki primjeri uporabe upravlja£kih konstrukcija<br />
static void controlStructures()<br />
{<br />
int j = 0, k = 1;<br />
if ( j == k )<br />
{<br />
k++;<br />
}<br />
else if ( j < k )<br />
{<br />
j++;<br />
}<br />
else
44 POGLAVLJE 2. RAZRADA DETALJA JEZIƒNIH SVOJSTAVA<br />
k = 0;<br />
//-------------------------------------<br />
for ( int i = 0; i < 5; i++ )<br />
{<br />
if ( i == j - 2 )<br />
continue;<br />
j += i;<br />
if ( j > 3 )<br />
break;<br />
}<br />
//-------------------------------------<br />
while ( j < 13 )<br />
j++;<br />
//-------------------------------------<br />
do<br />
{<br />
k++;<br />
} while ( k < 13 );<br />
//-------------------------------------<br />
switch ( j )<br />
{<br />
case 0:<br />
k++;<br />
break;<br />
case 1:<br />
j++;<br />
break;<br />
default:<br />
j = k - j;<br />
}<br />
}<br />
Posebnu pozornost zahtijevaju jedino naredbe za izvanredan prekid petlje<br />
(break) ili izravan pomak na sljede¢i korak petlje (continue), pogotovo<br />
u kombinaciji s ugnijeºženim petljama. Kori²tenjem oznaka (eng. label)<br />
mogu¢e je npr. u unutarnjoj petlji narediti izlazak iz vanjske petlje. To je<br />
demonstrirano ovim primjerom:<br />
Ispis 2.8: Izjave break i continue koje koriste oznake<br />
static void breakContinue()<br />
{<br />
outerLoop:<br />
for ( int i = 1; i < 4; i++ )<br />
{<br />
for ( int j = 1; j < 4; j++ )<br />
{<br />
if ( i - j == -1 )<br />
continue outerLoop;<br />
if ( i % j == 1 )<br />
break outerLoop;
POGLAVLJE 2. RAZRADA DETALJA JEZIƒNIH SVOJSTAVA 45<br />
}<br />
}<br />
System.out.println( "i = " + i + ", j = " + j );<br />
O£ekivani ispis je:<br />
i = 1, j = 1<br />
i = 2, j = 1<br />
i = 2, j = 2<br />
i = 3, j = 1<br />
Ove konstrukcije uvedene su u jezik Javu kao zamjena za naj£e²¢i slu£aj<br />
kori²tenja naredbe goto, koja ne postoji u jeziku.<br />
2.6 Sve razine dostupnosti<br />
Dosad smo se susreli s tri razine dostupnosti:<br />
• privatna £lan je dostupan samo u klasi gdje je deniran;<br />
• paketno-privatna £lan je dostupan iz svih klasa u istom paketu;<br />
• javna £lan je dostupan bez ograni£enja.<br />
Java poznaje jo² jednu razinu, ²iru od paketno-privatne koja dodatno<br />
dozvoljava dostup iz svake potklase, bez obzira u kojem paketu se nalazila.<br />
Pristup je jo² uvijek zabranjen klasama iz drugih paketa ako nisu potklase.<br />
Ova razina dostupnosti nazna£uje se klju£nom rije£i protected. Koristi se<br />
prvenstveno u klasi koja je posebno namijenjena da korisnik paketa iz nje<br />
izvodi svoje klase. Sveukupno, razine dostupnosti mogu se sistematizirati<br />
tablicom 2.1.<br />
Tablica 2.1: Pregled razina dostupnosti<br />
razina (eng.) klasa paket potklase ostali<br />
private o<br />
package-private o o<br />
protected o o o<br />
public o o o o
46 POGLAVLJE 2. RAZRADA DETALJA JEZIƒNIH SVOJSTAVA<br />
2.6.1 Model klasne enkapsulacije<br />
Java koristi model enkapsulacije koji je zasnovan ne na instanci, ve¢ na<br />
klasi. Ako je npr. neki atribut ozna£en kao private, to zna£i da je on privatan<br />
za sve instance te klase i nedostupan instancama drugih klasa. Drugim<br />
rije£ima, sve instance iste klase mežusobno imaju neograni£en dostup atributima<br />
i metodama. Na primjer, ovaj kod ne¢e proizvesti prevoditeljsku<br />
pogre²ku:<br />
class Abc {<br />
private int i;<br />
}<br />
Ispis 2.9: Model klasne enkapsulacije<br />
public void manipulateAnotherObject( Abc anotherObject )<br />
{<br />
anotherObject.i++;<br />
}<br />
Ovakav model, iako nije striktno u duhu paradigme objekata s nedodirljivom<br />
unutra²njosti, u praksi je opravdan jer je sav kod koji moºe manipulirati<br />
privatnim dijelovima drugog objekta lociran na istom mjestu pa se<br />
ne mogu pojaviti problemi zbog kojih je i uvedena enkapsulacija da kod<br />
koji koristi neki objekt prestane funkcionirati zbog izmjena na njegovim<br />
unutarnjim detaljima.<br />
2.7 Stati£ka metoda izuzeta od polimorzma<br />
Modikator static ve¢ je usputno spomenut i kori²ten u ispisima koda uz<br />
posebnu metodu main. Njime je mogu¢e ozna£iti i bilo koju drugu metodu.<br />
Time se postiºe da se takva metoda ne poziva nad nekom instancom, ve¢<br />
samostalno. Dakle, ne prosliježuje joj se implicitni argument this (vidjeti<br />
odjeljak 2.11). Argument this klju£an je za funkcioniranje polimorzma i<br />
bez njega ne moºe do¢i do dinami£kog povezivanja.<br />
Takva metoda odgovara pojmu funkcije iz proceduralne paradigme (kao<br />
npr. u jezicima C ili Pascal). Za stati£ke metode koristi se stati£ko povezivanje<br />
tijekom izvr²avanja ne donosi se odluka koju metodu pozvati, to<br />
je ksirano u trenutku prevoženja. Za stati£ke metode ponekad se koristi i<br />
izraz metoda klase odnosno klasina metoda jer je vezana uz klasu, a ne uz<br />
njene instance.
POGLAVLJE 2. RAZRADA DETALJA JEZIƒNIH SVOJSTAVA 47<br />
U ovoj skripti ponekad ¢e se koristiti stati£ke metode, u primjerima za<br />
koje dinami£ko povezivanje nije bitno. Podru£ja primjene u praksi:<br />
• Kad su parametri metode instance tužih klasa koje samo koristimo,<br />
a ne razvijamo pa nemamo dostup njihovom izvornom kodu i ne<br />
moºemo dodati svoju metodu. Takva primjena zna£ajno je oteºana<br />
gubitkom dinami£kog povezivanja.<br />
• Kad su svi parametri primitivne vrste. U tom slu£aju dinami£ko povezivanje<br />
ionako ne dolazi u obzir. Ovakva primjena prikazana je u<br />
primjeru koji slijedi malo niºe.<br />
• Kad metoda ne ovisi ni o jednom parametru. Tada jednostavno nema<br />
razloga za²to ne bi bila stati£ka, a stati£ko povezivanje je ekasnije<br />
od dinami£kog.<br />
Studenti ponekad, ugledaju¢i prevoditeljevu pogre²ku Instance method<br />
used in a static context, problem otklanjaju progla²avanjem metode stati£kom<br />
i time zapravo samo stvaraju dodatne probleme. Pogre²ku treba<br />
otkloniti tako da se jasno ustanovi nad kojom instancom je metoda trebala<br />
biti pozvana i da se kod ispravi u skladu s time.<br />
Ako se stati£ka metoda poziva iz neke druge klase, poziv se zapisuje u<br />
obliku Klasa.stati£kaMetoda:<br />
Ispis 2.10: Poziv stati£ke metode<br />
public class BooleanFunctions<br />
{<br />
public static boolean implies( boolean left, boolean right )<br />
{<br />
return !( left && !right ) ;<br />
}<br />
}<br />
public class Executable<br />
{<br />
public static void main( String[] args )<br />
{<br />
boolean a = true, b = false;<br />
boolean result = BooleanFunctions.implies( a, b );<br />
System.out.println( "" + a + " implies " + b + "? " + result );<br />
}<br />
}<br />
Ispis:
48 POGLAVLJE 2. RAZRADA DETALJA JEZIƒNIH SVOJSTAVA<br />
true implies false? false<br />
Specikacija jezika Java [4] dozvoljava i poziv stati£ke metode istom<br />
sintaksom kao da je instancina metoda, npr:<br />
Ispis 2.11: Poziv stati£ke metode sintaksom kao da je instancina metoda<br />
public class Executable<br />
{<br />
public static void main( String[] args )<br />
{<br />
boolean a = true, b = false;<br />
BooleanFunctions bf = null;<br />
boolean result = bf.implies( a, b );<br />
System.out.println( "" + a + " implies " + b + "? " + result );<br />
}<br />
}<br />
Prevoditelj utvržuje vrstu izraza lijevo od to£ke i ta vrsta mora biti<br />
klasa u kojoj je denirana doti£na metoda. Ovaj na£in zapisa ne treba<br />
nikada koristiti jer samo zavarava, a ne donosi nikakve dodatne prednosti<br />
(nema polimorzma). Primijetimo da je u gornjem primjeru metoda<br />
pozvana putem varijable £ija vrijednost je null, £ime se jasno demonstrira<br />
da se potpuno ignorira vrijednost varijable, bitna je samo njena vrsta.<br />
2.8 Stati£ki atribut, konstanta<br />
Sli£no kao metoda, atribut isto moºe biti stati£ki i time nevezan uz instancu.<br />
Postoji samo jedna, globalna vrijednost takvog atributa. Jedna od naj£e²¢ih<br />
uporaba stati£kih atributa je za deniranje konstanti, npr. mogli bismo u<br />
konstanti denirati podrazumijevanu duljinu ciklusa broja£a ModuloCounter:<br />
Ispis 2.12: Stati£ki atribut, javna konstanta<br />
public class ModuloCounter<br />
{<br />
public static final int DEFAULT_MODULUS = 10;<br />
private int modulus;<br />
public ModuloCounter()<br />
{<br />
this.modulus = DEFAULT_MODULUS;<br />
}<br />
...<br />
}
POGLAVLJE 2. RAZRADA DETALJA JEZIƒNIH SVOJSTAVA 49<br />
2.8.1 Modikator final rekapitulacija<br />
Pri deniranju konstante pojavljuje se i modikator final. U odjeljku 1.11<br />
obja²njeno je kako se on primjenuje na metode i klase radi kontrole polimorzma.<br />
Na atribute i varijable djeluje tako da zabranjuje promjenu njihove<br />
vrijednosti nakon inicijalizacije. Njime se, ako zatreba, moºe ozna£iti<br />
i parametar metode, npr.<br />
public void increment( final int step )<br />
{ ... }<br />
Kori²tenjem modikatora final na svim varijablama gdje je to smisleno<br />
pove¢ava se £itljivost koda jer se time jasno nagla²ava da se ustvari radi<br />
o konstanti. U praksi velik broj, a moºda i ve¢ina varijabli imaju ulogu<br />
konstante.<br />
2.9 Pravila oblikovanja identikatora<br />
U jeziku Java na snazi su neformalna, ali vrlo snaºna pravila oblikovanja<br />
identikatora, od kojih se ne smije odudarati. Naj£vr²¢a pravila ti£u se<br />
rasporeda velikih i malih slova:<br />
• U imenu paketa zabranjeno je koristiti velika slova. Npr. ime<br />
paketa kori²tenog u primjerima je hr.fer.tel.jsem.<br />
• Ime referentne vrste podatka (klasa i su£elje) obavezno po£inje<br />
velikim slovom. Svaka daljnja rije£ u imenu ponovo po£inje velikim<br />
slovom. Npr. SimpleCounter.<br />
• Ime lokalne varijable, parametra, uhva¢ene iznimke, atributa i metode<br />
obavezno po£inje malim slovom. Svaka daljnja rije£ u imenu po-<br />
£inje velikim slovom. Npr. counterState.<br />
• Poseban slu£aj su javne konstante, koje se pi²u velikim slovima, a<br />
rije£i se odvajaju podvlakom. Npr. DEFAULT_MODULUS.<br />
Za sve identikatore koristi se isklju£ivo engleski jezik. Za su£elje<br />
se uvijek odabire najkra¢e, naj£i²¢e ime bez posebnih preksa ili suksa<br />
kao u na²em primjeru Counter. To je stoga ²to se upravo ime su£elja<br />
najvi²e koristi u kodu. Ime apstraktne klase u vrhu hijerarhije uobi£ajeno
50 POGLAVLJE 2. RAZRADA DETALJA JEZIƒNIH SVOJSTAVA<br />
je napraviti od imena su£elja s preksom Abstract npr. AbstractCounter.<br />
Konkretne klase uobi£ajeno zadrºavaju ime svog su£elja na zadnjem mjestu,<br />
a naprijed se dodaju rije£i koje pobliºe opisuju o kakvoj implementaciji se<br />
radi npr. ModuloCounter.<br />
2.10 Upcast i downcast<br />
Op¢enito, pojam type cast odnosi se na tretiranje podatka jedne vrste kao<br />
da je neke druge. U nekim drugim jezicima, prije svega C-u, mogu¢e je<br />
cast-ati izmežu vrsta na razli£ite na£ine na primjer, cijeli broj mogu¢e<br />
je cast-ati u pokaziva£. Za referentne vrste jedini dozvoljeni oblik castanja<br />
je unutar klasne hijerarhije ili prema gore (upcast), ili prema dolje<br />
(downcast).<br />
Slu£aj upcast-a je jednostavniji i dogaža se automatski. Tako se naziva<br />
situacija s kojom smo se ve¢ sreli na primjer, kad se instanca klase<br />
SimpleCounter tretira kao instanca su£elja Counter (u ispisu 1.13). Isto se dogaža<br />
i kad imamo dvije klase u odnosu natklasa-potklasa. Dakle, upcast je<br />
tehni£ko sredstvo koje omogu¢ava transparentno kori²tenje polimorzma.<br />
Njega je uvijek sigurno provesti ne moºe se dogoditi da instanca potklase<br />
nema neku metodu deniranu za natklasu.<br />
Java dozvoljava i cast-anje u suprotnom smjeru, prema dolje po hijerarhiji<br />
downcast. Njega se ne koristi da bismo instancu natklase tretirali kao<br />
instancu potklase dapa£e, to bi prouzro£ilo pogre²ku jer potklasa moºe<br />
imati dodatne metode koje natklasa nema. Downcast sluºi da bi se npr. u<br />
metodi koja prima op¢eniti parametar (npr. Counter) ipak mogla koristiti<br />
puna funkcionalnost instance konkretne klase (npr. ModuloCounter). Uzmimo<br />
konkretan primjer recimo da klasa ModuloCounter (ispis 1.21) ima dodatnu<br />
metodu setModulus koja nije denirana u su£elju Counter jer je speci£na za<br />
slu£aj cikli£kog broja£a:<br />
Ispis 2.13: ModuloCounter s dodatnom metodom setModulus<br />
package hr.fer.tel.jsem;<br />
public class ModuloCounter<br />
extends AbstractCounter<br />
{<br />
private int modulus = 10;<br />
public ModuloCounter( int initialState )
POGLAVLJE 2. RAZRADA DETALJA JEZIƒNIH SVOJSTAVA 51<br />
{<br />
}<br />
super( initialState );<br />
}<br />
public ModuloCounter()<br />
{<br />
}<br />
public void setModulus( int newModulus )<br />
{<br />
this.modulus = newModulus;<br />
}<br />
void setState( int newState )<br />
{<br />
this.counterState = newState % this.modulus;<br />
}<br />
Nadalje, recimo da negdje imamo metodu koja prima parametar op¢enite<br />
vrste Counter, ali u slu£aju da se radi o cikli£kom broja£u, ºeli postaviti<br />
duljinu ciklusa na potrebnu vrijednost:<br />
Ispis 2.14: Metoda koja upravlja ModuloCounter-om<br />
public static void useCounter( Counter cntr )<br />
{<br />
ModuloCounter modCntr = (ModuloCounter) cntr;<br />
modCntr.setModulus( 7 );<br />
}<br />
Varijabli vrste ModuloCounter pridjeljuje se vrijednost parametra vrste<br />
Counter i stoga je potrebno eksplicitno provesti downcast izrazom<br />
(ModuloCounter) cntr<br />
2.10.1 Operator instanceof<br />
U slu£aju da obavimo downcast nad objektom koji nije instanca doti£ne<br />
klase (ili eventualno njena potklasa), tijekom izvr²avanja programa bit ¢e<br />
prijavljena pogre²ka. U trenutku kompilacije op¢enito nije mogu¢e ustanoviti<br />
ho¢e li se to dogoditi. Na primjer, ako imamo ovakav poziv metode<br />
useCounter:<br />
useCounter( new SimpleCounter() );
52 POGLAVLJE 2. RAZRADA DETALJA JEZIƒNIH SVOJSTAVA<br />
jasno je da mora do¢i do pogre²ke jer klasa SimpleCounter ne denira metodu<br />
setModulus. Java ne¢e dozvoliti ni da kod stigne do tog poziva jer ¢e<br />
prijaviti pogre²ku ve¢ prilikom poku²aja downcast-anja. Prekid izvr²avanja<br />
zbog pogre²ke moºe se izbje¢i obavljanjem prethodne provjere operatorom<br />
instanceof:<br />
Ispis 2.15: Doražena metoda useCounter<br />
public static void useCounter( Counter cntr )<br />
{<br />
if ( cntr instanceof ModuloCounter )<br />
{<br />
ModuloCounter modCntr = (ModuloCounter) cntr;<br />
modCntr.setModulus( 7 );<br />
}<br />
}<br />
Prije nego ²to se obavi downcast, provjerava se je li prosliježen objekt<br />
zaista instanca klase ModuloCounter. Operator instanceof s lijeve strane op-<br />
¢enito ima neki izraz £ija vrijednost je referentne vrste, a s desne strane<br />
doslovno navedeno ime neke referentne vrste (klase ili su£elja). Operator<br />
vra¢a true ako je lijevi operand instanca desnog operanda ili njegove potklase.<br />
Odnosno, ako je desni operand su£elje, provjerava je li lijevi operand<br />
objekt koji implementira to su£elje. U praksi, u ve¢ini slu£ajeva gdje se radi<br />
downcast potrebna je ovakva provjera.<br />
2.11 Implicitni parametar metode this<br />
Ve¢ smo se na samom po£etku, u ispisu 1.1, susreli s klju£nom rije£i this<br />
pomo¢u koje smo u kodu metode pristupali atributima objekta nad kojim<br />
je ona pozvana. Formalno, svaka instancina metoda (metoda objekta,<br />
tj. svaka koja nije stati£ka), osim eksplicitno navedenih parametara, prima<br />
implicitno i dodatni parametar £ije ime je this, vrsta mu je klasa u kojoj<br />
se metoda nalazi, a vrijednost referenca na objekt nad kojim je metoda<br />
pozvana. Na primjer, metodu increment iz ispisa 1.16 (deklaracija klase<br />
SimpleCounter) mogli smo zapisati i ovako, zadrºavaju¢i identi£no pona²anje:<br />
public void increment()<br />
Ispis 2.16: Implicitni parametar this
POGLAVLJE 2. RAZRADA DETALJA JEZIƒNIH SVOJSTAVA 53<br />
{<br />
}<br />
SimpleCounter cntr = this;<br />
cntr.counterState++;<br />
Prilikom poziva metode, npr. cntr1.increment() u ispisu 1.4, referenca<br />
iz varijable cntr1 prepisujse se u parametar this.<br />
2.11.1 Parametar this i polimorzam<br />
Ono ²to je najvaºnije za parametar this je da upravo on omogu¢uje funkcioniranje<br />
polimorzma jer se samo za njega provodi dinami£ko povezivanje.<br />
Tijekom izvr²avanja programa prije svakog poziva metode prvo se provjerava<br />
to£na vrsta objekta na koji ¢e pokazivati this. Na osnovu te vrste<br />
donosi se odluka koju metodu pozvati. Za sve ostale, obi£ne parametre<br />
vrsta se ne utvržuje prilikom izvr²avanja, ve¢ samo prilikom prevoženja<br />
i to na osnovu deklarirane vrste izraza koji se pojavljuju kao argumenti<br />
metode. Drugim rije£ima, stati£ki se ustanovljuje potpis pozvane metode<br />
(vidjeti odjeljak 1.12).<br />
Bez ovog saznanja mogli bismo pomisliti da su npr. ova dva pristupa<br />
deniranju metode increment ekvivalentni (modikator static narežuje da<br />
se ne proslijedi implicitni parametar this, vidjeti odjeljak 2.7):<br />
Ispis 2.17: Neuspio poku²aj uvoženja eksplicitnog parametra this<br />
public interface Counter<br />
{<br />
void increment();<br />
void incrementWithExplicitThis( Counter explicitThis );<br />
... ostale metode ...<br />
}<br />
public class SimpleCounter<br />
implements Counter<br />
{<br />
private int counterState;<br />
public void increment()<br />
{<br />
this.counterState++;<br />
}<br />
public static void incrementWithExplicitThis( Counter explicitThis )<br />
{<br />
explicitThis.counterState++;
54 POGLAVLJE 2. RAZRADA DETALJA JEZIƒNIH SVOJSTAVA<br />
}<br />
}<br />
... ostale metode ...<br />
Metode bismo mogli poku²ati koristiti ovako:<br />
Ispis 2.18: Neuspio poku²aj kori²tenja metode s eksplicitnim this<br />
public static void main( String[] args )<br />
{<br />
Counter cntr = new SimpleCounter();<br />
cntr.increment();<br />
Counter.incrementWithExplicitThis( cntr );<br />
}<br />
Naoko, situacija je ekvivalentna prvi put se argument metode navodi<br />
lijevo od to£ke, a drugi put kao normalni argument u zagradama. Mežutim,<br />
uvoženje metode incrementWithExplicitThis ustvari generira £itav niz<br />
prevoditeljevih pogre²aka, sve zbog toga ²to Java ne provodi dinami£ko<br />
povezivanje nad normalnim argumentima.<br />
Prilikom prevoženja poziva incrementwithExplicitThis( cntr ) utvržuje<br />
se da je deklarirana (stati£ka) vrsta argumenta cntr jednaka Counter. Tu se<br />
nigdje ne pojavljuje prava vrsta objekta, SimpleCounter, i Java uop¢e ne¢e<br />
uzeti u obzir tu klasu. Dakle, nemogu¢e je da ¢e se pozvati metoda iz klase<br />
SimpleCounter. Vrsta Counter je ustvari su£elje u kojem uop¢e nema denicija<br />
metoda. Jasno je dakle da je ovakva situacija nemogu¢a i da mora<br />
uzrokovati prevoditeljevu pogre²ku. U praksi, prevoditeljeva pogre²ka pojavit<br />
¢e se ve¢ £im metodu incrementwithExplicitThis ozna£imo kao static<br />
jer je to metoda su£elja, a metode su£elja nikad nisu stati£ke iz upravo<br />
obja²njenog razloga.<br />
Nadalje, £ak i da se svi ovi problemi na neki neobja²njeni na£in rije²e<br />
i da zaista bude pozvana metoda incrementwithExplicitThis u klasi<br />
SimpleCounter, tamo stoji izjava explicitThis.counterState++. Parametar explicitThis<br />
je vrste Counter, a u toj vrsti ne spominje se nikakav atribut<br />
counterState i stoga ¢e se i za to prijaviti prevoditeljeva pogre²ka.<br />
Jo² jedan primjer u kojem se o£ituje razlika izmežu parametra this i<br />
ostalih moºe se vidjeti u raspravi u odjeljku 3.2.1.<br />
Pojam koji se u struci koristi za proces dinami£kog povezivanja je method<br />
dispatch. Za Javu se stoga kaºe da podrºava samo single dispatch jer
POGLAVLJE 2. RAZRADA DETALJA JEZIƒNIH SVOJSTAVA 55<br />
obavlja dispatch po samo jednom parametru metode. Suprotan pojam je<br />
multiple dispatch.<br />
Stru£ni pojam za jezi£no svojstvo da se prilikom prevoženja, tj. stati£ki<br />
utvržuju vrste svih izraza je static typing. Java je, dakle, statically typed<br />
jezik. Suprotan pojam je dynamic typing.<br />
2.12 Detalji o hijerarhiji klasa<br />
2.12.1 Korijenska klasa Object<br />
U 1. poglavlju ve¢ se govorilo o izvedenim klasama. U Javi je svaka klasa izvedena<br />
iz to£no jedne klase, osim posebne ugražene klase java.lang.Object,<br />
koja nema natklasu. Ako nacrtamo detaljan UML-ov dijagram klasa prikazuju¢i<br />
sve veze izvedena iz, dobit ¢emo stablo £iji korijen je klasa java.lang.Object.<br />
Ako deklaracija klase ne navodi eksplicitno iz koje je klase izvedena, podrazumijeva<br />
se extends java.lang.Object. Klasa Object denira i neke metode<br />
koje stoga imaju svi objekti (vidjeti 3.2).<br />
Graf odnosa implementira su£elje s druge strane, jest hijerarhija, ali<br />
nije stablo. Jedna klasa moºe implementirati proizvoljan broj su£elja, a i<br />
su£elje moºe biti izvedeno iz proizvoljnog broja drugih su£elja.<br />
2.12.2 Denicije izraza vezanih uz hijerarhiju<br />
Kad se pri£a o hijerarhijskim odnosima izmežu klasa, treba imati na umu<br />
to£na zna£enja izraza kao ²to su natklasa i potklasa. Vrijede sljede¢e denicije:<br />
• Natklasa klase C je klasa B iz koje je ona izvedena, ali takožer i klasa<br />
A iz koje je izvedena B, itd. Koriste¢i izraze iz genealogije, natklasa<br />
odgovara pojmu pretka. Klasa Object je, dakle, natklasa svim ostalim<br />
Javinim klasama.<br />
• Potklasa klase A je, analogno, svaka klasa-potomak, tj. svaka klasa B<br />
kojoj je A natklasa.<br />
• Klasa-roditelj klase C je njena izravna natklasa ona iz koje je izvedena.
56 POGLAVLJE 2. RAZRADA DETALJA JEZIƒNIH SVOJSTAVA<br />
• Klasa-dijete klase C je svaka klasa koja je iz nje izvedena, tj. kojoj je<br />
klasa C roditelj.<br />
2.12.3 Ulan£avanje poziva konstruktora<br />
Svaki konstruktor, prije nego ²to se izvr²i njegov kod, duºan je prvo pozvati<br />
neki konstruktor svoje natklase. To se dogaža ili eksplicitno, kori²tenjem<br />
klju£ne rije£i super, ili, u odsustvu toga, implicitno se dodaje poziv podrazumijevanog<br />
konstruktora natklase (super()). Posljedica toga je da se<br />
konstruktori izvr²avaju ulan£ano, tako da se prvo uvijek izvr²i konstruktor<br />
klase Object, zatim kontruktor neke njene izravne potklase, itd. sve do<br />
konkretne klase koja se instancira. Zbog toga £ak i za apstraktne klase,<br />
koje se ne smiju instancirati, vrijede ista pravila o posjedovanju bar jednog<br />
konstruktora. Bez toga bi bilo nemogu¢e instancirati njihove potklase.<br />
Imaju¢i re£eno u vidu, moºemo uvidjeti da je puni oblik dvaju konstruktora<br />
u ispisu 1.20 ustvari ovakav:<br />
Ispis 2.19: Eksplicitan poziv konstruktora natklase<br />
public AbstractCounter( int initialState )<br />
{<br />
super();<br />
setState( initialState );<br />
}<br />
public AbstractCounter()<br />
{<br />
super();<br />
}<br />
Implicitne pozive treba uvijek imati na umu, posebice stoga ²to natklasa<br />
ne mora posjedovati podrazumijevani konstruktor. Ako dakle imamo konstruktor<br />
s izostavljenim pozivom konstruktora natklase, a natklasa nema<br />
podrazumijevani konstruktor, do¢i ¢e do pogre²ke prilikom prevoženja koja<br />
moºe biti prili£no zbunjuju¢a jer se dogodila u praznom prostoru u koji<br />
je implicitno dodan izostavljeni poziv super().<br />
Konstruktor moºe umjesto iz natklase pozvati i neki drugi konstruktor<br />
iz iste klase. Time je omogu¢ena vi²estruka iskoristivost koda u vi²e<br />
konstruktora. U kona£nici, nakon ²to se konstruktori klase mežusobno dogovore<br />
koji ¢e se prvi izvr²iti, taj konstruktor opet poziva konstruktor iz
POGLAVLJE 2. RAZRADA DETALJA JEZIƒNIH SVOJSTAVA 57<br />
natklase. Poziv susjednog konstruktora postiºe se kori²tenjem klju£ne rije£i<br />
this umjesto super. Na primjer, konstruktore u ispisu 1.20 mogli smo<br />
denirati i ovako:<br />
Ispis 2.20: Poziv konstruktora iste klase<br />
public AbstractCounter( int initialState )<br />
{<br />
super();<br />
setState( initialState );<br />
}<br />
public AbstractCounter()<br />
{<br />
this( 0 );<br />
}<br />
Kori²tenjem podrazumijevanog konstruktora AbstractCounter() prvo ¢e<br />
se izvr²iti podrazumijevani konstruktor natklase (super()), zatim ostatak<br />
konstruktora s jednim parametrom, a zatim ostatak podrazumijevanog<br />
konstruktora (u ovom slu£aju nema daljnjih akcija u njemu).<br />
Napomenimo na kraju da kori²tenje klju£nih rije£i super i this za pozive<br />
konstruktora nema nikakve veze s kori²tenjem istih klju£nih rije£i u<br />
smislu reference na trenutni objekt i pozivanja metode natklase. Radi se<br />
samo o ²tednji na klju£nim rije£ima njihovim vi²estrukim kori²tenjem u<br />
jeziku. Svaka dodatna klju£na rije£ zna£i jo² jedan zabranjeni identikator<br />
za varijable, metode itd.<br />
2.13 Java kao softverska platforma<br />
Pod pojmom Java ne podrazumijeva se samo programski jezik i njegova<br />
biblioteka klasa, nego i cjelokupna softverska platforma, £iji temelj je Javin<br />
virtualni stroj.<br />
Programi napisani u Javi ne prevode se, kao ²to je ina£e uobi£ajeno,<br />
u izvr²ni kod koji se izravno moºe izvr²avati na procesoru ra£unala, ve¢<br />
u posebnu vrstu izvr²nog koda za Javin virtualni stroj (JVM), koji onda<br />
konkretno ra£unalo emulira. Taj poseban binarni kod naziva se mežukodom<br />
ili byte-kodom (eng. bytecode). Cilj toga je postizanje neovisnosti o ra£unalskoj<br />
platformi: kod se kompilira jednom, a izvr²ava na bilo kojem stroju<br />
koji ima implementaciju JVM-a, bez obzira na vrstu procesora. Takožer<br />
se tehni£ki omogu¢uje i bolja kontrola nad interakcijom izmežu Javinog
58 POGLAVLJE 2. RAZRADA DETALJA JEZIƒNIH SVOJSTAVA<br />
programa i ra£unala na kojem se izvr²ava. Lak²e je mogu¢e blokirati svaki<br />
poku²aj pristupa osjetljivim resursima i tako onemogu¢iti zlonamjerni kod.<br />
Vi²e o ovome moºe se doznati na [5].<br />
Sve ovo potje£e od prvotnog cilja Jave da sluºi za tzv. embedded applications,<br />
aplikacije koje se u£itavaju unutar webskih stranica. ƒim se ne²to<br />
implicitno u£itava putem Weba, otvara se prostor za napade na ra£unalo<br />
pisanjem zlonamjernog koda. U mežuvremenu se Java po£ela masovno koristiti<br />
i za pisanje obi£nih, samostojnih aplikacija, gdje je pitanje sigurnosti<br />
manje bitno. Na primjer, £itavo razvojno okruºje Eclipse, kao i Borlandov<br />
JBuilder, napisani su u Javi.
Poglavlje 3<br />
Najvaºnije ugražene klase<br />
Ve¢ je re£eno da jezik Java sadrºi i ve¢i broj ugraženih klasa i su£elja. Takožer<br />
je istaknut i jedan od ugraženih paketa koji je posebno usko povezan<br />
s jezikom paket java.lang. Ovdje ¢e biti izloºeni detalji nekoliko najvaºnijih<br />
klasa iz tog paketa. Za daljnje detalje treba se obratiti dokumentaciji<br />
Javine biblioteke klasa dostupne na Webu [1].<br />
3.1 Uvodne upute za snalaºenje u Javinoj dokumentaciji<br />
API-ja<br />
Javin sustav za HTML-ovsku dokumentaciju zove se Javadoc. Na primjer,<br />
na upravo spomenutoj internetskoj lokaciji, [1], nalazi se Javadoc-ova dokumentacija<br />
cjelokupne Javine biblioteke klasa. Kad otvorimo tu lokaciju<br />
u pregledniku Weba, dobit ¢emo prikaz kao na slici 3.1. Pregled treba zapo£eti<br />
u okviru gore lijevo. Tamo je popis svih paketa. Treba kliknuti na<br />
paket od interesa, u ovom slu£aju java.lang. Time se u okviru dolje lijevo<br />
suºava izbor i prikazuju se samo klase iz doti£nog paketa. U tom okviru<br />
zatim moºemo na¢i klasu koja nas zanima, npr. Object. U glavnom okviru<br />
(desno) pojavljuje se dokumentacija traºene klase. Na vrhu je op¢eniti opis<br />
klase, a zatim slijedi popis £lanova konstruktora, atributa i metoda. Prvo<br />
su u tablicama pobrojani svi £lanovi s kratkom napomenom. Klikom na<br />
ime £lana odlazi se dublje u stranicu, gdje je njegov detaljan opis. Na slici<br />
3.2 prikazana je detaljna dokumentacija za metodu equals.<br />
59
60 POGLAVLJE 3. NAJVAšNIJE UGRAÐENE KLASE<br />
Slika 3.1: Po£etna stranica Javadoc-a
POGLAVLJE 3. NAJVAšNIJE UGRAÐENE KLASE 61<br />
Slika 3.2: Dokumentacija za metodu equals
62 POGLAVLJE 3. NAJVAšNIJE UGRAÐENE KLASE<br />
3.2 Klasa java.lang.Object<br />
Klasa Object je u korijenu Javine hijerarhije klasa, kao ²to je napomenuto<br />
u odjeljku 2.12.1. Ovdje ¢e se detaljnije govoriti o toj klasi. To nije kao<br />
²to bi se moglo pomisliti za najop¢enitiju klasu koja predstavlja bilo kakav<br />
objekt samo prazna, apstraktna klasa bez svojih funkcionalnosti. Radi<br />
se o konkretnoj klasi koju je mogu¢e normalno instancirati. Osim toga<br />
je i prili£no kompleksna i njena implementacija je vezana uz £itav niz programskih<br />
tehnika. Ovdje ¢e se od svega spominjati samo njene najosnovnije<br />
karakteristike.<br />
3.2.1 Metoda equals<br />
U odjeljku 2.1.2 spomenuto je da za provjeru jednakosti objekata treba<br />
koristiti metodu equals. Metodu se koristi tako da ju se pozove nad prvim<br />
objektom i kao argument se proslijedi drugi objekt: o1.equals( o2 ). Metoda<br />
equals denirana je u klasi Object, ali na trivijalan na£in usporežuju<br />
se reference (o1 == o2). Ako je potrebna smislena usporedba objekata neke<br />
klase, potrebno je nadja£ati ovu metodu odgovaraju¢om implementacijom.<br />
Potpis metode je equals( Object ) i upravo takvu metodu moramo dodati<br />
u svoju klasu. Budu¢i da je parametar vrste Object, prvo ¢e biti potrebno<br />
ustanoviti je li prosliježeni objekt odgovaraju¢e vrste jer ako nije, onda<br />
je jasno da nije jednak trenutnom objektu. Stoga kod ove metode rutinski<br />
zapo£inje kori²tenjem operatora instanceof, iza £ega slijedi odgovaraju¢i<br />
downcast. Na primjer, mogli bismo imati ovakav equals u klasi SimpleCounter<br />
(ispis 1.21):<br />
package hr.fer.tel.jsem;<br />
public final class SimpleCounter<br />
extends AbstractCounter<br />
{<br />
... sve isto kao ranije ...<br />
Ispis 3.1: Implementacija metode equals<br />
public boolean equals( Object o )<br />
{<br />
if ( !( o instanceof SimpleCounter ) )<br />
return false;<br />
SimpleCounter that = (SimpleCounter) o;
POGLAVLJE 3. NAJVAšNIJE UGRAÐENE KLASE 63<br />
}<br />
}<br />
return this.counterState == that.counterState;<br />
Za metodu equals tipi£no je da se okori²tava klasnom enkapsulacijom i da<br />
izravno pristupa atributima drugog objekta.<br />
Jedna od tipi£nih pogre²aka kod studenata je deklaracija metode s parametrom<br />
£ija vrsta je trenutna klasa (npr. equals( SimpleCounter )). Parametar<br />
mora biti vrste Object, u protivnom se ne¢e raditi o nadja£avanju,<br />
ve¢ o deniranju nove metode koja samo preoptere¢uje ime equals. U<br />
jednostavnim primjerima kod ¢e £ak ispravno raditi jer ¢e se pozivati ta<br />
metoda umjesto prave metode equals. Sljede¢i primjer pokazuje u kojem<br />
slu£aju ¢e raditi, a u kojima ne:<br />
Ispis 3.2: Tipi£na pogre²ka s preoptere¢enjem umjesto nadja£avanjem<br />
public class EqualsTester<br />
{<br />
public boolean equals( EqualsTester et )<br />
{<br />
return true;<br />
}<br />
public static void main( String[] args )<br />
{<br />
EqualsTester et1 = new EqualsTester();<br />
EqualsTester et2 = new EqualsTester();<br />
boolean b;<br />
b = et1.equals( et2 );<br />
System.out.println( b ); // true<br />
// upcast argumenta na vrstu Object:<br />
Object o2 = et2;<br />
b = et1.equals( o2 );<br />
System.out.println( b ); // false!<br />
}<br />
}<br />
// upcast objekta nad kojim se poziva metoda:<br />
Object o1 = et1;<br />
b = o1.equals( et2 );<br />
System.out.println( b ); // false!<br />
Kad god se ispi²e false, zna£i da je pozvana originalna metoda equals<br />
iz klase Object, koja je ostala nenadja£ana. Razlog za ovakvo pona²anje,<br />
koje se nekima moºe £initi nelogi£nim s obzirom na postojanje dinami£kog
64 POGLAVLJE 3. NAJVAšNIJE UGRAÐENE KLASE<br />
povezivanja, je u tome ²to se dinami£ko povezivanje provodi samo nad parametrom<br />
this, tj. nad objektom lijevo od to£ke u pozivu metode (vidjeti<br />
odjeljak 2.11). Vrste argumenata, tj. potpis pozvane metode utvržuje se<br />
stati£ki, prilikom prevoženja. U prvom pozivu, et1.equals( et2 ) utvrženi<br />
potpis metode je equals( EqualsTester ) pa se poziva na²a metoda. U drugom<br />
slu£aju ispada equals( Object ) pa se poziva metoda nasliježena od<br />
klase Object.<br />
Tre¢i slu£aj je najsuptilniji. Argument je ponovo vrste EqualsTester <br />
kao u prvom slu£aju, koji je radio ispravno. Mežutim, sada je izraz lijevo<br />
od to£ke vrste Object. Prevoditelj u toj klasi traºi sve metode imena equals<br />
i nalazi samo jednu, s parametrom vrste Object. Na osnovu toga odabire<br />
se potpis metode equals( Object ). Tek tijekom izvr²avanja uo£it ¢e se da<br />
je stvarna vrsta objekta lijevo od to£ke EqualsTester, mežutim tada je ve¢<br />
ksiran potpis metode koju treba pozvati, i to je equals( Object ) metoda<br />
nasliježena iz klase Object.<br />
Problem kao u drugom slu£aju dogaža se uvijek kad se metoda preoptere¢uje<br />
tako da su vrste parametara u odnosu potklasa-natklasa. Takvo<br />
preoptere¢enje redovito treba izbjegavati. Ako postoji potreba za takve<br />
dvije metode, trebaju imati razli£ita imena. Problem kao u tre¢em slu£aju<br />
ne bi se dogodio da su obje verzije preoptere¢ene metode bile denirane u<br />
istoj klasi.<br />
3.2.2 Metoda hashCode<br />
Ova metoda vezana je uz vrlo zna£ajnu programsku tehniku he²-tablicu<br />
(eng. hash table ili hashtable). To je struktura koja sluºi za memorijski<br />
ekasno spremanje ve¢e koli£ine podataka uz sposobnost brzog dohva¢anja<br />
podatka prema njegovom klju£u he²-kodu. U Javu ugražene kolekcije<br />
(vidjeti 5. poglavlje) interno koriste he²-tablice (npr. £esto kori²tena klasa<br />
HashSet). One se oslanjaju na ispravnu implementaciju metode hashCode u<br />
svakom objektu koji se sprema u kolekciju. Ova metoda usko je povezana<br />
s metodom equals i njih dvije se uvijek nadja£ava u paru.<br />
Metoda hashCode zaduºena je da za svaki objekt vrati njegov he²-kod<br />
u obliku cijelog broja (int-a). Presudno je da dva jednaka objekta uvijek<br />
prijavljuju i jednak he²-kod. Bitno je i da dva razli£ita objekta ²to £e²¢e<br />
vra¢aju razli£it he²-kod dakle, da he²-kodovi budu ²to bolje raspr²eni po<br />
objektima. U protivnom ¢e se degradirati ekasnost he²-tablice. Jasno je
POGLAVLJE 3. NAJVAšNIJE UGRAÐENE KLASE 65<br />
da objekt koji ima vi²e od 2 32 mogu¢ih stanja ne moºe za svako stanje<br />
prijaviti razli£it he²-kod i stoga je potrebno promi²ljeno implementirati<br />
ovu metodu da se postigne dobro raspr²enje. Postoji standardni predloºak<br />
za implementaciju ove metode i najbolje ga se uvijek drºati. U ispisu 3.3<br />
prikazana je klasa koja sadrºi sve mogu¢e slu£ajeve vrsta podataka i na£in<br />
izra£una njihovog he²-koda. Ukratko, postupak je sljede¢i:<br />
1. Deklariramo cjelobrojnu varijablu (u primjeru: hc) i inicijaliziramo ju<br />
na 17.<br />
2. Za svaki atribut koji sudjeluje u metodi equals izra£unamo njegov<br />
he²-kod kao u primjeru.<br />
3. U varijablu hc ugražujemo pojedini he²-kod atributa prema formuli<br />
hc = 37*hc + he²-kod<br />
4. Kona£nu vrijednost varijable hc vratimo kao ukupan he²-kod objekta.<br />
Ispis 3.3: Predloºak za generiranje he²-koda<br />
public class HashCodeExample<br />
{<br />
private boolean boolVal;<br />
private byte byteVal;<br />
private char charVal;<br />
private short shortVal;<br />
private int intVal;<br />
private long longVal;<br />
private float floatVal;<br />
private double doubleVal;<br />
private Object objVal;<br />
private int[] intArray = { 1, 2, 3 };<br />
public int hashCode()<br />
{<br />
int hc = 17;<br />
hc = 37*hc + ( boolVal? 1 : 0 );<br />
hc = 37*hc + byteVal;<br />
hc = 37*hc + charVal;<br />
hc = 37*hc + shortVal;<br />
hc = 37*hc + intVal;<br />
hc = 37*hc + (int) ( longVal ^ ( longVal >>> 32 ) );<br />
hc = 37*hc + Float.floatToIntBits( floatVal );<br />
long dblToLong = Double.doubleToLongBits( doubleVal );<br />
hc = 37*hc + (int) ( dblToLong ^ ( dblToLong >>> 32 ) );
66 POGLAVLJE 3. NAJVAšNIJE UGRAÐENE KLASE<br />
hc = 37*hc + objVal.hashCode();<br />
for ( int i = 0; i < intArray.length; i++ )<br />
hc = 37*hc + intArray[i];<br />
}<br />
}<br />
return hc;<br />
3.2.3 Metoda toString<br />
Pri razvoju programa, posebice tijekom traºenja i uklanjanja pogre²aka,<br />
program mora ispisivati detalje tijeka izvr²avanja. Na primjer, £esto je potreban<br />
ispis trenutnog stanja klju£nih objekata prije i nakon neke operacije.<br />
Takožer, u trenutku kad dože do pogre²ke iznimno je korisno dobiti izvje-<br />
²taj o stanju objekta koji je izazvao pogre²ku. Za ovakve i druge namjene<br />
postoji metoda toString koju imaju svi objekti jer je denirana u klasi<br />
Object. Metoda je zaduºena da vrati znakovni niz koji opisuje objekt.<br />
Izvorna implementacija u klasi Object prili£no je beskorisna (vra¢a ime<br />
klase £ija je instanca u pitanju i njen he²-kod u heksadecimalnom zapisu),<br />
stoga ju je redovito potrebno nadja£ati smislenom implementacijom. Studenti<br />
ponekad krivo shvate namjenu metode pa ju izvedu tako da sama<br />
ispisuje na zaslon. Pazite da izbjegnete takvu pogre²ku. Primjer implementacije<br />
u klasi SimpleCounter (ispis 1.21):<br />
package hr.fer.tel.jsem;<br />
Ispis 3.4: Implementacija metode toString<br />
public final class SimpleCounter<br />
extends AbstractCounter<br />
{<br />
... sve isto kao ranije ...<br />
}<br />
public String toString()<br />
{<br />
return "SimpleCounter:" + this.counterState;<br />
}<br />
3.3 Klasa java.lang.String<br />
U Javi, znakovni nizovi predstavljeni su instancama ugražene klase String.<br />
Ova klasa integrirana je u sam jezik i postoje jezi£ne konstrukcije posebno
POGLAVLJE 3. NAJVAšNIJE UGRAÐENE KLASE 67<br />
za nju:<br />
• Doslovno navoženje niza unutar dvostrukih navodnika, npr. "znakovni<br />
niz". Rezultat ovog izraza je instanca klase String s navedenim tekstom<br />
kao sadrºajem. Znakovne nizove, dakle, ne kreira se uobi£ajenim<br />
konstruktorom: new String("znakovni niz"). Ovo dodu²e radi,<br />
ali se sasvim nepotrebno stvara objekt vi²ka sam niz pod navodnicima<br />
je jedan objekt, a dodatni se stvara operatorom new.<br />
• Operator ulan£avanja, npr. "znakovni" + " " + "niz". Rezultat je novi<br />
String sadrºaja "znakovni niz". U skladu s ovime deniran je i sloºeni<br />
operator dodjele +=. Znak + koristi se dakle i za zbrajanje i za ulan-<br />
£avanje. Odluka o tome koji je smisao danog znaka donosi se prema<br />
vrsti lijevog operanda: ako je to String, operacija je ulan£avanje. U<br />
sloºenijim izrazima ne mora biti sasvim o£ito ²to je lijevi operand pa<br />
treba biti na oprezu (npr. ²to je lijevi operand drugog plusa u izrazu<br />
"a" + 2 + 3? to je izraz "a" + 2, vrste String). Ako se kao operand<br />
ulan£avanja pojavi ne²to drugo osim String-a, automatski se pretvara<br />
u odgovaraju¢i String. Ako se radi o objektu, poziva se njegova metoda<br />
toString, ako se radi o nul-referenci, pretvara se u niz "null", a i<br />
primitivni podaci se pretvaraju na smisleni na£in. Primjerice, rezultat<br />
izraza "a" + 2 + 3 je "a23". S druge strane, rezultat izraza 2 + 3 +<br />
"a" je prevoditeljeva pogre²ka jer se u ovom slu£aju radi o zbrajanju,<br />
a nizove se ne moºe pribrajati brojevima. Iz ovakve situacije moºemo<br />
se izvu¢i malim trikom: "" + 2 + 3 + "a" ¢e dati ºeljeni rezultat "23a".<br />
3.3.1 Provjera jednakosti znakovnih nizova<br />
Usporeživanje nizova nije na sli£an na£in elegantno integrirano u jezik i<br />
mora ih se usporeživati kao i ostale objekte, pozivom metode equals. Usporedba<br />
pomo¢u == samo ¢e usporediti reference, kao i ina£e. Kori²tenje<br />
pogre²nog na£ina usporedbe moºe izazvati vrlo neobi£no pona²anje ponekad<br />
ispravno, a ponekad neispravno. To je stoga ²to Java automatski<br />
optimizira kori²tenje instanci String-a pa moºe vi²estruko iskoristiti istu<br />
instancu na vi²e mjesta gdje se pojavljuje isti niz. Ako se to dogodi, provjera<br />
pomo¢u == ¢e pro¢i:<br />
Ispis 3.5: Posljedice pogre²nog kori²tenja == za usporedbu String-ova
68 POGLAVLJE 3. NAJVAšNIJE UGRAÐENE KLASE<br />
public static void main( String[] args )<br />
{<br />
String a = "a";<br />
String b = "b";<br />
String ab1 = "ab";<br />
String ab2 = "ab";<br />
String ab3 = a + b;<br />
}<br />
System.out.println( ab1 == ab2 ); // true<br />
System.out.println( ab1 == ab3 ); // false<br />
System.out.println( ab1.equals( ab2 ) ); // true<br />
System.out.println( ab1.equals( ab3 ) ); // true<br />
U primjeru je konstanta "ab" uspje²no iskori²tena dvaput i za ab1 i za<br />
ab2. Mežutim, kad taj isti niz sastavimo ulan£avanjem iz druga dva, kreira<br />
se novi objekt pa referenca na njega vi²e nije ista.<br />
3.3.2 Nepromjenjivost instanci String-a<br />
Klasa String dizajnirana je da bude nepromjenjiva (eng. immutable) njene<br />
instance nikad ne mijenjaju stanje. Ovo svojstvo uvijek je izrazito poºeljno<br />
za svaku klasu gdje je to mogu¢e. Takvi objekti imaju neka korisna svojstva<br />
koja ina£e imaju podaci primitivne vrste:<br />
• Ako se takav objekt proslijedi metodi, nema opasnosti da ¢e ga ona<br />
izmijeniti i tako neizravno i nepredviženo utjecati na funkciju pozivaju¢e<br />
metode.<br />
• Ako je neki od atributa objekta nepromjenjiv objekt, moºe ga se<br />
dati na raspolaganje vanjskom svijetu bez opasnosti da ga se izvana<br />
izmijeni i tako neizravno utje£e na stanje objekta koji ga sadrºi.<br />
• U vi²enitnom programu nema potrebe za sinkronizacijom pristupa<br />
nepromjenjivom objektu iz vi²e niti.<br />
Budu¢i da su String-ovi nepromjenjivi, jasno je da se pri svakoj promjeni<br />
vrijednosti neke String-ovske varijable stvara novi objekt. Npr. izraz<br />
s += "tri" ne¢e izmijeniti postoje¢i objekt pridijeljen varijabli s, ve¢ ¢e<br />
stvoriti novi i referencu na njega zapisati u varijablu. Ako u kodu imamo<br />
situaciju gdje se dogažaju brojne izmjene znakovnih nizova, moºemo radi<br />
²tednje radne memorije pribje¢i kori²tenju druge klase, StringBuffer, koja
POGLAVLJE 3. NAJVAšNIJE UGRAÐENE KLASE 69<br />
modelira promjenjivi niz. U tom slu£aju, mežutim, ne¢emo se mo¢i koristiti<br />
elegantnim jezi£nim konstrukcijama kao kad radimo sa String-om.<br />
3.4 Klasa System<br />
Ova klasa ne sluºi za instanciranje, ve¢ sadrºi korisne stati£ke metode i<br />
atribute koji ostvaruju vezu s uslugama ra£unalske platforme na kojoj se<br />
izvr²ava Javin program. Pregled najvaºnijih £lanova klase:<br />
• atributi in, out i err: veza na standardni ulaz, izlaz i izlaz za pogre²ke.<br />
Vidjeti 7.3<br />
• metoda currentTimeMillis: trenutno sistemsko vrijeme<br />
• metoda exit( int ): prekid izvr²avanja programa. Prosliježeni broj je<br />
statusni kod s kojim program zavr²ava. Kod 0 zna£i da program zavr-<br />
²ava na redovan na£in; ostali kodovi signaliziraju neku vrstu pogre²ke.<br />
Uobi£ajeno je npr. koristiti kod -1 ako se prekida zbog pogre²ke.<br />
• metoda getenv: dohva¢anje sistemskih varijabli (environment variables)<br />
• metoda arrayCopy: ekasno kopiranje sadrºaja iz jednog polja u drugo<br />
Detalje o ovim i drugim £lanovima klase treba potraºiti u dokumentaciji<br />
[1].<br />
3.5 Klase-omota£i primitivnih vrsta, autoboxing<br />
Java razlikuje primitivne od referentnih vrsta podataka i stoga brojevi,<br />
logi£ke vrijednosti i znakovi nisu objekti. Mežutim, mnoga programska rje-<br />
²enja, kao npr. kolekcije podataka (vidjeti poglavlje 5), rade isklju£ivo s<br />
objektima. Da bi se ipak u takvim slu£ajevima moglo baratati i njima,<br />
u Javinu biblioteku klasa ugražene su posebne klase £ije instance predstavljaju<br />
podatke primitivnih vrsta. Radi se o sljede¢im klasama u paketu<br />
java.lang:<br />
1. Boolean<br />
2. Byte
70 POGLAVLJE 3. NAJVAšNIJE UGRAÐENE KLASE<br />
3. Character<br />
4. Short<br />
5. Integer<br />
6. Long<br />
7. Float<br />
8. Double<br />
Jedna instanca predstavlja jednu vrijednost. Instance su nepromjenjive,<br />
²to dodatno olak²ava njihovo tretiranje kao da su podaci primitivne vrste<br />
(vidjeti odjeljak 3.3.2). Na primjer, ako negdje moramo koristiti objekte,<br />
a podatak kojim trebamo baratati je vrste int, moºemo koristiti omota£<br />
Integer na sljede¢i na£in:<br />
int i = 1;<br />
Integer intObj = i; // omotavanje u objekt<br />
... radimo ne²to s omotanom vrijednosti ...<br />
int j = intObj; // razmotavanje natrag u primitivnu vrijednost<br />
Integer intObj2 = i + 1;<br />
int k = intObj + intObj2;<br />
Java dozvoljava implicitnu konverziju izmežu primitivnih vrijednosti i<br />
njihovih omota£a u izrazu dodjele vrijednosti. To jezi£no svojstvo naziva se<br />
autoboxing/unboxing. Dozvoljeno je i izvoditi ra£unske operacije nad omota£ima,<br />
kao ²to se vidi u posljednjem retku primjera.<br />
3.6 Konverzija izmežu podatka i njegovog znakovnog<br />
zapisa<br />
Klase omota£a primitivnih vrsta sadrºe i stati£ke metode koje parsiraju<br />
znakovne zapise vrijednosti. Primjeri:<br />
int i = Integer.parseInt( "-2" );<br />
double d = Double.parseDouble( "2.021e-2" );<br />
boolean b = Boolean.parseBoolean( "true" );<br />
O£ekivani ispis:<br />
i = -2, d = 0.02021, b = true
POGLAVLJE 3. NAJVAšNIJE UGRAÐENE KLASE 71<br />
Konverzija u suprotnom smjeru, iz primitivne vrijednosti u znakovni<br />
niz, moºe se obaviti na dva na£ina. Jedna je memorijski ekonomi£nija, ali<br />
druga ima elegantniji zapis:<br />
double d = 2.02;<br />
String s1 = String.valueOf( d );<br />
String s2 = "" + d;<br />
U drugom retku koristi se stati£ka metoda String.valueOf. Metoda je<br />
preoptere¢ena i ima verziju za svaku primitivnu vrstu podataka. U tre¢em<br />
retku koristi se ve¢ prikazano svojstvo operacije ulan£avanja (u odjeljku<br />
3.3) da automatski pretvara sve primitivne podatke u nizove.
Poglavlje 4<br />
Iznimke<br />
U prakti£ki svakom programu dobar dio koda posve¢en je provjeri ispravnosti<br />
neke situacije i prijavljivanju pogre²aka ako se uo£i nepravilnost. Na<br />
primjer, tipi£no je provjeriti je li prosliježeni cijeli broj u potrebnom opsegu.<br />
U na²em slu£aju broja£a (ispis 2.13) bit ¢e potrebno npr. provjeriti<br />
da je ciklus brojanja prosliježen metodi setModulus pozitivan broj:<br />
package hr.fer.tel.jsem;<br />
public class ModuloCounter<br />
extends AbstractCounter<br />
{<br />
private int modulus = 10;<br />
Ispis 4.1: ModuloCounter s provjerom parametra<br />
public ModuloCounter( int initialState )<br />
{<br />
super( initialState );<br />
}<br />
public ModuloCounter()<br />
{<br />
}<br />
public void setModulus( int newModulus )<br />
{<br />
if ( newModulus < 0 )<br />
System.out.println( "Negativan ciklus brojanja!" );<br />
else<br />
this.modulus = newModulus;<br />
}<br />
void setState( int newState )<br />
{<br />
73
74 POGLAVLJE 4. IZNIMKE<br />
}<br />
}<br />
this.counterState = newState % this.modulus;<br />
Na prvi pogled ovo moºe izgledati kao savr²eno rje²enje ako je broj<br />
krivi, ispisuje se pogre²ka, ina£e se obavlja zatraºena akcija. Mežutim, u<br />
stvarnosti ovo je prili£no neupotrebljivo. Pozivaju¢a metoda uop¢e ne¢e<br />
imati informaciju o tome je li naredba za postavljanje ciklusa uspje²no izvr²ena.<br />
Program ¢e nastaviti dalje i, naravno, raditi neispravno. Programer,<br />
gledaju¢i ispis, mo¢i ¢e samo ustanoviti da je u jednom trenutku metoda<br />
setModulus pozvana s pogre²nom vrijedno²¢u. Tijekom izvr²avanja ta metoda<br />
se pozvala tko zna koliko puta i s kojih sve lokacija.<br />
Malo bolji pristup, koji se npr. koristi u jeziku C, je da metoda ne<br />
bude void, nego int i da broj koji vra¢a ovisi o uspje²nosti izvr²enja. Svaka<br />
pozivaju¢a metoda tada moºe provjeriti je li sve dobro zavr²ilo. Ovaj na£in<br />
opet ima niz mana npr. to zna£i da za svaki pojedini poziv bilo koje<br />
metode koja bi se mogla neuspje²no izvr²iti treba imati po jedan blok if<br />
za provjeru. Dodatni je problem ²to se sad povratna vrijednost ne moºe<br />
koristiti za regularne namjene mnoge metode nisu void pa da ih moºemo<br />
lako preurediti da vra¢aju int.<br />
4.1 Bacanje iznimke prijava iznimne situacije<br />
U jeziku Java za rje²avanje ovih problema postoji poseban mehanizam <br />
iznimke. Gornja metoda setModulus u Javi bi se izvela ovako:<br />
public class ModuloCounter<br />
{<br />
...<br />
public void setModulus( int newModulus )<br />
{<br />
if ( newModulus < 0 )<br />
throw new IllegalArgumentException( "Negativan ciklus brojanja" );<br />
}<br />
this.modulus = newModulus;<br />
...<br />
}
POGLAVLJE 4. IZNIMKE 75<br />
Primje¢ujemo klju£nu rije£ throw: njome se baca iznimku. Ovo je sasvim<br />
nov i neobi£an pojam i trebat ¢e malo vi²e vremena da legne po£etniku.<br />
Bacanjem iznimke prekida se izvr²avanje metode i ona, ako je i trebala<br />
vratiti neku povratnu vrijednost, u ovom slu£aju ne¢e ju vratiti, nego ¢e<br />
se u pozivaju¢oj metodi pojaviti ta ba£ena iznimka kao rezultat metode.<br />
Ako tamo nema nikakvog koda speci£nog za baratanje iznimkama, dogodit<br />
¢e se ne²to prili£no neo£ekivano: izvr²avanje te pozivaju¢e metode takožer<br />
¢e odmah biti prekinuto i ona ¢e ponovo baciti tu istu iznimku svojoj<br />
pozivaju¢oj metodi. Ako dakle imamo ovakav slu£aj:<br />
public class ModuloCounter<br />
{<br />
...<br />
Ispis 4.2: Bacanje i ponovno bacanje iznimke<br />
public void setModulus( int newModulus )<br />
{<br />
if ( newModulus < 0 )<br />
throw new IllegalArgumentException( "Negativan ciklus brojanja" );<br />
}<br />
this.modulus = newModulus;<br />
}<br />
...<br />
public class AppThatUsesTheCounter<br />
{<br />
private static boolean intermediateMethod( int i )<br />
{<br />
ModuloCounter mc = new ModuloCounter();<br />
mc.setModulus( i );<br />
System.out.println( "intermediateMethod uspje²no zavr²ava" );<br />
return true;<br />
}<br />
}<br />
public static void main( String[] args )<br />
{<br />
boolean b = intermediateMethod( -1 );<br />
System.out.println( "intermediateMethod je vratila " + b );<br />
}<br />
pokretanje programa rezultirat ¢e sljede¢im ispisom:<br />
Ispis 4.3: Izvje²taj o ba£enoj iznimci<br />
Exception in thread "main" java.lang.IllegalArgumentException: Negativan ciklus brojanja
76 POGLAVLJE 4. IZNIMKE<br />
at hr.fer.tel.jsem.AppThatUsesTheCounter.setModulus(AppThatUsesTheCounter.java:8)<br />
at hr.fer.tel.jsem.AppThatUsesTheCounter.intermediateMethod(AppThatUsesTheCounter.java:13)<br />
at hr.fer.tel.jsem.AppThatUsesTheCounter.main(AppThatUsesTheCounter.java:18)<br />
Primijetimo da se poruke intermediateMethod je vratila... i intermediateMethod<br />
uspje²no zavr²ava nisu ispisale. U intermediateMethod iznimka<br />
se pojavila u prvom retku i, u nedostatku koda koji ju obražuje, tu je metoda<br />
zavr²ila ponovnim bacanjem iste iznimke (eng. rethrow). Iznimka je<br />
tako stigla do metode main, takožer u njenom prvom retku. Budu¢i da od<br />
nje nema kamo dalje (to je metoda od koje je program zapo£eo), £itav<br />
program je prekinut uz prethodni ispis detaljnog izvje²taja o tome gdje<br />
je to£no do²lo do iznimke (cjelokupan redoslijed pozivanja metoda i to£no<br />
mjesto u kodu gdje se svaki poziv dogaža), koja je vrsta iznimke (Illegal-<br />
ArgumentException) i njen pobliºi opis (Negativan ciklus brojanja). Sve se<br />
to dogodilo automatski, bez ijednog retka koda vezanog uz obradu iznimke.<br />
Varijabli b u metodi main o£ito nije mogla biti dodijeljena nijedna vrijednost<br />
jer intermediateMethod nije zavr²ila vra¢anjem povratne vrijednosti.<br />
Iz toga je ve¢ jasno da se izvr²avanje ne moºe nastaviti normalnim putem<br />
jer je varijabla ostala neinicijalizirana, a ve¢ u sljede¢em retku se njena<br />
vrijednost treba ispisati.<br />
4.2 Hvatanje iznimke obrada iznimne situacije<br />
Prikazani na£in kori²tenja iznimke jo² uvijek nije previ²e svrsishodan jer ¢e<br />
uslijed svakog bacanja iznimke program pucati. U ve¢ini realnih situacija<br />
trebat ¢e se na odgovaraju¢i na£in pobrinuti za iznimnu situaciju i zatim<br />
nastaviti s izvr²avanjem programa. Klju£na je prednost ²to moºemo odabrati<br />
u kojoj metodi ¢emo napraviti provjeru to ne treba biti niti metoda<br />
gdje je do²lo do pogre²ke, niti metoda koja ju je izravno pozvala. Moºemo<br />
npr. sve iznimke obraživati u metodi main:<br />
Ispis 4.4: Hvatanje iznimke<br />
public static void main( String[] args )<br />
{<br />
boolean b;<br />
try<br />
{<br />
b = intermediateMethod( -1 );
POGLAVLJE 4. IZNIMKE 77<br />
System.out.println( "intermediateMethod je vratila " + b );<br />
}<br />
catch ( IllegalArgumentException e )<br />
{<br />
System.err.println( "Iznimka!");<br />
e.printStackTrace();<br />
}<br />
b = intermediateMethod( 1 );<br />
System.out.println(<br />
"Drugi poku²aj: intermediateMethod je vratila " + b );<br />
}<br />
Tu se pojavljuje nova konstrukcija, koja koristi i dvije nove klju£ne<br />
rije£i: try i catch. Pomo¢u try najavljuje se blok koda koji ¢e se poku²ati<br />
izvr²iti, ali u kojem bi moglo do¢i i do iznimke.<br />
U ovom trenutku potrebno je napomenuti par detalja u vezi s karakterom<br />
iznimke. U Javi to je jednostavno objekt kao i svaki drugi instanca<br />
klase java.lang.Exception ili bilo koje njene potklase. Iznimku se kreira na<br />
uobi£ajen na£in, operatorom new. Da je iznimka normalan objekt najjasnije<br />
se vidi po izjavi<br />
e.printStackTrace();<br />
Tu smo pozvali metodu nad objektom iznimke. Ovo je vrlo korisna metoda<br />
jer ona ispisuje izvje²taj o iznimci, kao ²to je pokazano u ispisu 4.3. Ranije<br />
je prikazan primjer u kojem se izvje²taj ispisuje automatski, prije prekida<br />
programa. Ovom metodom moºemo eksplicitno zatraºiti takav izvje²taj, a<br />
bez prekidanja programa. Izvje²taj se ²alje na standardni izlaz za pogre²ke<br />
(System.err).<br />
Iznimku se hvata klju£nom rije£ju catch. ƒitava konstrukcija je catch<br />
( VrstaIznimke imeParametra ) i vrlo je srodna konstrukciji za deklaraciju<br />
parametra metode. Sama iznimka koja ¢e biti uhva¢ena moºe biti instanca<br />
navedene klase ili bilo koje potklase isto vrijedi i za parametar metode.<br />
Nakon ove konstrukcije slijedi blok koda u kojem se imeParametra moºe<br />
koristiti kao i svaka druga varijabla. U gornjem primjeru nad njom se poziva<br />
metoda getMessage, kojom raspolaºe svaka iznimka i vra¢a poruku koja joj<br />
je dodijeljena prilikom bacanja.<br />
Ako je iznimka uspje²no uhva¢ena u odgovaraju¢em bloku catch, ne¢e<br />
se dalje bacati. Nakon izvr²enja ovog bloka program dalje nastavlja normalno,<br />
izvr²avanjem koda koji slijedi nakon £itave konstrukcije try...catch.<br />
Ostatak koda nakon pojave iznimke pa do kraja bloka try ne¢e se izvr²iti<br />
a to je i bio cilj. U protivnom bismo se ponovo susreli s problemom ²to
78 POGLAVLJE 4. IZNIMKE<br />
u£initi npr. s neinicijaliziranom varijablom b.<br />
S druge strane, ako ba£ena iznimka nije one vrste koja se hvata catchom,<br />
pona²anje ¢e biti kao da tog bloka i nema iznimka ¢e se proslijediti<br />
dalje, pozivaju¢oj metodi.<br />
4.3 Zavr²ne radnje prije bacanja iznimke blok<br />
finally<br />
Blok try moºemo popratiti i posebnim blokom koda koji ¢e se sigurno<br />
izvr²iti, bez obzira je li do²lo do iznimke. To je prvenstveno korisno za<br />
slu£aj kad dože do iznimke jer u tom bloku imamo ²ansu obaviti neku<br />
zavr²nu radnju npr. tipi£an slu£aj je zatvaranje datoteka, internetskih<br />
konekcija i sl. prije nego ²to se metoda prekine bacanjem iznimke. Za to<br />
sluºi klju£na rije£ finally:<br />
public class ModuloCounter<br />
{<br />
...<br />
Ispis 4.5: Blok finally<br />
private void setModulus( int newModulus )<br />
{<br />
if ( newModulus < 0 )<br />
throw new IllegalArgumentException( "Negativan ciklus brojanja" );<br />
}<br />
}<br />
...<br />
public class AppThatUsesTheCounter<br />
{<br />
private static boolean intermediateMethod( int i )<br />
{<br />
try<br />
{<br />
ModuloCounter mc = new ModuloCounter();<br />
mc.setModulus( i );<br />
}<br />
finally<br />
{<br />
System.out.println( "intermediateMethod zavr²ava" );<br />
}<br />
return true;<br />
}
POGLAVLJE 4. IZNIMKE 79<br />
}<br />
public static void main( String[] args )<br />
{<br />
boolean b;<br />
try<br />
{<br />
b = intermediateMethod( -1 );<br />
System.out.println( "intermediateMethod je vratila " + b );<br />
}<br />
catch ( IllegalArgumentException e )<br />
{<br />
System.out.println( "Iznimka: " + e.getMessage() );<br />
}<br />
b = intermediateMethod( 1 );<br />
System.out.println(<br />
"Drugi poku²aj: intermediateMethod je vratila " + b );<br />
}<br />
O£ekivan ispis za ovaj, ve¢ dosta sloºen slu£aj je:<br />
intermediateMethod zavr²ava<br />
Iznimka: Negativan ciklus brojanja<br />
intermediateMethod zavr²ava<br />
Drugi poku²aj: intermediateMethod je vratila true<br />
Vidimo kako se sada tekst intermediateMethod zavr²ava ispisuje u<br />
oba slu£aja i kad je do²lo do iznimke, i kad nije. U gornjem primjeru blok<br />
finally kori²ten je samostalno, kao i blok catch u metodi main, ali op¢enito<br />
oni se mogu i kombinirati. Jedino treba po²tivati pravilo da finally dože<br />
na kraj, iza catch.<br />
U primjeru je kori²tena jo² jedna metoda denirana za sve iznimke <br />
getMessage. Ona vra¢a znakovni niz koji je prosliježen konstruktoru prilikom<br />
bacanja iznimke.<br />
4.4 Vi²estruki blokovi catch<br />
Jo² jedna prednost mehanizma iznimki je u tome ²to moºemo imati podulji<br />
blok koda u kojem se mogu pojavljivati raznorazne iznimke bez potrebe da<br />
se svaka obražuje tamo gdje se pojavila. Da bi ta prednost bila u potpunosti<br />
iskoristiva, mogu¢e je nakon bloka try imati ne jedan, nego proizvoljno<br />
mnogo blokova catch, svaki za svoju vrstu iznimke. Od svih njih izvr²it ¢e<br />
najvi²e jedan, i to prvi £ija vrsta parametra odgovara vrsti iznimke. Realisti£an<br />
primjer u kojem je potrebno hvatati razne iznimke bio bi prili£no<br />
kompleksan pa ¢emo ovdje dati samo shematski primjer:
80 POGLAVLJE 4. IZNIMKE<br />
Ispis 4.6: Vi²estruki blokovi catch<br />
public int someMethod()<br />
{<br />
try<br />
{<br />
method1();<br />
method2();<br />
method3();<br />
return 0;<br />
}<br />
catch ( IllegalArgumentException e )<br />
{<br />
System.err.println( "Pogre²an argument" );<br />
}<br />
catch ( NullPointerException e )<br />
{<br />
System.err.println( "Nul-referenca" );<br />
}<br />
catch ( FileNotFoundException e )<br />
{<br />
System.err.println( "Datoteka ne postoji" );<br />
}<br />
catch ( Exception e )<br />
{<br />
System.err.println( "Nepredvižena iznimka" );<br />
}<br />
finally<br />
{<br />
System.err.println( "Blok try zavr²ava" );<br />
}<br />
return -1;<br />
}<br />
Uo£imo da se u posljednjem bloku catch hvata vrsta Exception. Taj blok<br />
je tu da uhvati bilo koju iznimku koju nije uhvatio nijedan prethodni blok.<br />
Vaºno je, naravno, da taj blok bude posljednji, u protivnom ¢e uvijek on<br />
hvatati iznimku.<br />
Potpuni redoslijed izvr²avanja je sljede¢i: prvo se normalno izvr²ava kod<br />
bloka try. Ako ne dože do iznimke, u posljednjem retku bloka try metoda<br />
treba uredno zavr²iti s povratnom vrijednosti 0. Mežutim, prije toga izvr-<br />
²ava se blok finally. Ako dože do iznimke, izvr²ava se odgovaraju¢i blok<br />
catch i nakon njega finally. U tom slu£aju nakon izvr²enja finally izvr²avanje<br />
nastavlja redovitim putem i nailazi se na return -1 pa metoda vra¢a<br />
vrijednost -1. Ako se pojavi iznimka koju se ne hvata, izvr²ava se blok<br />
finally i nakon toga metoda se prekida ponovnim bacanjem iste iznimke.<br />
Vidimo dakle da se blok finally izvr²ava apsolutno uvijek, bez obzira za-
POGLAVLJE 4. IZNIMKE 81<br />
vr²ava li blok try na redovan ili izniman na£in i bez obzira je li iznimka<br />
uhva¢ena ili nije.<br />
4.5 Najava iznimke; provjeravana i neprovjeravana<br />
iznimka<br />
Iznimke se u Javi dijele na dvije podvrste: provjeravane (eng. checked) i<br />
neprovjeravane (eng. unchecked). Sve neprovjeravane iznimke izvedene su iz<br />
RuntimeException; ostale su provjeravane. Sama RuntimeException je izravna<br />
potklasa spomenute klase Exception.<br />
Ako se moºe dogoditi da metoda baci provjeravanu iznimku, tada ju je<br />
potrebno najaviti klju£nom rije£ju throws:<br />
import java.io.*;<br />
...<br />
Ispis 4.7: Najava iznimke<br />
public static void fileMethod()<br />
throws FileNotFoundException<br />
{<br />
File f = new File( "/hr/fer/tel/file1.txt" );<br />
InputStream fis = new FileInputStream( f );<br />
...<br />
}<br />
U primjeru se koriste Javine ugražene klase za baratanje datotekama<br />
(iz paketa java.io). U konstruktoru klase FileInputStream najavljeno je da<br />
bi mogao baciti provjeravanu iznimku FileNotFoundException (ako zatraºena<br />
datoteka ne postoji). Budu¢i da tu iznimka ne hvata, na²a metoda takožer<br />
mora najaviti da bi mogla baciti ovu iznimku, u protivnom ¢e prevoditelj<br />
prijaviti pogre²ku da metoda moºe baciti doti£nu iznimku, ali to ne<br />
najavljuje.<br />
Iz navedenoga je jasno zna£enje pridjeva provjeravana: za takvu iznimku<br />
kompilator provjerava moºe li do¢i do njenog bacanja i inzistira da<br />
to bude najavljeno. Na² primjeri£ni kod ne sadrºi eksplicitnu izjavu throw<br />
new FileNotFoundException(), ali kompilatoru je dovoljna £injenica da se koristi<br />
konstruktor koji tu iznimku najavljuje. Op¢enito, spomenuti konstruktor<br />
moºe biti izveden i tako da nikad ne baci iznimku, ali kompilatoru je<br />
dovoljna sama najava. Isto tako, svaka metoda koja poziva na²u metodu
82 POGLAVLJE 4. IZNIMKE<br />
fileMethod morat ¢e se pobrinuti za ovu iznimku £ak i ako izbacimo redak<br />
u kojem se koristi sporni konstruktor.<br />
Najavljena iznimka korisna je za slu£ajeve kad do nje moºe do¢i tijekom<br />
normalnog, ispravnog izvr²avanja programa. Na primjer, u na²em<br />
slu£aju programer ne moºe unaprijed znati ho¢e li potrebna datoteka postojati<br />
svaki put kad se program izvr²ava. To op¢enito i ne mora zna£iti<br />
da je do²lo do pogre²ke radi se jednostavno o izvanrednoj situaciji koju<br />
se zahvaljuju¢i mehanizmu iznimaka moºe obraživati na mjestu gdje je to<br />
najprakti£nije. Kori²tenjem provjeravane iznimke posti¢i ¢emo to da programer<br />
ne moºe zabunom zaboraviti zapisati kod koji obražuje tu uvijek<br />
prisutnu mogu¢nost.<br />
S druge strane, neprovjeravana iznimka koristi se za slu£ajeve u kojima<br />
se prijavljuje neispravnost situacija do koje u ispravnom programu jednostavno<br />
ne smije do¢i. Takav je na² raniji primjer s metodom setModulus<br />
ona je dizajnirana da prima pozitivan broj i nijedan smislen poziv ne<br />
moºe biti s negativnim brojem. Dakle, mora da se radi o programerskoj<br />
pogre²ci. Mogu¢nosti za ovakve pogre²ke vrebaju na svakom koraku i kod<br />
bi bio neprihvatljivo nepregledan kad bi se inzistiralo na najavi ili obradi<br />
svake mogu¢e takve iznimke.<br />
U praksi uvijek postoje i rubni slu£ajevi, gdje je nejasno koju vrstu<br />
iznimke koristiti. Java dozvoljava da se i neprovjeravane iznimke najavljuju<br />
kao pomo¢ programeru koji koristi takvu metodu, da bude spreman na<br />
mogu¢nost pojave doti£ne iznimke, iako nije duºan i²ta poduzeti. Najava<br />
neprovjeravane iznimke nema nikakvog utjecaja na kompilator iznimka i<br />
dalje ostaje neprovjeravana.<br />
4.5.1 Najava vi²e iznimki<br />
Metoda moºe najaviti proizvoljan broj iznimki. Navodi ih se redom, odvojene<br />
zarezom, npr.<br />
public void throwingMethod()<br />
throws FileNotFoundException, ClassNotFoundException, NamingException<br />
{<br />
...<br />
}
POGLAVLJE 4. IZNIMKE 83<br />
4.6 Klasna hijerarhija iznimki<br />
Dosad je spomenuto nekoliko detalja o hijerarhiji klasa iznimki. Spomenuta<br />
je klasa Exception kao glavna klasa iz koje su izvedene druge iznimke. Mežutim,<br />
potpuna slika uklju£uje jo² nekoliko klasa. Prije svega tu je klasa<br />
Throwable, iz koje su izvedene to£no dvije klase: Error i Exception. Dakle,<br />
Throwable je u korijenu £itave klasne hijerarhije iznimki. Klasa Error i njene<br />
potklase rezervirane su za uporabu od strane Javinog virtualnog stroja i<br />
nisu namijenjene da ih se eksplicitno baca izjavom throw. Cjelokupna hijerarhija<br />
klasa £ije instance se mogu baciti prikazana je na slici 4.1. Dodatne<br />
klase iznimki koje uvode razvijatelji neizostavno moraju biti potklase od<br />
Exception.<br />
Throwable<br />
Error<br />
Exception<br />
pogreške koje<br />
signalizira JVM<br />
provjeravane<br />
iznimke<br />
RuntimeException<br />
neprovjeravane<br />
iznimke<br />
Slika 4.1: UML-ov dijagram klasa £ije instance se mogu baciti<br />
4.7 Kori²tenje ugraženih i vlastitih iznimki<br />
U Javinoj biblioteci postoji ve¢ mno²tvo iznimki, provjeravanih i neprovjeravanih.<br />
Nekoliko njih, neprovjeravanih, ima op¢enit karakter i u kodu ih<br />
treba koristiti gdje god zna£enje odgovara karakteru pogre²ke:
84 POGLAVLJE 4. IZNIMKE<br />
• NullPointerException baca se kad god se problem sastoji od toga da<br />
je neka referenca jednaka null umjesto da pokazuje na neki objekt.<br />
• IllegalArgumentException baca se kad se ustanovi da je metoda pozvana<br />
s pogre²nom vrijednosti nekog argumenta vrijednosti koja je<br />
protivna pravilima pozivanja doti£ne metode. U ovom poglavlju imali<br />
smo primjer pozivanja setModulus s negativnim brojem.<br />
• IllegalStateException baca se kad se neku metodu pozove dok je<br />
objekt u takvom stanju koje ne dopu²ta njezino pozivanje. Recimo da<br />
imamo klasu koja predstavlja broja£ koji odbroji do neke kona£ne vrijednosti<br />
i nakon toga ne moºe dalje, ve¢ se mora poni²titi. Ako u trenutku<br />
kad je broja£ ve¢ do²ao do gornje granice pozovemo increment,<br />
to bi bila situacija za bacanje ove iznimke.<br />
• ClassCastException baca se ako je metoda pozvana s instancom krive<br />
klase. To se dogaža tipi£no kad se radi o metodi koja je dio nekog<br />
su£elja pa formalno prima parametar op¢enitije vrste od one s kojom<br />
stvarno moºe raditi.<br />
• UnsupportedOperationException baca se ako je metoda pozvana nad<br />
objektom koji ju ne implementira. To se tipi£no dogaža kad objekt<br />
najavljuje da implementira neko su£elje, ali neka od metoda iz su£elja<br />
nije primjerena za takav objekt pa ju nema smisla implementirati.<br />
Za situacije speci£ne za softverski proizvod koji razvijamo treba denirati<br />
vlastitu klasu iznimke. Pravilo je da za £itav proizvod postoji samo<br />
jedna ili eventualno, za ve¢e projekte, nekoliko iznimki. Ako ih je vi²e, preporu£a<br />
se da sve budu izvedene iz jedne, op¢enite. Deniranje vlastite iznimke<br />
vrlo je jednostavno dovoljno je kreirati klasu izvedenu iz Exception<br />
za provjeravane odnosno RuntimeException za neprovjeravane iznimke. U<br />
klasi treba samo denirati konstruktore koji primaju iste parametre kao<br />
ve¢ postoje¢i konstruktori u natklasi i samo prosliježuju poziv na njih.<br />
Primjer:<br />
public class CounterException<br />
extends RuntimeException<br />
{<br />
public CounterException()<br />
Ispis 4.8: Deniranje vlastite iznimke
POGLAVLJE 4. IZNIMKE 85<br />
{<br />
}<br />
super();<br />
}<br />
public CounterException( String message )<br />
{<br />
super( message );<br />
}<br />
public CounterException( String message, Throwable cause )<br />
{<br />
super( message, cause );<br />
}<br />
public CounterException( Throwable cause )<br />
{<br />
super( cause );<br />
}<br />
Ovime je denirana neprovjeravana iznimka CounterException. Denirana<br />
su £etiri standardna konstruktora i sve ²to rade je da pozivaju isti takav<br />
konstruktor u natklasi.
Poglavlje 5<br />
Kolekcije objekata<br />
Jedna vrlo zna£ajna stvar u svakom programskom jeziku su kolekcije podataka.<br />
Kolekcija je jednostavno skupina objekata koju se moºe tretirati<br />
kao cjelinu. Osnovni primjeri su lista i skup, a Java podrºava jo² i repove.<br />
Struktura srodna kolekciji je i mapa (skup parova klju£-vrijednost).<br />
Jezik Java pruºa dobru podr²ku za kolekcije objekata. Kolekcije primitivnih<br />
podataka podrºane su neizravno putem posebnih objekata-omota£a<br />
primitivnih podataka. U biblioteci klasa, u paketu java.util, nalazi se pove¢i<br />
broj su£elja i klasa koje sa£injavaju sustav za manipulaciju kolekcija,<br />
tzv. Collections Framework.<br />
5.1 Su£elja Collection, List i Set<br />
Su£elje Collection jedno je od temeljnih su£elja u Collections Framework-u.<br />
Apstrahira mogu¢nosti zajedni£ke za sve kolekcije liste, skupove i repove.<br />
To su£elje je denirano ponajprije iz razloga koji je iznesen u odjeljku 1.7 <br />
da putem varijable (parametra) ove vrste moºemo baratati bilo kojom kolekcijom<br />
u opsegu funkcionalnosti koji je zajedni£ki za sve njih. Uz kolekcije<br />
veºu se i dosad neprikazana jezi£na svojstva Jave tzv. generics. Pogledajmo<br />
metodu u kojoj se demonstrira nekoliko osnovnih tehnika baratanja<br />
kolekcijom:<br />
Ispis 5.1: Osnovno baratanje kolekcijom<br />
static void manipulateCollection( Collection col )<br />
{<br />
String msg = "Poruka";<br />
boolean b = col.add( msg );<br />
87
88 POGLAVLJE 5. KOLEKCIJE OBJEKATA<br />
if ( b )<br />
System.out.println( "Poruka dodana u kolekciju" );<br />
System.out.println( "Sadrºi li kolekcija poruku? " +<br />
col.contains( msg ) );<br />
b = col.add( msg );<br />
if ( b )<br />
System.out.println( "Poruka dodana u kolekciju drugi put" );<br />
System.out.println( "Broj elemenata u kolekciji: " + col.size() );<br />
b = col.remove( msg );<br />
if ( b )<br />
System.out.println( "Poruka uklonjena iz kolekcije" );<br />
System.out.println( "Sadrºi li kolekcija poruku? " +<br />
col.contains( msg ) );<br />
}<br />
System.out.println( "Je li kolekcija prazna? " + col.isEmpty() );<br />
Sintaksu vezanu uz generics vidimo u prvom retku, u deklaraciji parametra<br />
metode: Collection col. Time nazna£ujemo da kolekcija sadrºi<br />
instance klase String. Vrsta Collection je tzv. parametrizirana<br />
vrsta parametar je vrsta objekata koji se u njoj nalaze. Kôd demonstrira<br />
kori²tenje metoda su£elja Collection: add, contains, isEmpty, remove, size.<br />
Ispis ¢e se razlikovati ovisno o tome koja konkretna implementacija kolekcije<br />
se proslijedi metodi. Razlika ¢e prije svega biti izmežu lista i skupova.<br />
Napravimo ovakvu metodu main da to ispitamo:<br />
Ispis 5.2: Lista i skup<br />
public static void main( String[] args )<br />
{<br />
List list1 = new ArrayList();<br />
List list2 = new LinkedList();<br />
}<br />
Set set1 = new HashSet();<br />
Set set2 = new TreeSet();<br />
System.out.println( "Poziv s ArrayList:" );<br />
manipulateCollection( list1 );<br />
System.out.println( "\nPoziv s LinkedList:" );<br />
manipulateCollection( list2 );<br />
System.out.println( "\nPoziv s HashSet:" );<br />
manipulateCollection( set1 );<br />
System.out.println( "\nPoziv s TreeSet:" );<br />
manipulateCollection( set2 );
POGLAVLJE 5. KOLEKCIJE OBJEKATA 89<br />
Ovdje je uveden £itav niz novih vrsta podataka. List i Set su su£elja<br />
izvedena iz su£elja Collection. Svako od njih dodaje metode speci£ne za<br />
listu odnosno skup. ArrayList i LinkedList su konkretne klase koje implementiraju<br />
listu. U praksi se rutinski koristi ArrayList koja elemente liste<br />
drºi u polju (eng. array), a LinkedList samo iznimno, kad neko od svojstava<br />
ArrayList ne zadovoljava (npr. ako ¢e postojati velike oscilacije u<br />
broju pospremljenih elemenata). HashSet i TreeSet su konkretne klase koje<br />
implementiraju skup. Rutinski se koristi HashSet, na bazi he²-tablice. Ovo je<br />
jedan primjer klase koja ne¢e ispravno raditi ako ju koristimo s objektima<br />
koji nemaju ispravno izvedene metode equals i hashCode (vidjeti odjeljak<br />
3.2). TreeSet je speci£an po tome ²to sortira svoje elemente (implementira<br />
i posebno su£elje za sortirane skupove SortedSet). Da bi sortiranje bilo<br />
mogu¢e, objekti trebaju imati deniran prirodni poredak (vidjeti odjeljak<br />
5.6.1). Svi hijerarhijski odnosi spominjanih referentnih vrsta prikazani su<br />
na slici 5.1.<br />
Za ArrayList i LinkedList dobit ¢emo sljede¢i ispis:<br />
Poruka dodana u kolekciju<br />
Sadrºi li kolekcija poruku? true<br />
Poruka dodana u kolekciju drugi put<br />
Broj elemenata u kolekciji: 2<br />
Poruka uklonjena iz kolekcije<br />
Sadrºi li kolekcija poruku? true<br />
Poruka uklonjena iz kolekcije drugi put<br />
Je li kolekcija prazna? true<br />
Za HashSet i TreeSet ispis ¢e izgledati druga£ije:<br />
Poruka dodana u kolekciju<br />
Sadrºi li kolekcija poruku? true<br />
Broj elemenata u kolekciji: 1<br />
Poruka uklonjena iz kolekcije<br />
Sadrºi li kolekcija poruku? false<br />
Je li kolekcija prazna? true<br />
Primijetimo da poruka nije dodana/uklonjena po drugi put jer skup po<br />
deniciji sadrºi elemente bez ponavljanja. Dodatna osobina skupa je da ne<br />
zadrºava poredak kojim su elementi dodavani u njega (za razliku od liste).<br />
U metodi main kori²ten je udoma¢eni pristup da se za vrstu varijable<br />
uvijek koristi su£elje tipi£no List ili Set, ovisno o namjeni a nikada<br />
konkretne vrste ArrayList ili HashSet. Same klase ne deniraju gotovo nijednu<br />
dodatnu metodu i gotovo nikad nema razloga koristiti ih za vrstu<br />
varijable (ili parametra, atributa itd.). Njihovim kori²tenjem samo onemogu¢ujemo<br />
kasniji jednostavan prelazak na druga£iju implementaciju.
90 POGLAVLJE 5. KOLEKCIJE OBJEKATA<br />
«interface»<br />
Collection<br />
«interface»<br />
SortedSet<br />
«interface»<br />
Set<br />
«interface»<br />
List<br />
TreeSet<br />
HashSet<br />
ArrayList<br />
LinkedList<br />
Slika 5.1: UML-ov dijagram klasa za Collection-Framework<br />
5.2 Prelazak preko svih £lanova kolekcije<br />
Naj£e²¢i na£in kori²tenja kolekcije je da se priježe po svakom njenom £lanu,<br />
jedan po jedan, i obavi neki posao. Za to postoji posebna jezi£na konstrukcija,<br />
tzv. petlja enhanced for 1 :<br />
Ispis 5.3: Prolaz po kolekciji petljom enhanced for<br />
static void iterateOverCollection( Collection col )<br />
{<br />
for ( String s : col )<br />
System.out.println( "ƒlan kolekcije: " + s );<br />
}<br />
public static void main( String[] args )<br />
{<br />
List list = new ArrayList();<br />
list.add( "a" );<br />
list.add( "b" );<br />
list.add( "c" );<br />
iterateOverCollection( list );<br />
}<br />
U metodi iterateOverCollection demonstrirana je konstrukcija enhanced<br />
for. Izraz u zagradama prije dvoto£ke je deklaracija varijable koja ¢e u<br />
svakom koraku petlje sadrºati po jedan element kolekcije. ƒlan iza dvoto£ke<br />
je kolekcija po kojoj treba obaviti prelazak.<br />
1 U praksi se jo² koriste i izrazi for each i for/in
POGLAVLJE 5. KOLEKCIJE OBJEKATA 91<br />
Ako metodu main prepravimo da koristi HashSet, mo¢i ¢emo uo£iti da se<br />
po elementima ne prolazi u istom poretku u kojem smo ih dodavali:<br />
public static void main( String[] args )<br />
{<br />
Set set = new HashSet();<br />
set.add( "a" );<br />
set.add( "b" );<br />
set.add( "c" );<br />
iterateOverCollection( set );<br />
}<br />
Ispis:<br />
ƒlan kolekcije: a<br />
ƒlan kolekcije: c<br />
ƒlan kolekcije: b<br />
5.3 Kolekcije i primitivne vrste podataka<br />
Podaci primitivne vrste ne mogu se spremati u kolekcije pa se koriste<br />
objekti-omota£i podataka primitivnih vrsta. Jezi£no svojstvo autoboxing/unboxing<br />
dozvoljava sintasku po kojoj izgleda kao da se spremaju sami podaci primitivne<br />
vrste. Primjer:<br />
Ispis 5.4: Kolekcija i primitivne vrste podataka<br />
public static void main( String[] args )<br />
{<br />
List intList = new ArrayList();<br />
intList.add( 2 );<br />
intList.add( 3 );<br />
intList.add( 4 );<br />
if ( intList.contains( 3 ) )<br />
System.out.println( "Lista sadrºi broj 3" );<br />
for ( int i : intList )<br />
System.out.println( "ƒlan liste: " + i );<br />
}<br />
List boolList = new ArrayList();<br />
boolList.add( true );<br />
boolList.add( 2 == 3 );<br />
List charList = new ArrayList();<br />
charList.add( 'a' );<br />
charList.add( 'b' );<br />
char c = charList.get( 0 ); // dohva¢a prvi element liste
92 POGLAVLJE 5. KOLEKCIJE OBJEKATA<br />
5.4 Napredni prelazak po £lanovima kolekcije<br />
Konstrukcija enhanced for prikazana u odjeljku 5.2 kra¢i je zapis za ovakav<br />
kod, koji se implicitno generira prilikom prevoženja te konstrukcije i koristi<br />
poznatu sintaksu petlje for:<br />
Ispis 5.5: Prolaz po kolekciji eksplicitnim kori²tenjem iteratora<br />
static void iterateOverCollection( Collection col )<br />
{<br />
for ( Iterator iter = col.iterator(); iter.hasNext(); )<br />
{<br />
String s = iter.next();<br />
System.out.println( "ƒlan kolekcije: " + s );<br />
}<br />
}<br />
(u realnom slu£aju ne koristi se ime varijable iter, koje bi tada bilo rezervirano,<br />
nego neko ime koje se ne moºe normalno pojaviti u kodu.)<br />
Za neke speci£ne namjene potrebno je koristiti upravo ovakvu sintaksu<br />
jer nam je tako eksplicitno dostupan poseban objekt iterator po kolekciji.<br />
Njega moºemo zamisliti kao pokaziva£ na trenutni objekt u kolekciji.<br />
Preusmjerujemo ga na sljede¢i element pozivom metode next, £ija povratna<br />
vrijednost je taj sljede¢i element. Prije prvog poziva te metode iterator jo²<br />
ne pokazuje ni na jedan element, tako da se prvim pozivom dobiva prvi<br />
element.<br />
Jedna od dodatnih mogu¢nosti je metoda remove kojom se trenutni<br />
objekt uklanja iz kolekcije:<br />
Ispis 5.6: Kori²tenje iteratora za mijenjanje liste<br />
public static void main( String[] args )<br />
{<br />
List intList = new ArrayList();<br />
intList.add( 2 );<br />
intList.add( 3 );<br />
intList.add( 4 );<br />
}<br />
for ( Iterator iter = intList.iterator(); iter.hasNext(); )<br />
{<br />
int i = iter.next();<br />
if ( i == 3 )<br />
iter.remove();<br />
}<br />
for ( int i : intList )<br />
System.out.println( "ƒlan kolekcije: " + i );
POGLAVLJE 5. KOLEKCIJE OBJEKATA 93<br />
Postoje i druge mogu¢nosti, o kojima se moºe informirati u dokumentaciji<br />
[1]. Za prolazak po listama postoji i specijalizirani ListIterator koji<br />
nudi jo² vi²e mogu¢nosti, speci£nih za liste.<br />
5.5 Mapa<br />
Pojam mapa odnosi se na skup preslikavanja izmežu nekih podataka (vrijednosti)<br />
i s njim povezanih identikacijskih podataka (njihovih klju£eva).<br />
Na primjer, ve¢ina baza podataka koje sadrºe podatke o drºavljanima RH<br />
kao klju£ koriste JMBG jer je to kratak podatak jedinstven za svakog gražana.<br />
Klju£ je dakle ²to kra¢i podatak na osnovu kojega se moºe nedvosmisleno<br />
identicirati neki ve¢i slog podataka.<br />
Mapa je korisna struktura podataka za velik broj namjena, npr. kad<br />
god imamo zbirku objekata koje je potrebno dohva¢ati po imenu ili nekom<br />
drugom ID-ju. U Javi je za mape denirano su£elje Map. Ovaj primjer<br />
demonstrira njegovu uporabu:<br />
Ispis 5.7: Klasa Student koja demonstrira mapu<br />
public class Student<br />
{<br />
private String id;<br />
private String firstName;<br />
private String lastName;<br />
public Student( String id, String firstName, String lastName )<br />
{<br />
this.id = id;<br />
this.firstName = firstName;<br />
this.lastName = lastName;<br />
}<br />
... metode getId, getFirstName, getLastName ...<br />
public String toString()<br />
{<br />
return this.firstName + " " + this.lastName;<br />
}<br />
public static void main( String[] args )<br />
{<br />
Student stud1 = new Student( "id1", "Bill", "McGill" );<br />
Student stud2 = new Student( "id2", "John", "Pepper" );<br />
Map students = new HashMap();<br />
students.put( stud1.id, stud1 );
94 POGLAVLJE 5. KOLEKCIJE OBJEKATA<br />
students.put( stud2.id, stud2 );<br />
}<br />
}<br />
Student fetchedStudent = students.get( "id2" );<br />
System.out.println( "Dohva¢eni student: " + fetchedStudent );<br />
Denirana je mala klasa koja sadrºi podatke o studentu. Instance te<br />
klase spremaju se u mapu tako da se kao klju£ koristi atribut id. Op¢enito<br />
nije nuºno da klju£ bude dio objekta, ali je uobi£ajeno. I za klju£ i<br />
za vrijednost moºe se koristiti bilo koji objekt, bilo koje vrste. Vrsta Map je<br />
parametrizirana s dva parametra prvi je vrsta klju£a, a drugi vrsta vrijednosti.<br />
Isto se, naravno, odnosi i na HashMap. Ovo je, ina£e, jo² jedna klasa<br />
koja se oslanja na ispravno funkcioniranje metoda equals i hashCode u objektima<br />
koji se koriste kao klju£. Klasa String ima uredno implementirane ove<br />
metode pa ne¢e biti problema.<br />
Iz primjera mogu se i²£itati dvije najvaºnije metode su£elja Map: put(<br />
key, value ) i get( key ). Za ostale raspoloºive metode treba konzultirati<br />
dokumentaciju [1].<br />
5.6 ƒesto kori²teni algoritmi nad kolekcijama<br />
U paketu java.util postoji klasa Collections koja ne sluºi za instanciranje,<br />
ve¢ sadrºi pove¢i broj stati£kih metoda koje provode £esto kori²tene<br />
algoritme za obradu kolekcija. Postoje npr. shuffle, reverse, sort, min, max<br />
itd. Za ove algoritme kaºe se da su polimorfni jer, sluºe¢i se Javinim polimorzmom,<br />
mogu raditi s velikim brojem razli£itih vrsta kolekcija. Za<br />
mnoge algoritme (prije svega sort) potrebno je da elementi kolekcije budu<br />
usporedivi, ²to tehni£ki zna£i da moraju ispravno implementirati su£elje<br />
Comparable.<br />
5.6.1 Implementiranje su£elja java.lang.Comparable<br />
Su£elje Comparable je parametrizirano parametar je neka vrsta T. U<br />
na£elu se kao parametar navodi upravo klasa u kojoj implementiramo to<br />
su£elje. Time kaºemo kompilatoru da se objekte na²e klase moºe usporeživati<br />
samo s drugim objektima iste klase. Op¢enito, tu bi se mogla staviti<br />
bilo koja klasa, ali druge vrijednosti ne¢e imati smisla osim eventualno
POGLAVLJE 5. KOLEKCIJE OBJEKATA 95<br />
neke natklase trenutne klase, u slu£aju da imamo vi²e sli£nih klasa £ije<br />
instance se mogu mežusobno usporeživati.<br />
Ovo su£elje sadrºi samo jednu metodu: compareTo( T ). Povratna vrijednost<br />
je int koji mora biti nula za jednake objekte, negativan ako je<br />
trenutni objekt manji od prosliježenog i pozitivan ako je trenutni objekt<br />
ve¢i od prosliježenog. Zna£enje pojmova manji odnosno ve¢i ovisi o<br />
vrsti objekta. Poredak objekata u kojem se nijedan objekt ne pojavljuje<br />
prije nekog drugog koji je od njega manji naziva se prirodnim poretkom<br />
tih objekata. Metoda compareTo, dakle, odrežuje prirodni poredak instanci<br />
klase u kojoj je implementirana. Na primjer, u klasi Student iz ispisa 5.7<br />
mogli bismo tu metodu implementirati ovako:<br />
public class Student<br />
implements Comparable<br />
{<br />
... isti kod kao ranije ...<br />
}<br />
Ispis 5.8: Metoda compareTo<br />
public int compareTo( Student that )<br />
{<br />
return this.lastName.compareTo( that.lastName );<br />
}<br />
Klasa sad najavljuje da implementira su£elje Comparable. Metoda compareTo,<br />
oslanjaju¢i se na String-ovu implementaciju iste metode, usporežuje studente<br />
po prezimenu.<br />
5.6.2 Poredak razli£it od prirodnog su£elje<br />
java.util.Comparator<br />
Sve metode u klasi Collections koje se oslanjaju na prirodni poredak objekata<br />
imaju i verzije koje se oslanjaju na poseban objekt-komparator, koji<br />
moºe nametnuti proizvoljan poredak nad objektima. Takav objekt mora<br />
implementirati su£elje java.util.Comparator. Evo primjera komparatora<br />
za studente koji name¢e poredak po ID-ju:<br />
Ispis 5.9: Komparator za studente koji name¢e poredak po ID-ju<br />
public class StudentComparatorById<br />
implements java.util.Comparator<br />
{
96 POGLAVLJE 5. KOLEKCIJE OBJEKATA<br />
}<br />
public int compare( Student left, Student right )<br />
{<br />
return left.getId().compareTo( right.getId() );<br />
}<br />
5.6.3 Primjer kori²tenja metode Collections.sort<br />
public static void main( String[] args )<br />
{<br />
List students = Arrays.asList( new Student[]<br />
{<br />
new Student( "005", "Joshua", "Bloch" ),<br />
new Student( "003", "Paul", "Graham" ),<br />
new Student( "004", "Richard", "Dawkins" ),<br />
new Student( "002", "Rick", "Martinoff" ),<br />
}<br />
);<br />
}<br />
System.out.println( "Po£etni popis:" );<br />
for ( Student student : students )<br />
System.out.println( student );<br />
Collections.sort( students );<br />
System.out.println( "\nStudenti sortirani po prezimenu:" );<br />
for ( Student student : students )<br />
System.out.println( student );<br />
Collections.sort( students, new StudentComparatorById() );<br />
System.out.println( "\nStudenti sortirani po ID-ju:" );<br />
for ( Student student : students )<br />
System.out.println( student );<br />
O£ekivani ispis:<br />
Po£etni popis:<br />
005 Joshua Bloch<br />
003 Paul Graham<br />
004 Richard Dawkins<br />
002 Rick Martinoff<br />
Studenti sortirani po prezimenu:<br />
005 Joshua Bloch<br />
004 Richard Dawkins<br />
003 Paul Graham<br />
002 Rick Martinoff<br />
Studenti sortirani po ID-ju:<br />
002 Rick Martinoff<br />
003 Paul Graham<br />
004 Richard Dawkins
POGLAVLJE 5. KOLEKCIJE OBJEKATA 97<br />
005 Joshua Bloch
Poglavlje 6<br />
Polja<br />
Polje je blok u memoriji u koji je spremljen niz podataka iste vrste. U<br />
Javi polje je izvedeno kao objekt, ali posebnog karaktera. Polja su objekti<br />
posebne referentne vrste polja i njima se barata putem referenci, kao i<br />
ostalim objektima. Ovo treba uvijek imati na umu jer vrijedi sve re£eno za<br />
referentne varijable u odjeljku 2.1.2.<br />
Sva polja u osnovi su jednodimenzionalna. Dvodimenzionalno polje izvedeno<br />
je kao jednodimenzionalno polje referenci na druga jednodimenzionalna<br />
polja.<br />
6.1 Osnovni elementi sintakse<br />
Jednodimenzionalna polja:<br />
Ispis 6.1: Rad s jednodimenzionalim poljem<br />
int[] intArray; // deklaracija varijable polja<br />
intArray = new int[3]; // kreiranje praznog polja (popunjeno nulama)<br />
int i = intArray[0]; // pristup £lanu polja<br />
intArray = { 1, 2, 3 }; // kreiranje uz popunu<br />
// navedenim vrijednostima<br />
Uo£imo sintaksu u posljednjem retku: na desnoj strani ne treba koristiti<br />
operator new i to£nu vrstu podataka, dovoljan je samo doslovan popis £lanova.<br />
Java ¢e automatski stvoriti polje one vrste koja je deklarirana na<br />
lijevoj strani.<br />
ƒlanovi polja inicijaliziraju se po istim pravilima kao i atributi objekta<br />
brojevi na nulu, logi£ke vrijednosti na false, a reference na null.<br />
Dvodimenzionalna polja (analogno i za vi²edimenzionalna):<br />
99
100 POGLAVLJE 6. POLJA<br />
Ispis 6.2: Rad s dvodimenzionalnim poljem<br />
int[][] intArray;<br />
intArray = new int[2][3];<br />
int i = intArray[0][0];<br />
intArray = { {1, 2}, {3, 4}, {5, 6} };<br />
Petlja enhanced for za prolaz po svim £lanovima polja:<br />
Ispis 6.3: Prolaz po svim £lanovima polja petljom enhanced for<br />
public static void main( String[] args )<br />
{<br />
int[] intArray = { 1, 2, 3, 4, 5 };<br />
for ( int i : intArray )<br />
System.out.print( " " + i );<br />
}<br />
System.out.println();<br />
Dvodimenzionalno polje ne mora nuºno biti pravokutno, ²to slijedi iz<br />
re£enog da je to ustvari polje polja. Sljede¢i primjer demonstrira ovo:<br />
Ispis 6.4: Trokutasto dvodimenzionalno polje<br />
public static void main( String[] args )<br />
{<br />
int[][] int2Array = { {11}, {21, 22}, {31, 32, 33} };<br />
for ( int[] intArray : int2Array )<br />
{<br />
for ( int i : intArray )<br />
System.out.print( " " + i );<br />
}<br />
}<br />
System.out.println();<br />
U prvom retku koda kreira se polje polja tako da je njegov prvi £lan polje<br />
duljine jedan, drugi £lan polje duljine dva i tre¢i £lan polje duljine tri.<br />
Zatim slijedi dvostruka petlja u kojoj se ispisuje cjelokupan sadrºaj stvorene<br />
strukture. Time se vizualizira trokutasti izgled strukture. Ispis je ovakav:<br />
11<br />
21 22<br />
31 32 33<br />
Polja mogu sadrºavati i reference na objekte. U deklaraciji se navodi<br />
referentna vrsta, npr. String[] stringArray.
POGLAVLJE 6. POLJA 101<br />
6.2 Polje kao objekt<br />
Polje, iako nije u pravom smislu instanca neke klase, svejedno je objekt<br />
koji je instanceof java.lang.Object, pa raspolaºe i svim odgovaraju¢im metodama.<br />
Svako polje ima i cjelobrojni atribut length, jednak njegovom kapacitetu.<br />
Atribut je final jer se kapacitet polja ne moºe naknadno mijenjati.<br />
Koristi ga se npr. za sloºenije primjere prolaska po svim £lanovima polja:<br />
Ispis 6.5: Prolaz po polju kori²tenjem eksplicitnog indeksa<br />
int[] intArray = new int[3];<br />
for ( int i = 0; i < intArray.length; i++ )<br />
{<br />
int j = intArray[i];<br />
...<br />
}<br />
6.3 Interakcija s kolekcijama<br />
Kolekcije su eksibilnije i op¢enito korisnije strukture od polja. Mežutim,<br />
polja su siroviji oblik strukture i stoga ra£unski i memorijski ekasnija.<br />
Prednost je i ²to se za rad s poljima moºe koristiti elegantnija sintaksa.<br />
Javina biblioteka podrºava laku transformaciju izmežu kolekcija i polja:<br />
Ispis 6.6: Pretvorbe izmežu polja i liste<br />
String[] stringArray = { "Harry", "Moe", "Joe" };<br />
// konverzija polja u listu:<br />
List stringList = Arrays.asList( stringArray );<br />
// konverzija liste u polje:<br />
String[] stringArray2 = stringList.toArray( new String[0] );<br />
Metodi toArray mora se proslijediti makar prazno polje jer je potrebna informacija<br />
o tome koju vrstu polja treba stvoriti. Ovdje, naºalost ne pomaºu<br />
generics i to se ne dogaža automatski. Ako je prosliježeno polje dovoljno<br />
veliko, iskoristit ¢e se; u protivnom se stvara novo.<br />
Metoda asList moºe se iskoristiti i za jednostavno stvaranje liste popunjene<br />
doslovno navedenim vrijednostima:<br />
List stringList = Arrays.asList( "Harry", "Moe", "Joe" );<br />
Dobivena lista ima ksiran sadrºaj zabranjeno je pozivati metode add,<br />
remove i ostale koje bi ga mijenjale.
Poglavlje 7<br />
Komunikacija s okolinom<br />
Svaka aplikacija treba komunicirati s okolinom. To se prije svega odnosi na<br />
korisni£ko su£elje (tipkovnica i zaslon), sustav datoteka i ra£unalsku mreºu.<br />
Ovdje se prikazuje na koji na£in se u jeziku Java komunicira s okolinom.<br />
7.1 Tokovi podataka<br />
Osnovna paradigma koja se u Javi koristi za razmjenu podataka s okolinom<br />
je tok podataka (eng. data stream). Postoje ulazni i izlazni tokovi i predstavljeni<br />
su apstraktnim klasama InputStream odnosno OutputStream. Nalaze<br />
se u paketu posve¢enom komunikaciji, java.io. Za svaku vrstu resursa s kojim<br />
se moºe komunicirati (zaslon, datoteka, udaljeno ra£unalo itd.) postoje<br />
konkretne klase izvedene iz njih. Apstraktne klase deklariraju osnovne metode<br />
za rad s tokom available, read, reset i close za InputStream odnosno<br />
write, flush i close za OutputStream. Sljede¢i primjer demonstrira kori²tenje<br />
izlaznog toka:<br />
Ispis 7.1: Rad s izlaznim tokom<br />
import java.io.OutputStream;<br />
import java.io.IOException;<br />
...<br />
static void sendToOutput( OutputStream os )<br />
throws IOException<br />
{<br />
for ( int i = 0; i < 256; i++ )<br />
os.write( i );<br />
os.flush();<br />
103
104 POGLAVLJE 7. KOMUNIKACIJA S OKOLINOM<br />
... daljnje operacije nad tokom ...<br />
}<br />
os.close();<br />
Ova metoda moºe raditi s bilo kakvim izlaznim tokom. U nju ¢e zapisati<br />
niz okteta od nule do 255 i nakon toga ju zatvoriti. Metoda flush poziva se<br />
kad se ºeli osigurati da podaci budu odmah isporu£eni na odredi²te. U protivnom<br />
¢e se najvjerojatnije gomilati u mežuspremniku u radnoj memoriji<br />
dok ih se ne skupi dovoljno za masovno slanje. Kori²tenje mežuspremnika<br />
op¢enito zna£ajno podiºe ekasnost komunikacije jer se ²alju ve¢i blokovi<br />
podataka, tako da flush ne treba koristiti ako to nije zaista potrebno.<br />
Ovaj primjer demonstrira kori²tenje ulaznog toka:<br />
Ispis 7.2: Rad s ulaznim tokom<br />
import java.io.InputStream;<br />
import java.io.IOException;<br />
...<br />
static void receiveInput( InputStream is )<br />
throws IOException<br />
{<br />
while ( true )<br />
{<br />
int i = is.read();<br />
if ( i == -1 )<br />
break;<br />
System.out.println( i );<br />
}<br />
is.close();<br />
}<br />
Metoda read vra¢a pro£itani oktet. Ako vrati vrijednost -1, to zna£i da je<br />
tok do²ao do kraja. Op¢enito se ne moºe samo ispitati je li tok do²ao do<br />
kraja bez poku²aja £itanja pa ne postoji posebna metoda za to koju bi se<br />
moglo koristiti u uvjetu petlje while. Metoda available vra¢a broj okteta<br />
raspoloºivih odmah, bez £ekanja. Ako vrati nulu, to ne mora zna£iti da je<br />
tok do²ao do kraja, ve¢ samo da trenutno nema novih podataka.<br />
7.2 Pisa£i i £ita£i<br />
Za komunikaciju tekstualnim podacima postoje posebni omota£i sirovih<br />
tokova okteta koji ih pretvaraju u tokove znakova. U Javinoj terminologiji
POGLAVLJE 7. KOMUNIKACIJA S OKOLINOM 105<br />
to su pisa£i (osnovna klasa Writer) i £ita£i (osnovna klasa Reader). Koriste<br />
se sli£no kao sirove tokove, ali barataju podacima vrste char. Jo² je<br />
prakti£nije koristiti klase s ugraženim mežuspremnikom (BufferedReader i<br />
PrintWriter) koje omogu¢uju komunikaciju redak po redak umjesto znak<br />
po znak. Ovo je prikazano u primjerima:<br />
Ispis 7.3: Pisa£ i £ita£<br />
import java.io.Writer;<br />
import java.io.PrintWriter;<br />
import java.io.Reader;<br />
import java.io.BufferedReader;<br />
import java.io.IOException;<br />
...<br />
static void writeOutput( Writer writer )<br />
throws IOException<br />
{<br />
PrintWriter pw = new PrintWriter( writer );<br />
pw.println( "Prvi redak" );<br />
pw.println( "Drugi redak" );<br />
pw.flush();<br />
pw.close();<br />
}<br />
static void readInput( Reader reader )<br />
throws IOException<br />
{<br />
BufferedReader br = new BufferedReader( reader );<br />
while ( true )<br />
{<br />
String line = br.readLine();<br />
if ( line == null || line.length() == 0 )<br />
break;<br />
System.out.println( line );<br />
}<br />
br.close();<br />
}<br />
Metoda readInput zavr²it ¢e ako naiže na kraj ulaznog toka (line == null)<br />
ili ako pro£ita prazan redak (line.length() == 0).<br />
Pisa£ i £ita£ rade tako da se povezuju sa sirovim tokom okteta i obavljaju<br />
potrebne konverzije. Povezuje ih se pri instanciranju proslježivanjem<br />
objekta toka konstruktoru. Sljede¢i primjer prikazuje metode koje pozivaju<br />
metode iz ispisa 7.3 s odgovaraju¢e omotanim sirovim tokovima:<br />
import java.io.OutputStream;<br />
Ispis 7.4: Omota£i podatkovnih tokova
106 POGLAVLJE 7. KOMUNIKACIJA S OKOLINOM<br />
import java.io.OutputStreamWriter;<br />
import java.io.InputStream;<br />
import java.io.InputStreamReader;<br />
import java.io.IOException;<br />
...<br />
static void writeOutput( OutputStream os )<br />
throws IOException<br />
{<br />
writeOutput( new OutputStreamWriter( os ) );<br />
}<br />
static void readInput( InputStream is )<br />
throws IOException<br />
{<br />
readInput( new InputStreamReader( is ) );<br />
}<br />
7.3 Komunikacija s korisnikom<br />
Osnovna tekstualna komunikacija s korisnikom ostvaruje se putem standardnog<br />
ulaz/izlaza koji pruºa svaka ra£unalska platforma. U Javi se s<br />
njima povezuje putem stati£kih atributa u klasi System. Ispis na standardni<br />
izlaz sreli smo ve¢ mnogo puta u kodu, svaki put kad se koristio izraz<br />
System.out.println( ... ). Stati£ki atribut out sadrºi referencu na objekt<br />
vrste PrintStream, koja je vrlo srodna prikazanoj vrsti PrintWriter i denira<br />
mnoge identi£ne metode. Za prakti£no kori²tenje standardnog ulaza<br />
potrebno je eksplicitno dodati omota£e, kao u ispisu 7.4. Kod iz ispisa 7.3<br />
i 7.4 moºemo isprobati dodavanjem sljede¢e metode main:<br />
import java.io.IOException;<br />
...<br />
public static void main( String[] args )<br />
throws IOException<br />
{<br />
System.out.println( "Utipkaj ne²to!" );<br />
readInput( System.in );<br />
}<br />
U konzolu treba utipkati tekst i pritisnuti Enter. Program ¢e ponoviti<br />
utipkano. Program dovr²avamo utipkavanjem praznog retka. Atribut<br />
System.in je vrste InputStream pa ¢e se pozvati metoda readInput( InputStream<br />
) iz ispisa 7.4, koja ¢e onda proslijediti poziv metodi readInput( BufferedReader<br />
) iz ispisa 7.3.
POGLAVLJE 7. KOMUNIKACIJA S OKOLINOM 107<br />
7.4 Rad s datotekama<br />
Datotekama se u Javi barata putem objekata vrste File. To su tzv. handleobjekti<br />
jer postojanje objekta ne zna£i da postoji i datoteka koju on predstavlja.<br />
Objektu se zada putanja do datoteke i zatim ga se moºe pitati postoji<br />
li datoteka, je li u pitanju prava datoteka ili kazalo itd. Ako datoteka<br />
ne postoji, moºe ju se stvoriti. Sljede¢i kod demonstrira rad s datotekama.<br />
I on se oslanja na metode iz ispisa 7.3 i 7.4.<br />
import java.io.IOException;<br />
import java.io.FileOutputStream;<br />
import java.io.FileInputStream;<br />
import java.io.File;<br />
...<br />
public static void main( String[] args )<br />
throws IOException<br />
{<br />
File file = new File( "Text.txt" );<br />
file.createNewFile();<br />
System.out.println( "Zapisujem u datoteku..." );<br />
writeOutput( new FileOutputStream( file ) );<br />
System.out.println( "ƒitam iz datoteke..." );<br />
readInput( new FileInputStream( file ) );<br />
}<br />
Moºemo vidjeti da se datoteku otvara za pisanje instanciranjem klase<br />
FileOutputStream kojoj se proslježuje instanca klase File. ƒim se taj objekt<br />
kreira, datoteka je ve¢ otvorena i spremna za zapisivanje. Analogno se i<br />
£ita iz datoteke.<br />
7.5 Rad s internetskim konekcijama<br />
Za dohvat sadrºaja s nekog URL-a dovoljna su samo dva retka koda (uz<br />
ve¢ postoje¢i kod iz ispisa (7.3 i 7.4):<br />
java.net.URL url = new java.net.URL( "http://www.google.com" );<br />
readInput( url.openStream() );<br />
Klasa URL predstavlja lokaciju na Internetu identiciranu URL-om (uniform<br />
resource locator). Pozivom metode openStream automatski se obavljaju<br />
sve radnje potrebne za pristup resursu pod tim URL-om kontaktira se<br />
DNS, otvara mreºna konekcija na potrebnom portu, ²alje HTTP-ov zahtjev<br />
za dostavom resursa i kreira objekt toka podataka koji ¢e isporu£ivati<br />
podatke koje posluºitelj ²alje kao odgovor.
Poglavlje 8<br />
Uzorci u dizajnu koda<br />
U praksi, pri izradi programskih rje²enja mnoge vrste problema koje treba<br />
rje²avati opetovano se pojavljuju bez obzira na konkretni zadatak. S vremenom<br />
su sazrijela za svaki takav tipi£an problem najbolja rje²enja, koja<br />
pokrivaju sve suptilnije nijanse problema 1 . Razvijatelj si moºe drasti£no<br />
olak²ati ºivot ako prou£i ta rje²enja tako da ih moºe primijeniti kad zatreba.<br />
Takožer, baratanje vokabularom vezanim uz uzorke (prije svega prepoznavanje<br />
uzoraka po imenu) bitno olak²ava komunikaciju izmežu razvijatelja,<br />
koji tako mogu puno ekasnije raspravljati o svojim programskim rje²enjima.<br />
Ideja uzoraka u dizajnu koda doºivjela je najve¢i proboj 1995. godine,<br />
kad je objavljena kultna knjiga Design Patterns [3] £etvorice autora popularno<br />
zvanih The Gang of Four ili GoF, kako se naj£e²¢e oslovljava i<br />
samu knjigu. U ovom poglavlju bit ¢e kao primjer ove koncepcije prikazano<br />
nekoliko najjednostavnijih uzoraka: Template method, Observer, Adapter i<br />
Decorator.<br />
8.1 Template method<br />
Ovaj uzorak jednostavan je za razumjeti i ima vrlo ²iroku primjenu. Ustvari,<br />
primijenili smo ga jo² u prvom poglavlju, prilikom refaktorizacije u<br />
odjeljku 1.11. Drugo ime za Template method je polimorfna metoda. Sli£an<br />
izraz, polimorfni algoritam, spominjao se ve¢ u poglavlju o kolekcijama, u<br />
odjeljku 5.6. Dakle, radi se naprosto o metodi koja u svojem kodu sadrºi<br />
1 U ovoj skripti se, mežutim, ne¢e ulaziti u te nijanse. Ovdje se daje samo najosnovniji<br />
uvod u koncepciju uzoraka.<br />
109
110 POGLAVLJE 8. UZORCI U DIZAJNU KODA<br />
pozive metoda koje podlijeºu polimorzmu odnosno dinami£kom povezivanju.<br />
Metoda na apstraktnoj razini implementira neki algoritam, gdje se<br />
pojedini koraci deniraju samo na razini ²to a ne i kako. U prvom poglavlju<br />
imali smo sljede¢i trivijalan primjer polimorfne metode:<br />
abstract class AbstractCounter<br />
implements Counter<br />
{<br />
...<br />
public final void increment()<br />
{<br />
setState( getState() + 1 );<br />
}<br />
}<br />
abstract void setState( int newState );<br />
Metoda increment je polimorfna jer se oslanja na apstraktnu metodu<br />
setState. Dakle, odrežuje da se mora postaviti novo stanje, ali ne propisuje<br />
to£no kako se postavlja novo stanje. To ¢e ovisiti o konkretnoj implementaciji<br />
metode setState. U na²em primjeru imali smo dvije varijante<br />
te metode: u klasi SimpleCounter jednostavno se prosliježeni int zapisao u<br />
atribut, dok se u klasi ModuloCounter zapisao ostatak dijeljenja s modulom.<br />
8.2 Observer<br />
Recimo da razvijamo softver za simulaciju rada mreºe TCP/IP. Svaki komunikacijski<br />
£vor moºe primiti neki paket namijenjen bilo njemu, bilo nekom<br />
daljnjem £voru i treba ga u skladu s time proslijediti dalje ili konzumirati.<br />
šelimo da na²em objektu koji predstavlja komunikacijski £vor<br />
moºemo dodavati procedure za obradu dogažaja paket stigao u £vor X.<br />
Na primjer, u najjednostavnijem slu£aju trebat ¢e ispisati tekstualnu poruku<br />
na standardni izlaz. U malo naprednijem slu£aju trebat ¢e prikazati<br />
poruku u prozoru gra£kog su£elja. Za o£ekivati je i da ¢emo imati gra£ki<br />
prikaz mreºe na kojem treba vizualizirati putovanje paketa od £vora do<br />
£vora. U tom slu£aju bit ¢e potrebno kad paket stigne u £vor promijeniti<br />
neki detalj u prikazu, npr. boju £vora. Sve te mogu¢nosti ºelimo imati na<br />
raspolaganju kao korisnik klase koja predstavlja mreºu TCP/IP, ²to zna£i<br />
bez ikakvih intervencija u izvorni kod te klase dapa£e, bez posjedovanja
POGLAVLJE 8. UZORCI U DIZAJNU KODA 111<br />
njenog izvornog koda. O£ito je da ¢e klasa £vora morati imati ugraženu<br />
podr²ku za to. Ta podr²ka ugražuje se kori²tenjem uzorka Observer.<br />
Prvo ²to treba uvesti je objekt-oslu²kiva£ (to je observer, ali u kontekstu<br />
Jave uobi£ajen je izraz listener). On ima metodu koju ¢e objekt £vora<br />
pozvati i time signalizirati stigao je paket. Oslu²kiva£a kreiramo i prijavimo<br />
£voru pozivanjem £vorove metode npr. addReceiveListener. Svaki<br />
put kad primi paket, £vor mora pro¢i po listi svih prijavljenih oslu²kiva£a<br />
i pozvati njihovu metodu npr. packetReceived. U toj metodi nalazi se kod<br />
koji obražuje dogažaj primitka paketa.<br />
Za oslu²kiva£a prvo treba denirati su£elje, ReceiveListener. Njega onda<br />
mogu implementirati razli£ite verzije oslu²kiva£a.<br />
Ispis 8.5: Su£elje oslu²kiva£a dolaznih paketa<br />
package hr.fer.tel.jsem.observer;<br />
public interface ReceiveListener<br />
{<br />
void packetReceived( Packet packet, Node receivingNode );<br />
}<br />
Pokaºimo odmah i jednu jednostavnu implementaciju oslu²kiva£a:<br />
Ispis 8.6: Jednostavna implementacija oslu²kiva£a dolaznih paketa<br />
package hr.fer.tel.jsem.observer;<br />
public class SysoutReceiveListenerHr<br />
implements ReceiveListener<br />
{<br />
public void packetReceived( Packet packet, Node receivingNode )<br />
{<br />
System.out.println(<br />
"ƒvor " + receivingNode.getName() + " je primio paket: "<br />
+ packet.getData() );<br />
}<br />
}<br />
Klasa komunikacijskog £vora s podr²kom za oslu²kiva£e:<br />
Ispis 8.7: Komunikacijski £vor s podr²kom za oslu²kiva£e<br />
package hr.fer.tel.jsem.observer;<br />
import java.util.HashSet;<br />
import java.util.Set;
112 POGLAVLJE 8. UZORCI U DIZAJNU KODA<br />
public class Node<br />
{<br />
private final Set recListeners =<br />
new HashSet();<br />
}<br />
private final String name;<br />
public Node( String name )<br />
{<br />
this.name = name;<br />
}<br />
public void addReceiveListener( ReceiveListener listener )<br />
{<br />
this.recListeners.add( listener );<br />
}<br />
public void receivePacket( Packet packet )<br />
{<br />
for ( ReceiveListener listener : this.recListeners )<br />
listener.packetReceived( packet, this );<br />
}<br />
public String getName()<br />
{<br />
return this.name;<br />
}<br />
Napokon, pogledajmo primjer kako se sve ovo koristi. Gore je prikazana<br />
klasa oslu²kiva£a koja ispisuje poruku na hrvatskom; u primjeru koji slijedi<br />
pojavit ¢e se i klasa koja ispisuje poruku na engleskom. Cilj je prikazati<br />
situaciju s vi²e od jednog oslu²kiva£a.<br />
Ispis 8.8: Kori²tenje sustava<br />
public static void main( String[] args )<br />
{<br />
Node node1 = new Node( "Node1" );<br />
node1.addReceiveListener( new SysoutReceiveListenerEn() );<br />
node1.addReceiveListener( new SysoutReceiveListenerHr() );<br />
}<br />
Ispis:<br />
System.out.println( "’aljemo paket £voru..." );<br />
node1.receivePacket( new Packet( "Testing Testing 1-2-3" ) );<br />
’aljemo paket £voru...<br />
Node Node1 received packet: Testing Testing 1-2-3<br />
ƒvor Node1 je primio paket: Testing Testing 1-2-3
POGLAVLJE 8. UZORCI U DIZAJNU KODA 113<br />
8.3 Adapter<br />
S primjerima uzoraka Adapter i Decorator susreli smo se u poglavlju o<br />
komunikaciji s okolinom, to£nije u odjeljku 7.2 o pisa£ima i £ita£ima. Podsjetimo<br />
se situacije koju smo imali. U ispisu 7.4 imamo metodu writeOutput(<br />
OutputStream ). Njen parametar, objekt vrste OutputStream, predstavlja odlazni<br />
tok podataka. To je je tzv. sirovi tok podataka jer se preko njega<br />
²alju £isti okteti, a ne npr. neki apstraktniji podaci kao ²to su znakovi.<br />
Mežutim, nama u implementaciji te metode upravo treba tok podataka u<br />
koji se mogu slati znakovi. To je zato ²to moramo pozvati metodu iz ispisa<br />
7.3, writeOutput( Writer ), koja o£ekuje da ju se pozove s pisa£em, ²to je<br />
Javin izraz za odlazni znakovni tok podataka.<br />
U praksi ¢emo tipi£no imati ve¢ gotove komponente koje rade sa znakovnim<br />
tokovima i ne znaju raditi druga£ije. Dakle, zamislimo da je metoda<br />
writeOutput( Writer ) ugražena u neku klasu za koju nemamo izvorni<br />
kod jer je ona dio biblioteke klasa koju samo koristimo, a ne razvijamo.<br />
Dovedeni smo u situaciju koja se £esto u praksi, u analogiji s elektrotehnikom,<br />
naziva impedance mismatch (neprilagoženost impedancija). S jedne<br />
strane imamo metodu koja o£ekuje znakovni tok, a s druge strane imamo<br />
sirovi tok u koji ta metoda treba slati podatke. Problem ¢emo rije²iti<br />
primjenom uzorka Adapter on ¢e nam adaptirati, tj. prilagoditi su£elje<br />
objekta koji imamo (sirovi tok) na su£elje objekta koji o£ekuje metoda<br />
iz biblioteke (znakovni tok). Moramo napraviti klasu adaptera sa sirovog<br />
na znakovni tok. U primjeru s ispisa 7.4 koristi se Javin adapter, klasa<br />
OutputStreamWriter. Pogledajmo kako bi ona mogla biti realizirana:<br />
Ispis 8.9: Pokazna izvedba adaptera na znakovni tok<br />
package hr.fer.tel.jsem;<br />
import java.io.IOException;<br />
import java.io.OutputStream;<br />
import java.io.Writer;<br />
public class OutputStreamWriter<br />
extends Writer<br />
{<br />
private OutputStream os;<br />
public OutputStreamWriter( OutputStream os )<br />
{<br />
this.os = os;
114 POGLAVLJE 8. UZORCI U DIZAJNU KODA<br />
}<br />
}<br />
@Override<br />
public void write( char[] cbuf, int off, int len )<br />
throws IOException<br />
{<br />
for ( int i = off; i < off+len; i++ )<br />
{<br />
char c = cbuf[i];<br />
if ( c < 0x80 )<br />
os.write( c );<br />
else<br />
os.write( '#' );<br />
}<br />
}<br />
@Override<br />
public void flush()<br />
throws IOException<br />
{<br />
os.flush();<br />
}<br />
@Override<br />
public void close()<br />
throws IOException<br />
{<br />
os.close();<br />
}<br />
Ova jednostavna izvedba u sirovi tok propu²ta samo znakove iz podskupa<br />
Unicode-a zvanog Basic Latin, koji odgovara ASCII-ju, a umjesto<br />
svih ostalih znakova ²alje znak #. Primijetimo da su nadja£ane samo tri<br />
metode, dok klasa Writer posjeduje znatno ve¢i broj njih. To je zahvaljuju¢i<br />
tome ²to su sve ostale metode u toj klasi paºljivo izvedene tako da se<br />
oslanjaju na osnovnu funkcionalnost koju pruºaju samo ove tri. Na² adapter<br />
moºemo isprobati na primjerima iz 7. poglavlja tako da u na² paket dodamo<br />
klasu adaptera, a izbacimo izjavu import java.io.OutputStreamWriter.<br />
Alternativno, moºemo koristiti ovakvu metodu main, koju moºemo ubaciti<br />
u klasu OutputStreamWriter:<br />
public static void main( String[] args )<br />
throws IOException<br />
{<br />
Writer osw = new OutputStreamWriter( System.out );<br />
osw.write( "Probni ispis\n" );<br />
osw.close();<br />
}
POGLAVLJE 8. UZORCI U DIZAJNU KODA 115<br />
U drugom retku poziva se metoda write( String ), koju nismo eksplicitno<br />
nadja£ali, ali koja se oslanja na na²u metodu write( char[], int, int<br />
) 2 .<br />
Uzorak Adapter moºemo dosta pregledno vizualizirati UML-om. Na²a<br />
situacija prikazana je na slici 8.1.<br />
Client<br />
+writeOutput()<br />
Writer<br />
+write()<br />
+flush()<br />
+close()<br />
Client očekuje ovakav objekt<br />
Adapter<br />
Ovakav objekt<br />
imamo<br />
OutputStreamWriter<br />
OutputStream<br />
+write()<br />
+flush()<br />
+close()<br />
+write()<br />
+flush()<br />
+close()<br />
Slika 8.1: UML-ov dijagram uzorka Adapter<br />
8.4 Decorator<br />
Osim ²to £esto imamo situaciju koju rije²avamo Adapter-om, dakle nesuglasje<br />
izmežu su£elja koje nam treba i su£elja koje imamo na raspolaganju,<br />
£esto se pojavljuje i sli£na situacija u kojoj imamo objekt £ije su£elje odgovara,<br />
ali mu nedostaje jo² neka dodatna funkcionalnost. Dakle, nisu nam<br />
potrebne druga£ije metode (kao u slu£aju uzorka Adapter), ve¢ samo izmijenjeno<br />
pona²anje postoje¢ih metoda. Za takve situacije primjenjujemo<br />
uzorak Decorator. Iz re£enog ve¢ moºemo naslutiti da je ovaj uzorak dosta<br />
blizak uzorku Adapter dapa£e, njegova shema prikazana UML-om je identi£na.<br />
U poglavlju o komunikaciji imamo situaciju na kojoj moºemo prikazati<br />
ovaj uzorak. Tamo se pojavljuje klasa Reader koja predstavlja neki dolazni<br />
znakovni tok. Problem koji sad ºelimo rije²iti je sljede¢i: ve¢ raspolaºemo<br />
2 Metoda write( String ) je stoga jo² jedan primjer primjene uzorka Template method.
116 POGLAVLJE 8. UZORCI U DIZAJNU KODA<br />
nekim objektom £ita£a, a ºelimo mu dodati funkciju korisnu prilikom tra-<br />
ºenja pogre²aka u programu da sve ²to pro£ita iz dolazne struje ispi²e i na<br />
zaslon radi kontrole. Naglasimo: ne kaºe se da raspolaºemo klasom £ita£a,<br />
£iji kod bismo mogli izmijeniti izravno, nego njenom instancom, dok samim<br />
izvornim kodom klase ne rapolaºemo.<br />
Kako ¢emo rije²iti ovaj problem? Denirat ¢emo novu klasu izvedenu<br />
iz klase Reader. Klasa, kao i kod Adapter-a, ima karakter omota£a. U ovom<br />
konkretnom slu£aju instanca na²e nove klase omota£ je za neki objekt vrste<br />
Reader. Dakle, u uzorku Decorator (hrv. ukra²iva£) klasa koju ukra²avamo<br />
pojavljuje se u dva konteksta odjednom: i kao vrsta ugraženog objekta<br />
(koji biva ukra²en), i kao natklasa ukra²iva£a. Najbolje je da prou£imo<br />
implementaciju na²eg ukra²iva£a:<br />
package hr.fer.tel.jsem;<br />
import java.io.IOException;<br />
import java.io.Reader;<br />
public class DebugReader<br />
extends Reader<br />
{<br />
private Reader rdr;<br />
Ispis 8.10: Primjer primjene uzorka Decorator<br />
public DebugReader( Reader rdr )<br />
{<br />
this.rdr = rdr;<br />
}<br />
@Override<br />
public int read( char[] cbuf, int off, int len )<br />
throws IOException<br />
{<br />
// prvo prosliježujemo poziv na omotani objekt:<br />
int charsRead = this.rdr.read( cbuf, off, len );<br />
// zatim obavljamo dodatne radnje:<br />
if ( charsRead != -1 )<br />
System.out.println( "Reading " +<br />
String.valueOf( cbuf, off, charsRead ) );<br />
return charsRead;<br />
}<br />
@Override<br />
public void close()<br />
throws IOException<br />
{<br />
// samo delegiramo na omotani objekt:
POGLAVLJE 8. UZORCI U DIZAJNU KODA 117<br />
}<br />
}<br />
this.rdr.close();<br />
Odmah moºemo uo£iti veliku sli£nost s primjerom za uzorak Adapter.<br />
Kod Adapter-a u igri su dvije razli£ite klase jer jednu prilagožavamo drugoj,<br />
a ovdje samo dodajemo funkcionalnost jednoj klasi, koja se stoga pojavljuje<br />
u dvostrukoj ulozi.<br />
Na²u klasu DebugReader moºemo isprobati na primjerima iz 5. poglavlja,<br />
tako da prepravimo ovu metodu iz ispisa 7.4:<br />
static void readInput( InputStream is )<br />
throws IOException<br />
{<br />
readInput( new InputStreamReader( is ) );<br />
}<br />
Treba samo ubaciti jo² jedan omota£, na² ukra²iva£:<br />
static void readInput( InputStream is )<br />
throws IOException<br />
{<br />
readInput( new DebugReader( new InputStreamReader( is ) ) );<br />
}<br />
Nakon toga moºemo isprobati primjere iz odjeljaka 7.37.5.<br />
Uzorak Decorator, kao ²to je najavljeno, u UML-ovom dijagramu izgleda<br />
vrlo sli£no kao Adapter. To se o£ituje na slici 8.2, na kojoj je jasno istaknuta<br />
dvojaka uloga ukra²avane klase 3 .<br />
Kao primjer kori²tenja uzorka Decorator u Javinoj biblioteci moºemo<br />
izdvojiti klase koje se pojavljuju u poglavlju o komunikaciji s okolinom:<br />
PrintWriter i BufferedReader. Te klase su i posebno zanimljive jer one istovremeno<br />
implementiraju i Adapter i Decorator: npr. klasa BufferedReader,<br />
osim ²to dodaje funkcionalnost mežuspremnika svim postoje¢im metodama<br />
(ukra²avanje), ima i dodatnu, vrlo zna£ajnu metodu readLine, koje nema<br />
u obi£nom Reader-u. Iz perspektive nekog koda koji o£ekuje da ¢e £ita£i<br />
imati upravo tu metodu, ona obavlja prilagodbu s obi£nog £ita£a na £ita£<br />
s metodom readLine.<br />
3 Dijagram sa slike 8.2 nije sasvim korektan jer se ista klasa ne bi smjela crtati dvaput;<br />
mežutim, ispravan crteº ne bi bio tako jasan.
118 POGLAVLJE 8. UZORCI U DIZAJNU KODA<br />
Client očekuje ovakav objekt<br />
Client<br />
+readInput()<br />
Reader<br />
+read()<br />
+close()<br />
Decorator<br />
Ovakav objekt<br />
imamo<br />
DebugReader<br />
Reader<br />
+read()<br />
+close()<br />
+read()<br />
+close()<br />
Slika 8.2: UML-ov dijagram uzorka Decorator
Poglavlje 9<br />
Savjeti za rad u Eclipse-u<br />
9.1 Automatizacija rada na kodu<br />
Razvojno okruºje Eclipse ima dosta bogatu zbirku alata koji automatiziraju<br />
mnoge aktivnosti na razvoju koda. Njihovim kori²tenjem kod se kreira<br />
brºe (puno manje tipkanja) i pouzdanije (Eclipse ne radi tipfelere). Preporu£a<br />
se detaljno prou£avanje menija Edit, Source i Refactor. Ovdje ¢e se<br />
spomenuti nekoliko najvaºnijih alata.<br />
Quick x aktivira se kombinacijom tipaka Ctrl-1 ili iz menija Edit. Pojavit<br />
¢e se mali prozor£i¢ u blizini kursora i ponudit ¢e razli£ite mogu¢nosti<br />
prepravljanja koda, i dodatno, ako se kursor nalazi na mjestu gdje je otkrivena<br />
pogre²ka, standardne na£ine njenog otklanjanja. Treba imati na umu<br />
ovo: Quick x ne zna bolje od vas! Student mora ve¢ znati kako treba<br />
ukloniti pogre²ku, Quick x je tu samo da taj postupak automatizira. Ako<br />
nasumice slu²ate savjete Quick x-a, najvjerojatnije je da ¢ete napraviti<br />
kaos u svom kodu.<br />
Rename in le je jedna od opcija koje nudi Quick x kad smo s kursorom<br />
unutar nekog identikatora. Opcija je dostupna i izravno pomo¢u Ctrl-<br />
2, R. Opciju koristimo uvijek kad ºelimo promijeniti ime nekoj lokalnoj<br />
varijabli, privatnom atributu/metodi i op¢enito bilo kojem identikatoru<br />
koji se koristi samo u trenutnoj datoteci.<br />
Content assist aktivira se pomo¢u Ctrl-Space ili iz menija Edit. Koristi<br />
se npr. na mjestu gdje ºelimo otipkati ime metode koju pozivamo. Ponudit<br />
¢e se sve metode denirane za objekt lijevo od to£ke. Npr. imamo varijablu<br />
String s i zapo£injemo novi redak koda tipkaju¢i<br />
s.<br />
119
120 POGLAVLJE 9. SAVJETI ZA RAD U ECLIPSE-U<br />
U ovom trenutku koristimo Ctrl-Space i uz kursor pojavljuje se popis svih<br />
metoda deniranih za String. Odaberemo pravu i njeno ime se ubacuje<br />
u kod. Ako je Eclipse odgovaraju¢e pode²en, izbornik Content Assist-a<br />
pojavit ¢e se i sam od sebe ako otipkamo to£ku i pri£ekomo koju sekundu<br />
(ovisno o brzini ra£unala).<br />
Content assist koristi se i za ekspanziju kodnih predloºaka: npr. mo-<br />
ºemo natipkati eq, pritisnuti Ctrl-space i odabrati equals. Time se ubacuje<br />
deklaracija metode equals. Isto radi i za toString, hashCode, main. Posebno<br />
koristan je predloºak sysout koji ubacuje £esto kori²tenu a duga£ku<br />
konstrukciju System.out.println(); Takožer su vrlo korisni predlo²ci for <br />
standardne petlje za prolazak po elementima polja ili kolekcije. Popis svih<br />
predloºaka moºe se pregledati i editirati pod Window, Preferences, Java,<br />
Editor, Templates.<br />
Organize imports aktivira se pomo¢u Ctrl-Shift-O ili iz menija Source.<br />
Kad god se pojavi pogre²ka da je neka vrsta podatka nepoznata, to je najvjerojatnije<br />
stoga ²to doti£na vrsta nije uvezena. Ovom naredbom automatski<br />
se pronalaze i uvoze sve potrebne vrste. Ako postoji vi²e vrsta istog<br />
imena, pojavit ¢e se prozor u kojemu treba odabrati pravu. Ova komanda<br />
takožer i uklanja nepotrebne uvoze, koji se trenutno ne koriste u kodu.<br />
Pomo¢u Rename moºemo promijeniti ime klasi, paketu, javnoj metodi i<br />
op¢enito bilo kojem identikatoru koji se koristi u vi²e datoteka. Dostupno<br />
iz menija Refactor ili pomo¢u Alt-Shift-R.<br />
Pomo¢u Move moºemo klasu preseliti u drugi paket i metodu/atribut u<br />
drugu klasu. Eclipse ¢e se pobrinuti da sva pozivanja na ono ²to se seli budu<br />
izmijenjena na odgovaraju¢i na£in, odnosno upozorit ¢e nas ako postoji<br />
prepreka preseljenju. Dostupno iz menija Refactor ili pomo¢u Alt-Shift-V.<br />
9.2 Povijest datoteke<br />
Ako shvatimo da smo krenuli s razvojem klase u pogre²nom smjeru, moºemo<br />
se vratiti na ranije stanje datoteke. U Package Explorer-u kliknemo desnim<br />
gumbom na na²u datoteku i odaberemo Replace with, Local history. Pojavit<br />
¢e se popis svih ranijih verzija datoteke koje su zapam¢ene.
POGLAVLJE 9. SAVJETI ZA RAD U ECLIPSE-U 121<br />
9.3 Seljenje projekta s ra£unala na ra£unalo<br />
9.3.1 Tipi£an problem prilikom selidbe<br />
Studenti se redovito susre¢u s potrebom da svoj projekt razvijaju na vi²e<br />
ra£unala u labosu, na svom laptopu, na ku¢nom ra£unalu itd. Projekt je<br />
prili£no jednostavno preseliti, ali jo² je jednostavnije to izvesti pogre²no,<br />
pogotovo stoga ²to je na raspolaganju £itav niz postupaka. Problemi nastaju<br />
zbog toga ²to postupak izvoza projekta nije usklažen s postupkom<br />
uvoza. Posebice, uo£ena je sljede¢a tipi£na pogre²ka:<br />
1. Projekt se ispravno izveze tako da glavno kazalo projekta sadrºi kazalo<br />
src koje je korijensko za datoteke s izvornim kodom. Ono ¢e<br />
tipi£no biti bez datoteka, samo s potkazalom hr.<br />
2. Na odredi²nom ra£unalu projekt se uveze tako da se kazalo src ne progla²ava<br />
korijenskim, ve¢ glavno kazalo projekta. Tako ispada da je src<br />
ustvari prvi segment imena paketa. Eclipse se sada ºali da deklaracija<br />
paketa u datoteci (npr. hr.fer.tel ²to je ispravno) ne odgovara<br />
kazalu u kojem se nalazi (src/hr/fer/tel ²to je neispravno!).<br />
3. Student odlazi na Quick x koji mu nudi izmjenu imena paketa u<br />
src.hr.fer.tel i time malu pogre²ku pretvara u puno ve¢u. To ponavlja<br />
za svaku datoteku.<br />
Ono ²to je zaista trebalo napraviti je popraviti korijenski direktorij izvornog<br />
koda. To se obavlja u Project, Properties, Java Build Path, Source.<br />
Tamo se nalazi okvir naslovljen Source folders on build path i unutra bi<br />
trebao biti navedeno kazalo src. Ako nije, dodajemo ga pomo¢u tipke Add<br />
folder...<br />
9.3.2 Savjeti za uspje²nu selidbu<br />
Sve datoteke projekta nalaze se na jednom mjestu. Potrebno je samo znati<br />
koje kazalo Eclipse koristi kao svoj Workspace. To se moºe doznati putem<br />
File, Switch Workspace. U tom kazalu za svaki projekt postoji po jedno<br />
potkazalo istog imena kao projekt. Op¢enito je dovoljno kazalo projekta<br />
preseliti na drugo ra£unalo, ponovo u kazalo tamo²njeg Workspace-a, pokrenuti<br />
Eclipse i pomo¢u File, New, Project, Java Project zatraºiti stvaranje
122 POGLAVLJE 9. SAVJETI ZA RAD U ECLIPSE-U<br />
projekta istog imena. Eclipse ¢e vas obavijestiti (u dnu dijalo²kog prozora)<br />
da kazalo tog imena ve¢ postoji i da ¢e sve postavke projekta biti preuzete.<br />
Eclipse nudi i izravnu podr²ku za selidbu projekta, kori²tenjem naredbi<br />
Export/Import. Na izvori²nom ra£unalu radimo ovo:<br />
1. U Package Explorer-u koristimo desni klik nad na²im projektom, zatim<br />
Export, Archive le<br />
2. Kad pritisnemo Next, nudi nam se mogu¢nost detaljnog odabira datoteka<br />
za izvoz. Datoteke .classpath i .project obvezno zadrºimo (tu<br />
su postavke projekta). Ako je bitno da arhiva bude ²to manja (mejl<br />
i sl.), moºemo izbaciti sva potkazala osim src.<br />
3. Niºe u dijalo²kom prozoru pomo¢u Browse odaberemo to£nu lokaciju<br />
i ime arhive koja ¢e se stvoriti. Provjerimo da su odabrane opcije Save<br />
in zip format i Create directory structure for les.<br />
4. Pritiskom na Finish izdajemo naredbu za stvaranje arhive.<br />
Stvorenu arhivu dopremamo do odredi²nog ra£unala na kojem pokre¢emo<br />
Eclipse i zatim:<br />
1. File, Import, Existing projects into Workspace. U dijalo²kom prozoru<br />
odabiremo opciju Select archive le i pomo¢u Browse nalazimo svoju<br />
arhivu.<br />
2. U dijalo²kom prozoru pojavljuje se popis svih projekata naženih u<br />
arhivi (trebo bi biti samo jedan). Provjerimo da je ime projekta ispravno<br />
i ozna£eno kva£icom.<br />
3. Pritiskom na Finish izdajemo naredbu za uvoz projekta.
Literatura<br />
[1] Java 2 Platform Standard Edition 5.0 API Specication.<br />
http://java.sun.com/j2se/1.5.0/docs/api.<br />
[2] Mary Campione, Kathy Walrath, and Alison Huml. The Java<br />
Tutorial. http://java.sun.com/docs/books/tutorial.<br />
[3] Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides.<br />
Design Patterns. Addison-Wesley Professional, 1995.<br />
[4] James Gosling, Bill Joy, Guy Steele, and Gilad Bracha. The Java<br />
Language Specication.<br />
http://java.sun.com/docs/books/jls/third_edition/html/j3TOC.html,<br />
2005.<br />
[5] James Gosling and Henry McGilton. The Java Language<br />
Environment, chapter 6: Security in Java.<br />
http://java.sun.com/docs/white/langenv/Security.doc.html.<br />
123