Sie sind auf Seite 1von 1217

Misliti

na

Javi
Prevod etvrtog izdanja

Bruce Eckel
MindVievv, Inc., p re d s e d n ik Preveo Miljenko uur

P R E N T IC E H ALL

M isliti na Javi, prevod etv rto g izdanja


G lavn i u re d n ik R e d a k to ri T ehniki u re d n ik L e k to r i k o re k to r R ealizacija k o ric a P relom tek sta i o b r a d a slika O lg a M ila n k o Stela Spasi S n ean a B iseni S an ja Tasi Vesna u k i N ataa Pavlov San ja Tasi M iiica D ean sk i N ataa Pavlov Izdava D ire k to r ta m p a M ik ro k n jig a , B eo g rad D ra g a n T anaskoski P u b lik u m , B eograd

A ko im a te p ira n ja ili k o m e n ta re , ili a ko elite d a d o b ije te b e s p la ta n k atalo g , p iite n a m ili se javite: M ik ro k n jig a P. fah 20 -87 11030 B eog rad tel: 01 1/3 5 4 0 -5 4 4 pi sma@mi k ro k n j i g a . r s A u to riz o v a n p re v o d sa e n g le sk o g jezika k n jig e T h in k in g in Java, F o u rth l'd itio n . llo p v rig h t 2 007 M ik ro k n jig a . Sva p rav a z a d r a n a . N ije d o z v o lje n o d a ije d a n d eo ove knjige b u d e re p ro d u k o v a n ili e m ito v a n n a bilo koji n a in , e le k tro n sk i ili m e h a n i k i, u k lju u iu i fo to k o p ira n je , s n im a n je ili b ilo koji d ru g i sistem za beleen je, b ez p r e th o d n e p ism e n e d o z v o le izdavaa. T ra n sla tio n c o p v rig h t H a ll, In c, a C o m p a n y . 2007 by M ik ro k n jig a , T h in k in g in Java, F o u r th E d itio n b y B ru ce Eckel, C o p y rig h t

< 0

2006,

All R ights Reserved. P u b lish e d by a rra n g e m e n t vvith th e o rig in a l p u b lish e r, P earso n F d u c a tio n , Inc., p u b lish in g as P re n tic e

H a p o n n a r*n6.nii(vrcKa C p n iijc . lic o i pa;i

004.438JAVA 001.42:00-4.738.5

EK EJl. Bpvc
M isliti n a Javi / B ru ee Eckel ; p rev o d 4. izd. M iljenko ueur. - B eograd : Mikro knjiga, 2007 (B e o g ra d : P ub lik u m ) XIII, 1201 s tr . graf. prikazi : 24 cn i P re v o d d ela: T hink in g in Java. - T ira 500. - Bibliografija: 1170-1171. R egistar. ISBN 978-86-7555-308-3
a ) PlporpaMCKH jc u ik "Java" b) H iircpiicT - flporpaMnpaibc

COBISS.SK - ID 141670412

MNI4/289/290069M 19Q8P101S16 1K76 5 4

Kratak sadraj
Predgovor U vod 1: U poznavanje sa objektim a 2: Sve je objekat 3: O p erato ri 4: K ontrolisanje izvravanja 5: Inicijalizacija i ienje 6: K ontrola p ristupa 7: Ponovno korienje klasa 8: Polim orfizam 9: Interfejsi 10: U nutranje klase 11: uvanje objekata 12: O b rad a greaka pom ou izuzetaka 13: Znakovni nizovi 14: Podaci o tip u 15: G eneriki tipovi 16: Nizovi 17: D etaljno razm atranje kontejnera 18: Javin ulazno-izlazni sistem 19: N abrojani tipovi 20: A notacije 21: Paralelno izvravanje 22: Grafika korisnika okruenja A: D odaci B: Resursi Spisak term in a korienih u knjizi Indeks 1 8 15 42 65 99 115 159 180 211 239 266 302 345 392 432 484 593 628 719 805 845 885 1045 1166 1170 1175 1177

Sadraj
Predgovor 1
Paralelni r a d ...................................34
Ja v a i I n t e r n e t ....................................... 34 Sta je W eb?............................................ 34 Programiianje s klijentske stra n e .. . . 36 Programiranje sa serverske strane . . . 40 S a e ta k .......................................................41

Java SE5 i S E 6 ...................................2 Java SE6..........................................2 etvrto izdanje.................................3


I z m e n e ..........................................................3

N apom ena o dizajnu k o r ic a .........4 Z ahvalnice......................................... 5 U vod 8

2: Sve je objekat
R a d sa o b je k tim a

42

p r e k o r e f e r e n c i .................................... 42

P reduslovi......................................... 8 Uenje Ja v e .......................................9 C ilje v i................................................9 Pouavanje na osnovu ove k n jig e .......................................10 D okum entacija na W ebu............. 11 V ebe................................................11 Javini tem elji.................................. 11 lzvorni k o d .....................................12
Nain pisanja korisen u knjizi............13

M o r a te d a n a p r a v ite sve o b je k te . 43
G de se nalazi skladite............................43 Specijalan sluaj: p rosti tip o v i............44 Nizovi u Javi.............................................. 46

N ik a d a n e m o r a te d a u n i t i t e o b j e k a t ...................................................... 46 Oblast vaenja......................................46 Oblast vaenja o b je k a ta ..................... 47 P ra v lje n je n o v ih tip o v a p o d a t a k a : k l a s a .................................... 48 Polja i m e to d e ..................... ................ 48 M e to d e , a r g u m e n t i i p o v r a t n e v r e d n o s t i ................................................. 50
Lista a rg u m e n a ta .....................................51 P r a v l j e n j e p r o g r a m a n a J a v i . . . .5 2 Vidljivost i m e n a .....................................52 Korienje d rugih k o m p o n e n a ta . . . . 52 Rezervisana re s t a t i c ............................53 V a p r v i p r o g r a m n a J a v i ................. 5 4 Prevodenje i iz v rav a n je ....................... 56 K o m e n ta ri i u g ra e n a d o k u m e n t a c i j a ........................................ 5 7 D okum entacioni k o m e n ta ri................ 58 S in ta k s a .....................................................58 U gradeni H T M L .....................................59 P rim eri o z n a k a ....................................... 60 P rim er d o k u m e n ta c ije ......................... 62 S til p r o g r a m i r a n j a ............................... 6 3 S a e t a k ........................................................... 6 3 V e b e .............................................................. 6 3

G re k e ............................................. 14 1: U p o z n a v a n j e s a o b j e k t i m a 15

Razvoj apstrakcije..........................15 O bjekat im a in te rfe js................... 17 O bjekat prua u slu g e................... 19 Skrivena realizacija........................20 Ponovno korienje realizacije.. .21 N asleivanje...................................22 Relaeije !! i IE-KAO.......................25 Virtuelizacija objekata preko p o lim o rfiz m a................................ 26 H ijerarhija s jedinstvenim k o r e n o m ......................................... 29 K ontejneri.......................................29 Paramctri/uvani (generieki) tipuvi .. 30 O brada izuzetaka: postupanje s grekam a.......................................33

vi

Misliti na Javi

3: O p e r a t o r i

65

N a r e d b a s w i t c h ................................ 112 S a e ta k .................................................... 114

Jednostavnije naredbe za ispisivanje.................................. 65 Korienje operatora u Jav i.........66 P rio rite ti.........................................66 Dodela v re d n o s ti..........................67 Pojava pseudonima pri pozivanju metode.....................68 Matematiki o p erato ri................. 69 Unarni operatori minus i plus.........71 Automatsko uveanje i um anjenje.....................................72 O peratori poreenja......................73 Ispitivanje jednakosti objekata.........73 Logiki o p e r a to ri..........................75
N ep o tp u n o iz ra u n a v a n je ...................76

5: I n i c i j a l i z a c i j a

i i e n je

115

G a r a n t o v a n a in ic ija liz a c ija p o m o u k o n s t r u k t o r a .................. 115 P r e k l a p a n je m e t o d a ........................ 117


Razlikovanje preklopljenih m etoda ..1 1 9 Preklapanje i prosti t i p o v i ................ 120 Preklapanje p o v ra tn ih v re d n o s ti. . . 123

Io d r a z u m e v a n i k o n s t r u k t o r i .

.124

R e z e r v is a n a r e t h i s ........................125
Pozivanje k o n stru k to ra iz k o n s tr u k to r a ..................................... 127 Z naenje rezervisane rei s t a t i c . . . . 129

i e n je : f in a liz a c ija i s a k u p l ja n je s m e a .......................... 129


em u slui m etoda fin a liz e ()? ......... 130 M o rate da istite s a m i......................... 131 Stanje o k o n a n ja ................................... 131 Kako radi sakuplja s m e a ? .............. 133 I n i c i j a l i z a c i j a l a n i c a ....................... 136 Z adavanje in ic ija liz a cije ..................... 137 I n i c i j a l i z a c i j a k o n s t r u k t o r i m a . .1 3 9 Redosled in ic ija liz a cije ....................... 139 Inicijaiizacija statinih elem enata .. 140 I:ksplicitna inicijalizacija statinih e le m e n a ta ................................................. 143 Inicijaliz.acija nestatin ih instanci .. 144

Literali............................................. 77
E ksponencijalni z a p i s ............................78

O peratori nad bitovim a............... 79 O peratori p om eranja................... 80 Ternarni operator uslovljavanja. .84 O peratori + i += za znakovne n iz o v e ............................................. 85 este greke prilikom korienja operatora......................86 Eksplicitna konverzija tipova . . .87 Odsecanje i zaokruivanje..............88 Unapredenje tipova........................ 89 Java nema operator za odreivanje veliine................. 89 Saet pregle o p e ra to ra ............... 89 Saetak............................................. 98 4: K o n tr o lis a n je i z v r a v a n j a 99

I n ic ija liz a c ija n i z o v a ........................ 145 E.iste promenljivih param etara........ 150 N a b r o ja n i t i p o v i ............................... 155 S a .e ta k .................................................... 158

6:

K o n tro la p r is tu p a

159

Logike v red n o sti.......................... 99 Naredba if-e ise .............................. 99 P e tlje ............................................. 100
Petlja do-vvhile....................................... 101 Petlja f o r ...................................................101 O p e ra to r z a r e z ....................................... 103

P a k e t: b i b l i o t e k a j e d i n i c a .......... 160
O rg anizacija k o d a ................................. 10 1 PravI jenje jedinst ven i h im en a p a k e ta .......................................... 1^2 Lina bihlioteka sa a la tk a m a ............ 166 K orienje tivoenja za p ro m e n u po n aan ja p ro g r a m a ............................ 16S U pozorenje pri rad u s pakelim a . . . 168

Foreach sin tak sa.......................... 103 Rezervisana re r e tu r n ............... 106 Rezervisane rei break i c o n tin u e .....................................107 Cuveni s;oto...................................108

S p e c if ik a to ri p r i s t u p a u Javi . .

.168

Paketni p r i s t u p ..................................... 169 p u b lic: intertejs za p r i s t u p ................ 169 p riv a te : to ne sm e da se d ir a ! ............ 171 p ro te c te d . pristup nasledi\'anjem . . 172

Sadraj

v ii

Interfejs i realizacija....................174 Pristup k la s a m a ..........................175 Saetak........................................... 178 7: P o n o v n o k o r i e n j e k la s a 1 80

Projektovanje pom ou n asle iv an ja ................................ 2 3 3


P oredenje zam ene i proirivanja . . . 235 Svoenje navie i podaci o tip u p rilikom iz v ra v a n ja ............................236

Saetak........................................... 2 3 8

S i n t a k s a k o m p o z i j e .......................1 8 0 S i n t a k s a n a s l e i v a n j a ....................... 1 8 3 Inicijalizacija osn ov ne k la s e ..............185 D e l e g i r a n j e ..........................< .................. 1 8 8 K o m b in o v a n je k o m p o z ic ije i n a s l e i v a n j a ........................................1 8 9 G aran to v an je prav ilno g ienja . . . 191 Sakrivanje im e n a .................................. 194 I z b o r iz m e u k o m p o z ic ije i n a s l e i v a n j a ........................................1 9 6 R e z e r v i s a n a r e p r o t e c t e d ............1 9 7 S v o e n j e n a v i e ..................................... 1 9 8 Z ato svoenje navie ? .....................199 Ponovo o hiran ju izm eu kom pozicije i n a sle iv a n ja................ 200 R e z e r v i s a n a r e f i n a l .......................2 0 0 Finalni p o d a c i .......................................200 Finalne m e t o d e .....................................204 Finalne klase............................................206 Faljivo sa f i n a l .....................................207

9: Interfejsi

239

Apstraktne klase i m e to d e .........239 In te rfe jsi....................................... 242 Potpuno razd v ajan je................. 246 Viestruko nasleivanje u Javi. .251 Proirivanje interfejsa nasleivanjem .............................. 253 Sukobljavanje imena prilikom kombinovanja interfejsa................255 Prilagoavanje interfejsu...........256 Polja u in te rfe jsim a ....................258
Inicijalizacija polja u interfejsim a . . 259

Ugneivanje in terfejsa............. 260 Interfejsi i Proizvoai............... 262 Saetak........................................... 265

10: Unutranje klase

266

Inicijalizaja i uitavanje klasa................................................208


Inicijalizacija i n a sle d iv a n je ..............208

Saetak........................................... 210 8: P o l i m o r f i z a m 211

Ponovo o svoenju navie.........211


Z an em ariv an je tipa o b jc k ta ..............213

Pravljenje unutranjih klasa .. .266 Veza ka spoljnoj k lasi................. 268 U potreba sintaksi .this i .new . .270 Unutranje klase i svoenje navie..............................................271 Unutranje klase u m etodam a i oblastim a vaenja......................273 A nonim ne unutranje klase . . .275
Ponovo o p ro izvodnim m e to d a m a . 279

Z ako ljica.....................................214
Vezivanje poziva m e to d a .....................214 D obijanje p ravilnog ponaanja . . . . 215 P r o ir iv o s l..............................................218 ( 'ireka: ,,redel l n isan je priv atn ih m e to d a .................................. 221 Cireka: polja i statine m e to d a ......... 222

Ugnedene klase.......................... 2 8 2
Klase u n u ta r in te rf e js a ....................... 283 P ristu p an je spoljanjosti iz viestruko ugneenih k la s a .................................. 284

Zato unutranje k lase?............. 285


Zakljuci i p o v ra tn i p o z iv i................ 287 U nu tran je klase i kosturi u p ra v lja n ja .............................................. 290

K onstruktori i p olim orfizam .. .224


Redosled po/.iva k o n s tr u k to r a ......... 224 N asledivanje i ienje......................... 226 Ponaanje po lim o rfn ih m etoda u n u ta r k o n s tr u k to r a ............................230 K o v a r i j a n t n i p o v r a t n i t i p o v i . . .2 3 2

Nasleivanje unutranjih klasa................................................296 Da li unutranja klasa moe da se redefinie?.......................... 297

v iii

Misliti na Javi

Lokalne unutranje klase...........299 Identifikatori unutranjih klasa.300 Saetak...........................................301


11: u v a n je o b je k a ta 302

i e n j e o d r e d b o m f i n a l l y . . . .3 6 7 em u slui o d red b a finally?..............368 U potreba bloka finally tokom p o v ratk a iz m e to d e p o m o u rezervisane rei r e t u r n ....................... 371 M ana: izgubljeni iz u z e ta k ...................372 O g r a n i e n j a i z u z e t a k a .................... 3 7 4 K o n s t r u k t o r i ........................................... 3 7 7 P r o n a l a e n j e s l i n i h i z u z e t a k a . .3 8 2 D r u g i p r i s t u p i ........................................ 3 8 3 I s t o r i j a ..................................................... 384 Perspektive.............................................. 386 Prosleivanje izuzetaka na k o n z o lu . .387 P retvaranje pro v eren ih izuzetaka u n e p ro v e re n e ....................................... 388 U p u t s t v a z a i z u z e t k e ..........................3 9 0 S a e t a k ......................................................... 3 9 1

Generike klase i kontejneri za bezbedan rad s tipovim a. .. .303 Osnovni p o jm o v i........................306 Dodavanje grupa elemenata .. .307 Ispisivanje sadraja kontejnera. .309 L iste............................................... 311 I t e r a t o r i .......................................315 Listlterator.................................318 Ulanana lis ta .............................. 319 Pravljenje steka od ulanane liste................................................. 320 Funkcionalnost s k u p a ............... 322 Funkcionalnost m ape................. 326 Pravljenje reda za ekanje (od ulanane lis te ) ......................329
Prioritetni red ekanja (P rio rity Q u e u e )............................... 330

13: Znakovni nizovi

392

Poreenje kolekcija i Iteratora .332 Foreach i iteratori........................336 Adapterska metoda.......................338 Saetak...........................................341


12: O b r a d a g re a k a p o m o u iz u z e ta k a 345

Nepromenljivi znakovni nizovi .392 Poreenje preklapanja operatora + i StringB uildera . .393 N enam erna rekurzija................. 397 Operacije sa znakovnim n izov im a....................................... 398 Formatiranje izlaza......................400
M etoda p r in t f ( ) ..................................... 400 S y s te m .o u t.fo rm a t()............................400 Klasa F o r m a t t e r ...................................401 Specifikatori f o r m a t a ......................... 402 Konverzije klase F o r m a t t e r ..............403 S tr in g .f o rm a t() ..................................... 406 R e g u l a r n i i z r a z i ..................................... 4 0 8 O s n o v e .....................................................408 Pravljenje regiilarnih i / r a z a ..............411 K v antilikatori..........................................413 C h a rS e q u e n c e ....................................... 414 Klase P a tte rn i M a tc h e r .....................414 M etoda s p l i t ( ) ....................................... 422 O p eracije z a m e n e ................................ 422 M etoda r e s e t( ) ....................................... 424 R egularni i/.ra/.i i Javin ula/.no i/Iazni s iste m ............................425

K o n c e p t i .............................................. 34 5 O s n o v n i i z u z e c i ............................... 3 4 6 Argumenti izuzetku........................... 347 H v a t a n j e i z u z e t k a ............................ 3 4 8 Blok trv ................................................ 34S Blokovi za obradu izuzetaka...............348 P ra v lje n je s o p s t v e n i h iz u z e ta k a .3 4 9 Izuzeci i zapisivanje........................... 352 S p e c if ik a c ija i z u z e t a k a .................. 3 5 5 H v a t a n j e b ilo k o g iz u z e tk a . . . .3 5 6 Poloaj nastanka izuzetka na steku izvravanja..........................................358 Ponovno generisanje izu z e tk a ........ 359 Ulanavanje izuzetaka....................... 362 S t a n d a r d n i iz u z e c i u J a v i .............3 6 5 Specijalan sluaj klase R untim eE xcep tio n........................... 305

Leksiko analiziranje ulaza . . . .427


C ran inici k la se S c a n n e r .................. 42l> l.eksika anali/a p om ou reg u larnih i / r a / a ...................................430

Klasa S tringT okenizer............... 431 Saetak........................................... 4 3 1

Sadraj

ix

14: Podaci o tipu

432

P roblem s b ris a n je m ............................517 ta se zbiva na g ra n ic a m a ...................518 K o m p e n z a c i j a z a b r i s a n j e ............5 2 2 Pravljenje instanci tip o v a ...................523 Nizovi generikih tip o v a .....................526 G r a n i c e ...................................................... 531 D o k e r s k i a r g u m e n t i ....................... 5 3 5 Koliko je prevodilac p a m e ta n ? ......... 537 K o n tra v a rija n sa ..................................... 539 N eogranieni dokerski a rg u m e n ti................................................ 541 Konverzija h v a ta n je m ..........................547 N e d o s t a c i ................................................... 5 4 9 P rosti tipovi ne m o g u biti p a ra m e tri t i p a ....................................... 549 Realizacija p aram etrizo v an ih in terfeisa...................................................551 E ksplicitna konverzija tipova i u p o z o re n ja ............................................551 P r e k la p a n je ............................................553 O snovna klasa o tim a in te r f e js ......... 554 S a m o o g r a n i e n i t i p o v i .................... 5 5 5 G eneriki ko d koji se neobino p o n a v lja ...................................................555 S a m o o g ra n i e n je ...................................557 K ovarijansa a rg u m e n a ta .....................559

Potreba za prepoznavanjem tipa tokom izvravanja............... 432 Objekat tipa Class........................434


Literali k la se ........................................... 439 G enerike reference k la s a .................. 441 Nova sintaksa za konverziju tipova . 444

Provera pre konverzije tipa . . . .445


Korienje literala ld ase....................... 451 D inam iki in sta n ce o f . . : ...................453 Rekurzivno b r o ja n je ............................454

Registrovane proizvodne m etode...........................................456 Poreenje instanceof sa ekvivalencijama klase............. 459 Refleksija: informacije o klasi u vreme izvravanja....................461 itanje metoda klase.....................462 Dinamiki posrenici................. 465 Null o b jek ti...................................469 Lani objekti i vezivne funkcije . . . . 476 Interfejsi i podaci o tip u ............. 476 Saetak........................................... 482

15: Generiki tipovi

484

D in a m i k a b e z b e d n o s t t ip o v a .

.562

I z u z e c i .................................................... 563 M i k s i n i ................................................. 565


M iksini u jeziku C + + ......................... 566 M eanje p o m o u interfejsa................ 567 K orienje obrasca D e c o r a to r ......... 568 M iksini s d inam ikim p o s re d n ic im a ..........................................570

Poreenje sa C + + - 0 1 1 1 ............... 485 Jednostavni generiki tipovi . . .485


Bihlioteka n - t o r k i ................................ 487 Klasa s t e k a ..............................................490 N a s u in ic n a L is ta .................................. 491

Generiki interfejsi..................... 492 Generike m etode........................496


Korienje zakljuivanja o tipu a r g u m e n ta ..............................................497 A rgum enti prom enljive d u /in e i generike m e to d e ................................499 G enerika m etoda za u p o treh u s G e n e r a to r im a .....................................500 G e n e ra to r opte n a m e n e .................. 501 Pojednostavljenje u p o tre h e n - to r k i. 502 U sluna m eto da za S e t .......................504 A n o n i m n e u n u t r a n j e k la s e . . . .5 0 8 P r a v l j e n j e s l o e n i h m o d e l a . . . .5 0 9 T a j a n s t v e n o b r i s a n j e ..........................5 1 2 P ristup u G + + - u .................................. 513 M igracijska k o m p a tih iln o s t..............516

L a t e n t n i t i p o v i .................................. 572 K o m p e n z a c ija za n e p o s t o ja n je l a t e n t n i h t i p o v a ............................... 576 R elleksija........ ................................... 576 Priinena m etode na se k v e n c u ........ 577 Kada sltiajno neinate odgovarajuc'i interfejs....................... 580 Simuliranje latentnih tipova pom ocu a d a p te r a ............................. 582 U p o t r e b a f u n k c ijs k ih o b j e k a t a k a o s t r a t e g i j a .................. 585 S a e ta k : d a li je e k s p l ic it n a k o n v e r z ija t ip o v a z a is ta ta k o lo a ? ............................................... 590
Proitajte i o v o ....................................... 592

Misliti na Javi

1 6 :N iz o v i

593

I z b o r r e a l i z a c i j e ..................................... 6 8 3 S tru k tu ra za ispitivanje p e rfo rm a n s i............................................684 Perform anse razliitih L is ta ..............688 O p asn o sti od m ik roporedenja p e rfo r m a n s i............................................694 Iz b o rsk u p a (realizacije interfejsa S e t ) .................. 696 Izb or m apa (realizacija interfejsa M a p )................ 698 U s l u n e m e t o d e .................................. 7 01 U redivanje i p retraivanje lista (realizacija interfejsa L i s t ) ................ 705 Kako kolekciju ili m a p u uiniti n e p ro m e n ljiv o m .................................. 706 Sinhronizovanje kolekcije ili m a p e . .708

ta to nizove ini p o se b n im .. . .593 Nizovi su prvorazredni objekti. .595 Vraanje niza v re n o sti............. 597 Viedimenzionalni n iz o v i.........599 Nizovi i generiki tipovi............. 603 Pravljenje podataka za testiran je...................................605 Arrays.fill()................................. 605 Generatori podataka..................... 606 Pravljenje nizova od Generatora... 612 M etode klase A rrays....................616 Kopiranje niza..............................617 Poreenje nizova.......................... 618 Poreenje elemenata niza..............619 Ureivanje niza............................ 623 Pretraivanje ureenog niza.......... 624 Saetak........................................... 626 17: D e t a l j n o r a z m a t r a n j e k o n te jn e ra 628

uvanje referen ci ............................... 7 0 9


K ontejner W e a k H a s h M a p ................ 712

Kontejneri Jave

1 . 0 / 1 . 1 ....................7 1 3

V ector i E n u m e r a t i o n .......................713 Klasa H a s h t a b l e .................................. 714 Klasa S ta c k ..............................................714 Klasa B itS e t........................................... 716

Saetak .........................................................7 1 8
18: J a v in u la z n o - iz la z n i s is te m 719

Potpuna taksonomija kontejnera.....................................628 Popunjavanje k ontejnera...........629


G en erato rsk o reen je............................630 G en erato ri m a p a .................................. 632 K orienje a p strak tn ih k la sa ..............635

Klasa File ................................................... 7 1 9


Listanje d ir e k to r iju m a ....................... 719 U slune m eto d e za d ire k to riju m e . . 723 lrovera postojanja i pravljenje d ire k to riju m a ......................................... 72S U la z i i / . I a z ................................................ 7 3 0 Vrste ukr/nih to k o v a ............................730 Vrste iz la/nih tokova (O u tp u tS tre a m .................................. 732 D o d a v a n je a tr ib u ta i k o ris n ih i n t e r f e j s a ................................................... 7 3 2 I iltriranje u la /n o g t<.kd.....................733 I iltriranje i/la /n o g t o k a .....................734 K la s e z a i t a n j e i u p i s i v a n j e . . . .7 3 4 I/vori i p o n o ri p o d a ta k a .....................735 M enjanje ponaanja t o k a .................. 736 Klase koje nisu p ro m e n je n e ..............736 P o s e b a n s lu a j: k la s a R a n d o m A c c e s s F i l e ..........................7 3 7 T i p i n e p r i m e n e U / I t o k o v a . . .7 3 7 Baferisana u la /n a d a to te k a ................ 738 ('ita n je i/ m e m o rije .............................. 739

Funkcije interfejsa C ollection. .643 O pcione o p e ra c ije ......................646


N ep o d rzan e o p e ra c ije ......................... 647

Funkcije liste.................................649 Skupovi i redosled skladitenja. .652


S o r te d S e t................................................ 656 R e d o v i z a e k a n j e ................................6 5 7 Redovi za ekanje s p rio rite to m . . . . 658 D vostrani redovi za e k a n je ..............660 I s c r p n i j e o M a p a m a ..........................6 6 1 F e rfo rm a n se ............................................663 S o r te d M a p .............................................. 666 L in k e d H a sh M a p ...................................667 T r a n s f o r m i s a n j e k lj u e v a i k l j u e v i z a h e i r a n j e .......................6 6 8 N ain rada m eto d e h a sh C o d e ( ) . . . 671 Transform isanje kljueva zbog h r z i n e ....................................................... 674 Kedefinisanje m etode h a s h C o d e ( ) . . 678

Sadraj

xi

F o rm atiran ulaz iz m e m o r ije ............739 O snove pisanja u d a to te k u ................ 740 uvanje i rek on stru isan je p o d atak a. .742 C itan je i upisivanje d ato tek a s n a su m in im p r is tu p o m ...................744 C ev o v o d i................................................... 745 U s lu n e k la s e z a ita n je i p i s a n j e ...................................................... 7 4 5 itan je b in a rn ih d a to te k a ...................748 S t a n d a r d n i U / I t o k o v i .................... 7 4 9 itan je s ta n d a rd n o g u lazn o g toka . . 749 O m o ta v a n je to k a S y ste m .o u t u P r in tW rite r..........................................750 P reu sm erav an je stan d a rd n o g u laza/izlaza.............................................. 751 U p r a v l j a n j e p r o c e s i m a .................... 7 5 2 N o v e U / I k l a s e ..................................... 7 5 3 Konverzija p o d a ta k a ............................757 Pribavljanje p ro stih tip o v a ................ 759 Baferi p rik a z a ..........................................761 Rad s p o d acim a p o m o u b a f e r a .. . . 765 D etaljno o b a te rim a .............................. 766 D atoteke preslikane u m e m o riju . . . 769 Z akljuavanje d a to te k a ....................... 772 K o m p r i m o v a n j e .................................. 7 7 5 Ied n o slav n o k o m p i im ovanje u lo rm a tu G Z I P ..................................... 776 K om prim ovanje veeg broja dato tek a u ib rm atu Z i p ....................... 777 lava arbive ( IA IO ...................................779 S e r i j a l i z o v a n j e o b j e k a t a ................. 7 8 0 Pronalaenje k la s e ................................ 78 1 U pravljanje s e rija liz o v a n je m ............785 Korienje tr a jn o s ti .............................. 793 X M L ................................................................ 7 9 9 P r e f e - r e n c e s ..............................................8 0 2 S a e t a k ......................................................... 8 0 4

Misterija m etode v a lu e s().........810 Realizuje, ne nasleuje............... 813 Nasumian iz b o r ........................814 U potreba interfejsa za organizovanje..........................815 Skup Enum Set urnesto in d ik a to ra .....................................819 Korienje m ape E num M ap . . .821 M etode koje se menjaju u zavisnosti od k o n sta n te ............. 823 Stvaranje lanca ogovornosti pomou nabrojanih tipova............ 826 Maine stanja s nabrojanim tipovima...................................... 830 Viekratno otkrivanje tip a .........836 Otkrivanje tipa pomou nabrojanih tipova........................ 838 Korienje metoda koje se menjaju u zavisnosti od konstante nabrojanog tipa............................840 Otkrivanje tipa pomou mapa EnumMap...................................842 Korienje 2-D niza.......................843 Saetak........................................... 844

20:

A n o ta c ije

845

Osnovna sin tak sa........................846


Detinisanje anotacije......................... 846 M etaano tacije....................................848 P is a n je p r o c e s o r a a n o t a c ij a . . . .848 Elementi anotacija............................. 849 Ogra n ienj a pod razu meva n ih v re d n o s ti............................................ 850 Generisanje spoljnih d ato te k a ........ 850 Anotacije ne podravaju nasleivanje........................................ 854 Realizacija procesora......................... 854

19: N a b ro ja n i tip o v i

805

O snovne mogunosti nabrojanih tip o v a........................805 Uvoz slalinih lanovii u nabrojani tip............................ . H06 Dodavanje metoda nabrojanom t i p u ........................807 Kedolinisanie t'iuini mcloda.......... S0S Nabrojani tipovi u naredbam a sv v itch ............................................809

apt za obradu an o tacija............. 857 Upotreba obrasca Visitor sa alatkom a p t.............................. 861 Jedinino testiranje pom ou a n o ta c ija .......................................864 Testiranje generikih tipova alatkom @Unit............................873 ,,Svite nisu potrebne................... 874 Realizaciia interfejsa (Unit...........875 Uklanjanje koda za testiranje.........881 Saetak........................................... 883

xii

Misliti na Javi

2 1 : P a ra le ln o iz v r a v a n je

885

Svi aspekti paralelnog izvravanja.....................................886


Bre iz v rav a n je .....................................886 Poboljan d izajn k o d a ......................... 889 O s n o v e v i e n itn o g p r o g r a m i r a n j a ........................................ 8 9 0 Definisanje z a d a ta k a ............................890 Klasa T h r e a d ......................................... 891 U p o treb a izvrilaca (interfejsa E x e c u to r)............................893 D obijanje p o v ra tn ih v redn osti o d z a d a ta k a ............................................896 S p a v a n je ...................................................897 P r i o r i t e t ...................................................899 P rep u ta n je .............................................. 901 Servisne n i t i ............................................901 V arijante p ro g ra m ira n ja .....................906 T erm inologija..........................................911 Kako se p rid ru iti postojeoj n i t i . . . 912 Korisniko o k ruenje koje brzo re a g u je ............................................913 G ru p e n iti................................................ 914 H vatanje iz u z e ta k a .............................. 915

Uzajam na b lo k a d a ......................978 Nove kom ponente biblioteke . .983


C o u n tD ovvnL atch - brava sa o d b ro ja v a n je m ................................ 983 C y c lic B a rrie r..........................................986 D e Ia y Q u e u e ............................................ 988 P rio rity B lo c k in g Q u e u e ..................... 991 K ontroler staklenika napravljen po m o u S c h e d u le d E x e c u to ra ......... 994 S e m a f o r ................................................... 997 E x c h a n g e r............................................ 1001

S i m u l a c i j a ..........................................1003 Sim ulacija alterskog slubenika . . 1003 Sim ulacija r e s to r a n a ......................... 1008 R aspodela p o s la .................................. 1013 O p t i m iz a c ij a p e r f o r m a n s i . . . .1 0 1 8
Poreenje tehnologija u zajam no iskljuivih brava ( m u t e x a ) .............. 1018 K ontejneri bez z a k lju a v a n ja ......... 1027 O p tim istik o z a k lju a v a n je ............1034 R ea d W rite L o c k .................................. 1036

A k tiv n i o b j e k t i ............................... 1039 S a e t a k ................................................. 1042 L iteratura za dalje usavravanje . . . 1044

Deljenje r e s u r s a .......................... 918


N epravilno p ristu p a n je resursim a . . 918 R azreavanje takm ienja za deljene re s u rse ...................................921 A tom ske operacije i tre n u tn a v id ljiv o st...................................................926 A tom ske k l a s e ....................................... 932 K ritini o d e ljc i....................................... 933 S inhronizacija s d ru g im o b je k tim a ................................................ 939 Lokalno skladite n i t i ......................... 940 G a e n j e z a d a t a k a ............................... 9 4 2 U krasna b a ta ......................................... 942 G aenje blo kiran ih z ad a ta k a ..............945 Prekidanje izv rsav anja......................... 946 Provera postoji li p r e k id .....................954 M e u s o b n a s a ra d n ja z a d a t a k a ...................................................... 9 5 6 w ait() i n o tity A lI() ..............................957 n o tify () u o d n o su na n o tify A ll(). . . 962 Proizvoai i p o tr o a i....................... 965 Proizvodai, p otroai i redovi za e k a n je ................................................ 971 Cevi za ulazno/izlazne operacije izm edu z a d a ta k a .................................. 976

22: Grafika korisnika okruenja

1045

A pleti........................................... 1047 Osnove Svvinga..........................1047


Alatka za p rik aziv an je....................... 1050

Pravljenje d ugm eta................... 1051 I Ivatanje d o g a d a ja ................... 1052 Vieredna polja za te k s t...........1054 Rasporeivanje elem enata. . . .1056
1056 1057 1058 1058 A p solutno p o z ic io n ira n je ................ 1059 R asporediva B o x I,a y o u t................ 1059 Koji je p ristu p najbolji?..................... 1059
R asporediva R asporediva R asporediva R asporediva B o r d e r L a y o u t......... F lo w L a y o u t.............. G r id L a y o u t.............. G ridB agL avout . . . .

Model dogaaja grafike biblioteke Svving.........1059


T ipovi dogadaja i p rije m n ik a ......... 1060 Praenje vi.e d o g a d a ja ..................... 1066

Primeri Svving kom ponenata. . 1068


D u g m a d ................................................ IO (->9 Ik o n ic e ................................................... 1071

Sadraj

x iii

P riru n i s a v e ti..................................... 1073 Jed n o redn a polja za t e k s t ................ 1073 Ivice..........................................................1075 Mali program za u redivanje te k sta . . 1076 Polja za p o tv rd u ...................................1077 R a d io -d u g m a d ..................................... 1078 K om hinovane liste (pad aju e liste)..................................... 1080 G rafike liste..........................................1081 O k n o s je z i c im a ................................ 1083 O k v iri za p o r u k e ................................ 1083 M e n iji...................................! ................1085 Iskaui m e n iji..................................... 1091 C r t a n j e ...................................................1092 O kviri za d i j a l o g .................................1095 D ijalozi za rad s d a to te k am a ............1099 H T M I. u k o m p o n en tam a biblioteke Svving...................................1101 Klizai i linije n a p re d o v a n ja ............1102 B iranje izgleda i p o n a a n ja .............. 1103 Stabla, tabele i ip b o a r d ...................1105 J N L P i J a v a W e b S t a r t ................. 1105

I z r a d a SVVT a p l i k a c i j a ..................1 1 5 0 Instaliranje S W T - a ............................1150 Z dravo, S W T ....................................... 1150 Izbegavanje re d u n d a n tn o sti............1153 M e n iji..................................................... 1155 O kna s k articam a, d u g m ad i d o g a a ji..............................................1156 G ra fik a ...................................................1160 Paralelno izvravanje u SW T-u . . . 1162 Poredenje SW T-a i Svvinga.............. 1164 S a e t a k ...................................................... 1 1 6 4 R e s u rs i.................................................. 1165

A: Dodaci

1166

Paralelno izvravanje i Svving. .1110


D u g o trajn i z a d a c i .............................. 1111 V izuelno vienitno p ro g ram iranje ..1 1 1 8

Vizuelno program iranje i zrna J a v e ................................. 1120


ta je z rn o ? ............................................ 1121 Ispitivanje zrna klasom I n t r o s p e c t o r ....................... 1123 N apred n ije z r n o ...................................1128 Z rna J.ne i s in h ro n iz a c ija ................ 1131 Pakovanje z r n a ..................................... 1135 Sloenija podrka za z r n a ................ 1137 V'ie o z r n im a ....................................... 1137 A l t e r n a t i v e z a S v v in g .................... P r a v l j e n j e F la s h VVeb k l i j e n a t a p o n i o u F l e x a .................................. 1138 Z dravo, h lc x ..........................................1139 Prevodenje M X M l.-a......................... 1 140 M XM L i A c tio n S c rip t....................... 1141 K ontejneri i k o n tro le ......................... 1141 Hfekti i s tilo v i....................................... I 143 l )o g a d a ji..................................... : . . . . 1 144 Povezivanje ,s lav o m ............................I 144 M odeli podataka i povezivanje p o d a ta k a .................................................1147 Izgradnja i p rim e n a ............................1 148 1 13 7

Dodaci koji se mogu preuzeti sa Interneta................. 1166 Misliti na jeziku C: osnova za J a v u .......................... 1166 Seminar Thinking in Java . . . . 1166 CD sa sem inarom H ands-O n Java.......................... 1167 Seminar Thinking in O bjects.. 1167 Thinking in Enterprise fav a .. .1167 T hinking in Patterns (vvith Java)...................................1168 Seminar Thinking in P atte rn s.. 1168 Konsultacije i revizije dizajn a.. 1169

B: Resursi

1170

S oftver......................................... 1170 Programi za ureivanje teksta i alatke za pravljenje aplikacija.....................................1170 K n jig e ......................................... 1171 Analiza i projektovanje................ 1171 Jezik Python............................... 1173 Spisak rnojih knjiga.......... ......... 1174

Spisak termina korienih u knjizi Indeks

1175 1177

Predgovor
Na poctku satn priao Javi kao jojednom programskom jeziku , to ona umnogome ijeste.
A
li, k a k o je v r e m e o d m i c a l o

I St

s a m

je v i Se p r o u a v a o

,p

e o s a m

d a

u v i a m

d a

je osnovna nam ena Jave drugaija od nam ene svih drugih jezika koje sam sretao. Program iranje je snalaenje u sloenosti: sloenost problem a koji elite da reite dodaje se na sloenost raunara na kome se problem reava. Zbog ove sloenosti veina naih programskih projekata propadne.- Pa ipak, za gotovo nijedan program ski jezik za koji znam nije odlueno da bi njegov glavni cilj trebalo da bude savladavanje sloenosti razvoja i oravanja program a.1Naravno, mnoge odluke pri stvaranju program skih jezika donete su sa sloenou na umu, ali u nekom trenutku uvek bi se nale neke druge stvari za koje se smatralo da su neophodne u meavini. Te druge stvari neizbeno prouzrokuju da program er koji koristi dati jezik na kraju udari glavom o zid. Na primer, C ++ je m orao da bude kom patibilan sa starijim jezikom C (da bi se omoguio lak prelazak C program erim a), a pri tom i efikasan. I jedno i drugo su veoma korisni ciljevi i um nogom e zasluni za uspeh jezika C++, ali takoe unose dodatnu sloenost zbog koje neki projekti ne bivaju zavreni (naravno, moete kriviti program ere i upravu, ali, ako jezik moe da pom ogne tako to e otkrivati vae greke, zato to ne bi i radio). Evo jo jednog prim era. Visual Basic (VB) je bio vezan za BASIC, koji ba nije bio proiriv jezik, tako da su sva proirenja nagom ilana u VB-u proizvela zaista zastraujuu sintaksu koja se veoma teko odrava. Perl je vertikalno kom patibilan sa Avvkom, Sedom, Grepom i drugim Unixovim alatima koje je trebalo da zameni, a posledica je bila da je esto optuivan kako proizvodi ,,kod sam o za pisanje" (tj. posle nekoliko meseci ne moete ga vie itati). S druge strane, pri stvaranju jezika kao to su C++, VB, Perl i Smalltalk, deo napora je usmeren na reavanje problem a sloenosti, pa oni veoma uspeno izlaze na kraj sa odreenom vrstom problema. Dok sam uio Javu, ini mi se da je na m ene najvie uticalo to to je Sun m eu svojim ciljevima pri projektovanju imao i princip smanjenja sloenosti zaprograrnera. Kao da su rekli: Vano nam je da ubrzam o i pojednostavim o pravljenje robusnog koda. U ranim danim a, ovaj princip je rezultovao kodom koji se nije brzo izvravao (iako se to s vremenom popravilo), ali je zaista zadivljujue skratio vreme potrebno za pisanje program a - za izradu program a na lavi potrebno je upola m anje vrem ena nego za pravijenje odgovarajueg C ++ programa. Ovaj rezultat sam po sebi moe da utedi m nogo vrem ena i novca, ali lava se tu ne zaustavlja. O na nastavlja da pakuje sve vane i sloene zadatke, kao to su vienitni rad i mreno program iranje, u svojstva jezika ili bibliotcke koji pojednostavljuju te zadatke. Najzad, latila se nekih zaista veoma sloenih problem a kakvi su meduplattonnsko programiranje, dinamika prom ena koda, ak i pitanja zatite od kojih se svaki na vaoj lestviei sloenosti moe svrstati od prepreka'1do potpuno nemogue". Stoga, uprkos problem ima s perform ansam a koje smo susreli, Java silno obeava: moe od nas da napravi znatno produktivnije programere.

Misiim da je programski jc/ik l vthon najblii t o m cilju. I oglodajte ivw w .P yth o n .o rg

Misliti na Javi

Java na sve naine poveava opseg komunikacije tneu Ijudima: stvaranjem program a, tim skim radom na stvaranju program a, izgradnjom korisnikog okruenja kao veze izm eu program a i l<orisnika, izvravanjem program a na razliitim tipovim a raunara i lakim pisanjem program a kojima se pristupa preko Interneta. Mislim da ogrom na koliina inform acija koje putuju Internetom nije najvaniji pokazatelj znaaja kom unikacione revolucije; prava revolucija je u tom e to emo moi m nogo lake da razgovaramo - pojedinano, u grupam a, i sa celom planetom . Pria se da bi naredna revolucija iznedrila svojevrstan globalni um proizaao iz iskustva vie ljudi i njihove m edusobne povezanosti. Java moe ali i ne m ora, biti alat koji e podstai tu revoluciju - i zbog same takve m ogunosti, oseam kako je znaajno da i drugi naue ovaj jezik.

Java SE5 i SE6


Ovo izdanje knjige m nogo duguje poboljanjim a Jave koja je Sun prvo nazvao JDK 1.5, a kasnije to izmenio u JDK5 ili J2SE5, te najzad ispustio zastarelo 2 i proglasio verziju Java SE5. D obar deo izm ena u jeziku Java SE5 trebalo je da poboljaju iskustvo program era. Kao to ete videti, projektanti Jave nisu u tom e potpuno uspeli, ali su, uopte uzev, napravili veliki korak u pravom smeru. Jedan od vanih ciljeva ovog izdanja bio je potpuno obuhvatanje poboljanja Jave SE5/6 - da se ona objanjavaju i upotrebljavaju u celoj knjizi. To znai da je ovo izdanje, donekle hrabro, samo za Javu SE5/6 i da se d obar deo koda u knjizi nee prevesti pom ou ranijih verzija Jave; ukoliko pokuate, sistem e se pobuniti i zaustaviti. Mislim ipak da su prednosti ovog pristupa vredne tog rizika. Ako ste prim orani da se sluite ranijim verzijama Jave, na www.MindView.net moete besplatno preuzeti prethodna izdanja ove knjige. Zbog mnogih razloga, odluio sam da u besplatnom elektronskom obliku ne ponudim tekue izdanje knjige, nego samo prethodna.

Java SE6
Ova knjiga je bila ogrom an projekat koji je zahtevao mnogo vremena, i pre nego to je izala, pojavila se beta verzija Jave SE6 (pod radnim im enom mustartg). lako je lava SF.6 donela nekoiiko manjih izmena koje su poboljale neke prim ere u knjizi, uglavnom nije uticala na sadraj ove kniige; glavna obeleja Jave SE6 su poveanje brzine izvravanja i novi elementi biblioteka, koja nisu tem a ove knjige. Program i navedeni u knjiz.i uspeno su testirani na kandiatskoj verziji Jave SF.6, pa ne oekujem da e ona doneti izmene koje bi uticale na sadraj ove knjige. Ukoliko u vreme objavljivanja zvanine verzije Jave SE6 bude vanih iz.mena, njih emo uvaiti u izvornom kodu knjige koji se moe preuzeti na adresi www.MiiidVicw.nct. Na koricama knjige pie da je ona ,,za Javu SE5/6", to znai napisana za |avu SF.5 i veoma vane iz.mene koje je ta verzija unela u jezik, ali jednako primenljiva i na Javu SE6.

Predgovor

etvrto izdanje
Zadovoljstvo pisanja novog izdanja knjige donosi injenica se stvari m ogu uraditi kako treba, u skladu sa onim to sam saznao od izlaska poslednjeg izdanja. esto su ti uvidi tipa Kada ne dobije ono to si hteo, dobije pouku" a za m ene je to prilika da popravim ono to je pogreno ili prosto zamorno. fednako tako, pisanje novog izdanja dovodi do fascinantnih novih zamisli, a stid zbog pogreaka m nogo je manji od uivanja u otkriu i m ogunosti da se zamisli izraze bolje nego pre. Tu je i izazov da knjigu napiem tako da i vlasnici prethodnih izdanja poele da je kupe. To me tera da poboijavam ,preraujem i premetam sve to mogu da bi knjiga postala novo i vredno iskustvo za posveene itaoce.

Izmene
U ovom izdanju nem a CD-a. Osnovni deo tog CD-a, m ultim edijski sem inar Thinking in C (koji je za MindVievv napravio Chuck Allison), sada se moe preuzeti u obliku Flash prezentacije. Polaznici tog seminara koji nedovoljno poznaju sintaksu C-a obuavaju se da ovladaju m aterijalom iz ove knjige. Iako dva poglavlja ove knjige daju pristojan uvod u sintaksu, ona m oda nee biti dovoljna osobam a bez adekvatnog predznanja, a prezentacija Thinking in C upravo njima pomae da dostignu potreban nivo. Poglavlje Paralelno izvravanje( ranijeVienitno izvravanje1 1 ) potpuno je preraeno da bi odraziio velike izmene u odgovarajuim bibliotekama Jave SE5, ali jo uvek prua temelj za osnovne koncepte paralelnog izvravanja. Bez tog jezgra, teko ete razumeti sioenije oblasti paralelnog izvravanja. Mnogo sam meseci radio na tome, uronjen u taj drugi paralelan svet, i na kraju je ispalo da to poglavlje daje ne samo osnovu, nego i naprednije uvide. Znaajnim novim m ogunostim a Jave SE5 posveena su nova poglavlja, a ostale su utkane u izmene postojeeg materijala. Poto ja neprestano prouavam projektne obrasce, povean je i njihov broj u knjizi. U knjizi je biio m nogo premetanja, u dobroj meri nadahnutih predavakom praksom i sa/.nanjem da bi se moje poimanje onoga to treba da stane u je d n o poglavlje moglo razborito kritikovati. I5io sam sklon nepromiljenom verovanju da tema m ora biti dovoljno velika" kako bi zasluila sopstveno poglavlje. Ali, naroito tokom predavanja projektnih obrazaca, uvideo sam da polaznici seminara najbolje shvataju kada uvodim jedan po jedan obrazac i odm ah uradim odgovarajuu vebu, ak i ako zbog toga tek nakratko govorim . (Otkrio sam da je taj tempo prijatniji i za m ene kao predavaa.) Zato sam u ovoj verziji knjige pokuao da poelim poglavlja na teme i da ne brinem koliko su duga. ini m i se da je tako bolje. Uvideo sani i kolika je vanost testiranja programa. Ukoliko nem ate ugraen sistem za testiranje, u kojem se testiranjeobavlja svaki put kada generiete program, ne moete znati koliko je va program pouzdan. Da bih izbegao tu nesigurnost, napravio sam sistem za testiranje koji prikazuje i vrednuje rezultate svakog programa. (Sistem je napisan na Pvthonu; nai ete ga u kodu ove knjige koji se moe preuzeti na lokaciji www.MindView.net.) Testiranje uopte razm otreno je u dodatku objavljenom na adresi http://M indView.net/ Books/Bctterjavn; tu su objanjene osnovne vetine kojima bi, po m om miljenju, svi program eri trebalo da vladaju.

Misliti na Javi

Pored toga, proeljao sam sve prim ere u knjizi i zapitao se: Zato sam to uradio ba ovako?" U veini sluajeva neto sam izm enio i poboljao, da bi prim eri bili dosledniji i da bih pokazao ono to sm atram najboljom praksom u pisanju Java koda (koliko se to moe u tekstu uvodnog nivoa). Dizajn i realizacija m nogih postojeih prim era, znaajno su izmenjeni. Uklonjeni su prim eri koji su izgubili smisao i dodati novi. itaoci su imali zaista divne kom entare na prva tri izdanja ove knjige to mi je bilo veoma drago. Tu i tamo, poneko bi im ao i prim edbu i iz nekog razloga, periodino se javljala ista prim edba knjiga je preobimna". Po m eni, ako itaocu smeta samo obim dela, to i nije tako strano (prisetim o se prim edbe austrijskog cara o M ocartovom delu: Previe nota!, iako se ja ni na koji nain ne poredim s M ocartom ). M ogu sam o da pretpostavim kako takvu prim edbu upuuje onaj ko e se tek upoznati sa irinom Jave i ko nije video ostale knjige o ovom jeziku. Ipak, pokuao sam da u ovom izdanju skratim delove koji su postali nepotrebni ili barem ne previe znaajni. Po praviiu, pokuao sam da uklonim sve to vie nije potrebno, ubacim izmene i poboljam sve to sam mogao. Mislim da sam to mogao slobodno da uradim , jer je originalni m aterijal i dalje na Internetu (na adresi www.MindView.net), u obliku prva tri izdanja koje se m ogu besplatno preuzeti, i dodataka za ovu knjigu. Iskreno se izvinjavam svima koji jo uvek ne mogu da podnesu veliinu ove knjige. Verovali ili ne, naporno sam radio da bih je smanjio.

Napomena o dizajnu korica


Korice knjige Misliti na Javi inspirisala je am erika varijanta pokreta um etnost i zanatstvo (American Art & Crafts Movement) koji se javio poetkom 20. veka i dostigao zenit izmeu 1900. i 1920. Nastao je u Engleskoj kao reakcija na mainsku proizvodnju, industrijsku revoluiu i veoma izraen dekorativni stil viktorijanskog doba. Pokret se zalagao za um eren dizajn i prirodne form e koie je prom ovisao um etniki pravac art nuvo (fr. l'art nouveau). Isticana je vanost um etnika kao pojedinca, a nije odbacivano korienje modernih alata. Postoje velike slinosti s dananjom situacijom: prelazak u novi vek, napredak od korena raunarske revolucije do neega plemenitijeg i znaajnijeg za pojedinca, isticanje vetine pisanja soltvera um esto iste proizvodnje koda. Javu vidim kao pokuaj uzdizanja program era iznad nivoa tehniara operativnog sistema, u pravcu softverskog majstora". I autor i dizajner korica (koji su prijatelji od detinjstva) nalaze inspiraciju u ovom pokretu i obojica su okrueni nam etajem, lam pam a i drugim predm etim a koji ili potiu iz tog perioda, ili su njime inspirisani. T3rugi detalj na ovim koricam a je kutija za prikupljanje prirodnjakih eksponata - u ovom sluaju insekata. Ti insekti su objekti stavljeni u kutije kao objekte. Same kutije su stavljene u objekat ,,korica, to ilustruje osnovni koncept kom binovanja u objektno orijentisanom program iranju. Program eru se lako namee asocijacija na ,,bubice; bubice su
Vecina p rim e ra je p re v e d e n a , pa je in o g u c e d a su se p u tk ra le i greke. U k o lik o u o c ite g re k u , m o lim o vas d a p o aljete p o ru k u n a rctlakcijci@ tnikrokiijiga.co.yit. O rig in a ln e verzije svih p rim e r a naci cete na w w w .in iiic iv icw in c .c o m /ril4 /C o c icln stritc tio iis.litiiil.

Predgovor

ovde uhvaene i, po svoj prilici, ubijene u tegli za uzorke i smetene u male izlobene kutije: sve ovo nagovetava Javinu sposobnost da pronalazi, prikazuje i obuzdava bubice (to je zbilja jedan od njenih najjaih aduta). Za ovo izdanje naslikao sam akvarel prikazan na koricama, ispod stilizovane kutije.

Zahvalnice
Prvo, hvala saradnicim a koji su radili sa m nom na sem inaru, u konsultacijam a i pri ostvarivanju nastavnih projekata. To su: Dave Bartlett, Bill Venners, Chuck Allison, Jeremy Meyer i Jamie King. Cenim vae strpljenje, dok i dalje pokuavam da napravim najbolji model za m eusobnu saradnju nezavisnih ljudi kao to smo mi. O dnedavno, bez sum nje zaslugom interneta, povezao sam se sa iznenadujue velikim brojem ljudi koji m i pom au u poslu, uglavnom radei u svojim kunim kancelarijama. U prolo vreme, m orao bih da iznajmim prilino veliki kancelarijski prostor gde bih smestio sve te ljude, ali zahvaljujui Mrei, ekspres poti i telefonu, koristim njihovu pomo bez tih dodatnih trokova. Tokom m ojih pokuaja da nauim da se lepo igram sa ostalima svi vi ste bili veom a susretljivi, i nadam se da u i dalje bolje raditi svoj posao uz tuu pomo. Paula Steuer je neprocenjiva zato to je preuzela m oju ofrlje poslovnu praksu i postavila je na zdrave temelje (hvala Paula, to mi ne da mira kada me mrzi da neto uradim ). Jonathan Wilcox, Esq. proeljao je struk turu mog preduzea, prevrnuo svaki kamen ispod kojeg se m oda kriju korpioni i naterao nas da proem o kroz postupak pravno valjanogorganizovanja. Hvala ti na staranju i upornosti. Sharlynn Cobaugh je napravila od sebe strunjaka za obradu zvuka i postala je bitan deo tim a za pravljenje multimedijskih kurseva, kao i za reavanje drugih problema. Hvala na upornosti koju pokazuje suoena s nepredvidivim raunarskim problem im a. Ljudi iz prake firme Amaio izvukli su m e iz neprilika u nekoliko projekata. Daniel W ill-Harris me je prvi upoznao s radom preko Interneta, a i glavni je dizajner svih grafikih reenja. Tokom mnogih godina, Gerald VVeinberg je putem svojih konferencija i radioriica postao moj nezvanini trener i m entor, na emu mu zahvaljujem. Ervin Varga mi je izuzetno m nogo pom ogao svojim tehnikim ispravkama 4. izdanja - iako su drugi pomagali u pojedinim poglavljima i prim erim a, Ervin je bio osnovni tehniki recenzent cele knjige, a preradio je i vodi s reenjima za 4. izdanje. Ervin je pronaao greke i uneo poboljanja u knjigu koja su nemerljiv doprinos ovom tekstu. Njegova temeljnost i panja koju obraa na pojedinosti zauujue su, i on je najbolji recenzent kog sam imao. Hvala ti, Ervine. Moj blog na lokaciji www.Artiiiia.coin Bila Vennersa bio je izvor pom oi kada sam traio tua miljenja. Hvala itaocima koji su mi slali kom entare na osnovu kojili sam razjasnio pojmove - to su James VVatson, Hovvar Lovatt, Michael Barker i drugi, posebno oni koji su pomogli s generikim tipovima. Hvala Marku VVelshu na njegovoj stalnoj pomoi. Evan Cofskv mi stalno prua veliku podrku svojim poznavanjem (napamet!) svih zam renih pojedinosti o podeavanju i odravanju Web servera na Linuxu. On se stara da MindVievvov server bude podeen i bezbedan.

Misliti na Javi

Posebno hvala m om novom prijatelju, kafi, koja mi je ulila gotovo neogranien entuzijazam za ovaj projekat. Kafi Camp4 u gradu Crested Butte u Koloradu bio je stalno sastajalite ljudi koji su dolazili na MindVievvove seminare, a tokom sem inarskih odm ora pruao nam je najbolju opskrbu hranom i piem koju sam ikada dobio. Hvala mom drugaru Alu Smithu to ga je stvorio i od njega napravio tako zanimljivo i zabavno mesto. I svim barm enim a kafia koji tako veselo toe pie. Hvala ljudima u Prentice Hallu na tolerantnosti i ogrom nom strpljenju kojim mi obezbeuju sve to traim. Odreene alatke su se pokazale nezamenljivim tokom procesa razvoja program a i ja osetim veliku zahvalnost prema njihovim stvaraocima svaki put kada ih upotrebim . Cygwin ( www.cygwin.com) reio mi je bezbroj problem a koje W indows nee/ne moe, svakog dana sam m u sve vie privren (da sam ga sam o imao pre 15 godina kada je moj mozak jo bio ,,zalemljen za GNU Emacse). IBM-ov Eclipse ( www.eclipse.org) zaista je divan doprinos zajedni projektanata softvera, pa oekujem da e i ubudue davati velike rezultate, poto se i dalje razvija. (Od kad je to IBM postao kul? M ora da mi je neto promaklo.) JetBrains IntelliJ Idea nastavlja da kri nove kreativne puteve m eu razvojnim alatkama. Na ovoj knjizi poeo sam da koristim Sparxsystemsov Enterprise Architect i on je brzo postao m oja omiljena UML alatka. U raznim prilikam a dobro je posluio form ater koda Jalopy Marca Hunsickera ( www.triemax.com ), a Marco je bio veoma susretljiv i konfigurisao ga prem a mojim specifinim potrebam a. Katkada je bio koristan i JEdit Slava Pestova ( www.jedit.org ) sa svojim softverskim dodacim a, i to je sasvim dobar editor za poetnike na seminarima. 1 naravno, ako to nisam ve dovoljno puta rekao na svim moguim mestim a, za reavanje problem astalnokoristim Pvthon ( www.Pytlion.org). On je delo m o g drugara Guida Van Rossuma i grupe uvrnutih genija s kojima sam trao nekoliko divnih dana (Tim e Petersu, uram io sam mia kojeg si pozajmio i zvanino ga nazvao TimBotM i"). Vi, m om ci, treba da nadete neko zdravije mesto za ruak. (Takoe, hvala celoj Pvthon zajednici, oni su fenom enalna gomila ljudi.) Mnogi su mi slali ispravke i svima sam dunik, ali posebnu zahvalnost zasluuju (za prvo izdanje): Kevin Raulerson (koji je pronaao obilje sjajnih bubica), Bob Resendes (zaista neverovatan), (ohn Pinto, Joe Dante, Joe Sharp (sva trojica su bili udesni), David Combs (tnnoge gramatike ispravke i razjanjenja), dr Robert Stephenson, John Cook, Franklin Chen, Zev Griner, David Karr, Leander A. Stroschein, Steve Clark, Charles A. Lee, Austin Maher, Dennis P. Roth, Roque Oliveira, Douglas Dunn, Dejan Risti, Neil Galarneau, David B. Malkovsky, Steve VVilkinson i mnogi drugi. Prot. dr Marc Meurrens uloio je veliki n ap o rd a u Evropi objavi i uitii dostupnom elektronsku verziju prvog izdanja knjige. Hvala svitna koji su mi pomogli da ponovo napiem prim ere uz korienje biblioteke Swing (za 2. izanje) i za svu drugu pomo. To su Jon Shvarts, Thom as Kirsch, Rahim Adatia, Rajesh Jain, Ravi M anthena, Banu Rajamani, Jens Brandt, Nitin Shivaram, Malcolm Davis i svi koji su tni daii podrku. U 4. izdanju, Chris G rinstaff je m nogo pom ogao tokom razvoja odeljka o SVVT-u, a Sean Neville je za mene napisao prvu verziju odeljka o Flexu.

Predgovor

Kad god pomislim da sam najzad sve nauio o program iranju za paraleleno izvravanje, otvore se neka nova vrata i pokae se da im a jo planina koje m oram prei. Hvala Brianu Goetzu to mi je pom ogao da savladam sve prepreke u novoj verziji poglavlja Paralelno izvravanje i to je otkrio (nadam se!) sve greke. Nije iznenaenje to mi je poznavanje Delphija pom oglo da razum em Javu, budui da ta dva jezika imaju puno zajednikih koncepata i dizajnerskih reenja. Moji prijatelji koji se bave Delphijem pom ogli su mi da proniknem u unutranjost tog sjajnog programskog okruenja. To su Marco Cantu (jo jedan Italijan: da li dobro poznavanje latinskog doprinosi sklonosti ka program skim jezicima?), Neil Rubenking (koji se bavio jogom, vegetarijanstvom i zenom dok nije-otkrio raunare) i, naravno, Zack Urlocker (prvi direktor projekta Delphi), stari prijatelj s kojim sam proputovao svet. Svi mi sm o dunici briljantnom A ndersu Hejlsbergu, koji se i dalje mui sa C#-om (a taj jezik je bio glavni uzor za Javu SE5, u ta ete se uveriti u knjizi). Moj pronicljivi prijateij Richard Hale Shaw pruio mi je vrlo korisnu podrku (i Kim takoe). Richard i ja smo proveli vie meseci zajedno, drei seminare, pokuavajui da osm islim o savren m etod uenja za polaznike. Dizajn knjige, dizajn korica i sliku na koricam a uradio je moj priiatelj Daniel WillHarris, priznati autor i dizajner ( www.Will-Harris.com ), koji se igrao sa samolepljivim slovima u osnovnoj koli dok je ekao da raunari i stono izavatvo budu otkriveni, i koji se alio na moje gunanje zbog algebarskih problem a. Ipak, knjigu sam sloio sam tako da sve greke u slaganju idu na moj raun. Pisao sam u M icrosoftovom W ordu XP, a fotoslog je priprem ljen u Adobeovom Acrobatu. Desilo se da sam bio u inostranstvu oba puta kada sam pravio finalnu verziju knjige - prvo izdanje sam slao iz Kejptauna u Junoj Africi, a drugo iz Praga (to je moj doprinos eri elektronike). Tree i etvrto sam poslao iz Creste Butta u Koloradu. Font korien na koricam a je ITC Rennie Mackintosh. Posebno zahvaljujem svojim uiteljima i svim svojim studentim a (koji su istovremeno i moji uitelji). Dok sam radio na ovom izdanju, u krilu mi je esto leala maka Mollv i tako davala svoju toplu, krznenu podrku. Listu prijatelja koji su me podravali ine izmedu ostalih i: Patty Gast (izvanredna maserka), Andrew Birnstock, Steve Sinofskv, (D Flildebrandt, Tom Keffer, Brian McElhinney, Brinklev Barr, Liill Gates iz asopisa M idnight Engineering, Larry C onstantine i Lucv Lockvvood, Gene Wang, Dave Mayer, David Intersim one, Chris i Laura Strand, porodica AImquist, Brad Jerbic, Marilvn Cvitanic, M ark Mabry, porodice Robbins, porodice Moelter (i McMillan), Michael Wilk, Dave Stoner, porodica Cranston, Larry Fogg, Mike Sequeira, Cary Entsminger, Kevin i Sonda D onovan, Joe Lordi, Dave i Brenda Bartlett, Patti Gast, Blake, Annette i Jade, porodica Rentschler, porodica Sudek, Dick, Patty i Lee Eckel, Lvnn i Todd i njihove porodice. I, naravno, moji m ania i tata.

Uvod
oveku je dao jezik, a jezik je stvorio misao, koja jc mera Univerzuma Osloboeni Prometej, eli Ljudska bia... sasvim su pod vlau odreenog jezika koji je postao sredstvo izraavanja u njihovotn drutvu. U zabludi je ko misli da u prilagodavanju pojedinca stvarnosti jezik ne igra znaajnu ulogu i da je jezik samo sluajno sredstvo reavanja specifinih problema optenja i miljenja. injenica je da je stvarni svet u velikoj meri nesvesno izgraden na osnovu jezikih navika grupe. Status lingvistike kao nauke, Edvard Sapir 1929.
K A O I BILO KOJI LJU D SK IJEZIK , JAVA O M O G U U JE ISKAZIVANJE PO JM O V A . S NARASTANJEM I

uslonjavanjem problem a, ovaj nain izraavanja bie vam znatno laki i fleksibilniji od bilo kojeg drugog, ukoliko bude uspean. Ne m oete gledati na Javu samo kao na gom ilu mogunosti, jer neke m ogunosti nemaju smisla same za sebe. Celinu svih delova moete da 'koristite sam o ako razmiijate o projektovanju, a ne o pisanju koda. Da biste razumeli Javu na ovaj nain, m orate razumeti i problem e koji se pri tom e javljaju, kao i pri program iranju uopte. Ova knjiga razm atra problem e pri program iranju, objanjava zbogega oni predstavljaju problem e i pokazuje postupke kojima ih Java reava. Zato se skup m ogunosti koje objanjavam u svakom poglavlju zasniva na nainu reavanja pojedine vrste problem a pom ou Jave. Na taj nain, trudim se da vas dovedem, korak po korak, do take kada Java postaje va m aternji jezik. Od poetka do kraja, moj stav poiva na tom e da vi elite sebi da predoite model koji e vas dovesti do dubokog razumevanja jezika; ako naiete na zagonetku, moi ete da je ubacite u svoj model i pronaete odgovor.

Preduslovi
U ovoj knjizi podrazumeva se da koliko-toliko poznajete programiranje: shvatate da je program skup naredaba, poznajete princip potprogram a/tunkcije/m akroa, kontrolnih struktura kao to je ,,if konstrukcija za petlje kao to je vvhile" itd. Sve ovo ste mogli nauiti na raznim mestima, recimo pri program iranju na nekom makro-jeziku ili pri radu sa alatom kao to je Perl. Bez obzira na to koliko ste u dosadanjem programiranju ovladali osnovnim idejama, moi ete da radite po ovoj knjizi. Naravno, kniiga e biti laka C programerima, i jo vie C ++ program erim a, ali ne otpisujte sebe ako nemate iskustva u ovim jezicima samo budite sprem ni da naporno radite. Takode, uz pomo multimedijalnog seminara Thinking in Ckoji moete preuzeti s Iokacije mvw.MiudView.net, ovladaete u potpunosti osnovama potrebnim za uenje Jave. Kroz knjigu u postepeno uvoditi koncepte objektno orijentisanog program iranja (OOP) i osnovnih Javinih kontrolnih ntehanizama. Cesta pozivanja na osobine jezika C i C ++ ni.su kom entari poznavaoca, ve su pomo program erim a da uporede Javu s tim jezicima iz kojih je potekla. Trudiu se da ta poreenja budu jednostavna i objasniu sveto mislim da ne zna neko ko ne koristi C/C++.

Uvod

Uenje Jave
O tprilike u isto vreme kada se pojavila moja prva knjiga, Using C++ (Osborne/McGravvHill, 1989), poeo sam da predajem taj jezik. Predavanje program skih jezika postala je m oja profesija: gledao sam glave koje klimaju, bele poglee i zbunjene izraze u publici irom sveta jo od 1987. Kada sam poeo privatno da obuavam m anje grupe Ijudi, tokom vebi sam otkrio da mnoga pitanja zbunjuju ak i one koji su se smeili i klimali glavom. Vie godina predsedavao sam na odseku za C ++ na Konferenciji za razvoj softvera (kasnije i na odseku za Javu), i prim etio sam kako sm o i ja i drugi govornici sldoni da prosenoj publici serviramo p reV ie informacija za kratko vreme. Zbog razliitih nivoa publike i naina na koji sam predstavljao materijal, na kraju bih izgubio deo slualaca. M oda previe traim , ali poto oseam otpor prem a tradicionalnom nainu predavanja (a verujem da veina slualaca takav otpor osea zbog dosade), pokuao sam da uinim da m oja predavanja nikom e ne budu dosadna. Jedno vreme sam pravio dosta raznih prezentacija u relativno kratkom periodu. Tako sam doao u situaciju da uim m etodom pokuaja i pogreaka" (to je dobar nain i za projektovanje program a). Na kraju sam iskoristio sve to sam nauio kao predava- i napravio kurs koji bih rado drao due vreme. Sada ovaj kurs drim na javnim i privatnim sem inarim a o Javi; to je moj glavni uvodni seminar, koji prua osnovu za naprednije sem inare. O tim sem inarim a moete saznati vie na adresi www.MindView.net. (Uvodni sem inar je dostupan i u obliku CD-a Hands on Java. Podaci o njem u su na istoj Web lokaciji.) Povratne informacije koje dobijam na svakom sem inaru pom au mi da m enjam i preraujem materijal sve dok nc zakliuim da je podesan kao sredstvo pouavanja. Ovu knjigu ne ine samo beleke sa seminara: u nju sam pokuao da spakujem to vie inform acija i spojio sam ih tako da vas vode od jednog do drugog predmeta. Zamislio sam da ova knjiga slui usam ljenom itaocu koji se bori s novim program skim jezikom.

Ciljevi
Kao i moja prethodna knjiga, Misliti na jczikit C++, i ova knjiga je pisana u skladu s nainom na koji Ijudi ue jezik Java. Poglavlje u knjizi zamiljao sam kao jednu dobru lekciju na sem inaru. Povratne intormacije od slualaca na sem inarim a pom ogle su mi da otkrijem koji su delovi tei i zahtevaju dodatna objanjenja. U oblastim a gde sam bio pream biciozan i uvrstio previe novih pojmova odjednom , spoznao sam - kroz proces prikaza materijala - da vie novih pojmova zahteva vie objanjavanja, a to lako zbunjuje sluaoca. Cilj svakog poglavlja je da savladate jedan pojam , ili m anje grupe povezanih pojmova, bez obzira na sve ostalo to se tu pominje. Na taj nain moete da prihvatite svaki eli teorije, u zavisnosti od vaeg dotadanjeg znanja, pre nego to produite dalje.

10

Misliti na Javi

Ciljevi koje sam hteo da ostvarim u ovoj knjizi jesu: 1. Da predstavim materijal u jednostavnim koracima, tako da lako savladate svaki koncept pre nego to nastavite. Da paljivo odaberem redosled pojmova koje objanjavam, tako da ne naiete na neto s im se ranije niste susreli. Naravno, to nije uvek mogue; u takvim situacijama, dat je kratak uvodni opis. 2. Da koristim to jednostavnije i krae primere. To me ponekad spreava da se uhvatim u kotac sa ,,stvarnim problem im a, ali sam prim etio da su poetnici obino sreniji kada mogu da shvate svaki detalj prim era, i nisu im presionirani obim om problem a koji se reavaju. Takoe postoji ozbiljno ogranienje koliine koda koji m oe da se razum e u uionici. O ni kojima se to ne dopada, neka ga prihvate kao pedagoki ispravniji pristup. 3. Da pruim ono to sm atram bitnim za razumevanje jezika, a ne sve to znam. Verujem u hijerarhiju inform acija, kao i da postoje injenice koje 95% program era nikada nee imati potrebe da zna, a koje samo zbunjuju i poveavaju sloenost jezika. Da uzm em prim er iz C-a: ako zapam tite tabelu prioriteta operatora (ja nikada nisam), moete da piete efikasan, ali neitljiv kod. Ali ako malo promislite o takvom stilu pisanja, shvatiete da zbunjuje onoga ko ita/odrava kod. Stoga zaboravite na prioritete i koristite zagrade svuda gde stvari nisu potpuno jasne. 4. Da svaki odeljak bude ovoljno usredsreen, tako da vreme izlaganja i pauza izmeu iziaganja i vebe budu kratki. To sluaoca dri budnim i aktivnim tokom seminara, a itaocu prua oseanje da bre ovladava materijom . 5. Da vam dam vrst temelj, kako biste mogli dobro da razum ete teoriju i a sami nastavite da prouavate Javu.

Pouavanje na osnovu ove knjige


Prvo izdanje ove knjige nastalo je na osnovu jednonedeljnog seminara. Dok je Java bila u povojima, to je bilo dovoljno vremena za savladavanje ovog jezika. Kako je Java rasla i obuhvatala sve vie mogunosti i biblioteka, ja sam tvrdoglavo nastojao da sve to ispreajem za sedmicu dana. U jednom trenutku, neki naruilac je od mene zatraio da predajem sam o osnove, i radei to otkrio sam da je sabijanje svega u jednu sedmicu postalo muno i za mene i za polaznike. Java vie nije bila jednostavan" jc/.ik koji se moe ispredavati za nedelju dana. To iskustvo i spoznaja potakli su me da reorganizujem knjigu, koja je sada napisana tako da moe da se ispredaje za dve sedmice na sem inaru ili dva semestra na fakultetu. Uvodni deo se zavrava s poglavljem Obrada greakn pomou izuzetnkn , enut biste mogli da dodate uvod u JDBC, servlete i serverske Java stranice. To sainjava osnovni kurs i jezgro CD-a Hands-On Javn. O statak knjige obuhvata kurs srednjeg nivoa i nalazi se na CDu Intermedinte Thinking in Javu. O ba CD-a moete kupiti na w w w .M ind\r icw.nct. Ukoliko vam trebaju informacije o dodatnom materijalu za predavae, obratite se Prentice-Hallu na lokaciji www.prenludlprojessiouul.coin.

Uvod

11

Dokumentacija na Webu
Jezik Java i njegove biblioteke iz Sun Microsystemsa (besplatno se preuzim aju s Weba) im aju okum entaciju u elektronskom obliku, kojoj moete pristupati koristei ita Weba; gotovo svaka druga nezavisna distribucija Jave ima takav ili slian sistem dokum entacije. U veini knjiga o Javi kopira se taj sistem. Stoga takvu dokum entaciju ili ve imate, ili je m oete preuzeti s Weba, i ukoliko to zaista nije neophodno, u ovoj knjizi je neemo ponavljati. O bino m nogo bre moete da naete opis neke klase pom ou itaa Weba, nego da ga traite po knjizi (a dokum entacija na Webu je verovatno aurnija). Jednostavno u vas uputiti nadokum entaciju JDK-a. O vaknjiga e obezbediti dodatne opise klasa samo kada je neophodno dopuniti dokum entaciju da biste shvatili odredeni prim er.

Vebe
Prim etio sam da jednostavne vebe pom au studentim a da shvate seminarsko gradivo, pa dajem nekoliko vebi na kraju svakog poglavlja. Vei broj vebi moe relativno lako i u razum nom roku da se uradi u uionici, pod nadzorom instruktora koji proverava da li su svi studenti usvojili izloenu m ateriju. Neke vebe su malo izazovnije, a li nijedna nije nereiva. Reenja izabranih vebi nalaze se u elektronskom dokum entu pod im enom Thc Thinking iu java Annotatcd Solution Guide, koji se moe kupiti na lokaciji www.MindVicw.com.

Javini temelji
Sa ovom knjigom dobijate i besplatan m ultim edijalni sem inar koji moete preuzeti sa w w w .M in dV iew .L O in . To je sem inar T h i n k in g in C, koji vas uvodi u sintaksu, operatore i funkcije jezika C na osnovu kojih je Java napravljena. U prethodnim izdanjima ove knjige na engleskom, to se nalazilo na CD-u Foundations for Java, ali sada se moe besplatno preuzeti. Prvobitno sam od Chucka Allisona naruio da napravi Thinking in C k ao nezavisan proi/.vod, aii sam odluio da ga priloim uz drugo izdanje knjige Thinkingin C++ i drugo i tree izdanje knjige Thinking in Java, poto su mi na sem inar stalno dolazili Ijudi bez dovoljnog poznavanja osnovne sintakse C-a. Izgleda da takva osoba misli: ,,Ja sam pam etan program er i neu da uim C, nego C ++ ili Javu, pa u C preskoiti i odm ah prei na C + + / Javu. Po dolasku na seminar, ljui polako shvataju da sam imao veoma dobar razlog to sam poznavanje C-a proglasio za preduslov za pohaanje. Tehnologije se menjaju, i bilo je bolje preraditi Thinking in C u Flash prezentaciju koja se moe preuzeti sa Interneta, nego prilagati ga na CD-u. Poto je taj seminar na Internetu, svi mogu dobro da se priprem e prc nego to dou na moj seminar. Sem inar Thinking in C i ovoj knjizi pribavlja veu publiku. Iako njena poglavlja Operalori i Kontrolisanjc izvravanja obuhvataju osnove Jave koje potiu iz C-a, mreni seminar postepenije uvodi itaoca, a za njega je potrebno jo manje program erskog predznanja nego za knjigu.

12

Misliti na Javi

Izvorni kod
Kompletan izvorni kod iz ove knjige dostupan je kao slobodan softver zatien autorskim pravom, u jednom paketu, na adresi www.MindView.net. Ovo je zvanina Web lokacija za distribuciju koda i elektronskog izdanja knjige, pa m oete biti sigurni da ete tu uvek nai najnoviju verziju. Kod m oete deliti i koristiti u uionicam a i u druge obrazovne nam ene. Osnovni cilj zadravanja autorskog prava jeste da se poreklo koda ispravno navede i da se sprei objavljivanje koda u tam panim medijim a bez dozvole. (Ukoliko je poreklo koda ispravno navedeno, prim eri iz knjige m ogu se navoditi u veini medija.) U svakoj datoteci sa izvornim kodom nai ete sledeu napom enu o autorskim pravima (obino na engleskom):
//:! A u t o r s k a P r a v a . t x t Ovaj Inc. Sva pra v a zadrana. D o z v o l j a v a se b e s p l a t n a u p o t r e b a , ovog r a u n a r s k o g i z v o r n o g koda p r a v i m a , ovaj kopiranje, kod) prepravljanje i distribuiranje i njegove dokumentacije, u dole poruku o autorskim iz j e d n e rau n a r s k i iz vorni kod z a t i e n j e a u t o r s k i m p r a v i m a (c)2006 MindView,

(Izvorni

n a v e d e n e s v r h e i b ez p i s m e n o g o d o b r e n j a , n j e g o v e kopije. 1. D o z v o l j a v a formatu. 2. Neizmenjen Izvorni se p r e v o d e n j e Izvornog

ukoliko gornju

i s l e d e i h pet n u m e r i s a n i h p a s u s a ne u k l o n i t e ni

koda.

Preveden

Izvorni ali

kod s m e t e da samo u izvrnom

ukljuite u privatne i komercijalne softverske programe,

kod s m e t e da u p o t r e b l j a v a t e za p o t r e b e n a s t a v e u k o l i k o n a v e d e t e da j e p o t e k a o

i u

m a t e r i j a l i m a za p r e z e n t a c i ju, ,,Mi sl i ti na J a v i " . 3. D o z v o l u za k o r i e n j e

iz k n j i g e

I z v o r n o g ko da u t a m p a n i m m e d i j i m a m o e t e pr i b a v i t i

ako se o b r a t i t e na adresu: MindView, Inc. 5343 V a l l e V i s t a La M es a , C a l i f o r n i a 9 1 9 4 1 W a y n e @ M i n d V i e w . n e t Izvornog koda i dokumentacije. se ne d o b i j a g a r a n c i j u o proMindView, bez u vezi i kod raditi

4. Mi n d V i e w , Izvorni daji,

Inc. j e z a t i t i o a u t o r s k a p ra v a ili implicitna garancija, p r o g r a m koji Inc.

kod j e d o s t u p a n u o b l i k u u k o j e m j e d a t za o d r e e n u u p o t r e b u MindView,

i uz n j e g a

nikakva eksplicitna pogo d n o s t i Inc. ne jami prekida

ukljuujui Izvorni Izvorni

ili n e k r e n j u n e i j i h prava. sadri da j e snosi

da e b i l o koji

ili gre a k a .

ne tvrdi

kod ili b i l o koji riz i k , I z v o r n o g koda. istraivanja ga s a d r i . s v e tro-

s o f t v e r koji ga o b u h v a t a p o d e s a n za b i l o ko ju na m e n u . s kvalitetom i performansama ovog softvera, K o r i s n i k I z v o r n o g koda r a z u m e da j e Izvorni nasta v e , o s l a n j a i s k l j u i v o na A k o se ispostavi da j e k o v e s e r v i s i r a n j a , Izvorni Izvorni kod, niti

Celokupan

sam korisnik

kod n a p r a v l j e n radi

pa mu se s a v e t u j e da se ni u koju s vr h u niti na b i l o koji kod n e i s p r a v a n , isp ra vk i.

iz b i l o ko j e g r a z l o g a ne p r o g r a m koji

s a m k o r i s n i k snosi

o p r a v k e ili

Uvod

13

5.

NI U J E D N O M S L U A J U NI M I N D V I E W , BILO KOME, POSE B N U , POSLEDINU

INC.

NIT I

NJE60V

I Z D A V A N E E BITI ZA D I R E KT NU , ZA B I L O KA K A V P R E K I D ILI B I L O KA K A V DRUGI IZVO R-

ODGOVORNI

B EZ O B Z I R A N A B I L O K A K V U POSLOVNIH

PRAVNU TEORIJU,

INDIREKTNU, POSLOVANJA,

ILI S L U A J N U TE T U, PODATAKA,

G U B I T A K P R O F I T A ILI

N O V A N I G U B I T A K O D N O S N O T E L E S N U O Z L E D U K O J E SU I Z A Z V A N E U P O T R E B O M O V O G N OG K O D A I N J E G O V E D O K U M E N T A C I J E KO J E G R E Z U L T U J U E G PR O G R A M A , NIKAKVU GARANCIJU, A K I K A D A BI M I N D V I E W , MINDVIEW, SE NA TO. INC. INC.

ILI SU P O S L E D I C A N E M O G U N O S T I U P O T R E B E BI LO ILI N J E G O V I Z D A V A P O S E B N O NE D A J E I POGODNOSTI INC,

BILI U P O Z O R E N I N A M O G U N O S T T A K V I H T ETA . UKLJUUJUI ZA O D R E E N U U P O T R E B U ALI NE O G R A N I A V A J U I KOJI N E M A NITI AURIRANJA, M o lim, View.net preu z e t i

I I M P L I C I T N U G A R A N C I J U 0 PR O D A J I

IZ V O R N I KOD JE D O S T U P A N U PO DRKE,

O B L I K U U K O J E M J E D AT I UZ N J E G A SE NE D O B I J A N I K A K V A U S L U G A O D M I N D V I E W , P R E U Z I M A B I L O K A K V E O B A V E Z E ZA P R U A N J E U S L U G A , ILI M O D I F I K A C I J A . Inc. POBOLJANJA

i m a j t e u vidu da M i n d V i e w , koda,

odrava Web lokaciju http://www.MindJ e d i n o se o d a t l e m o g u pr eu ze ti e le k -

(i n j e n e z v a n i n e d u p l i k a t e ) . besp l a t n o .

tronske kopije Izvornog

k o j e se p o d p r e t h o d n o n a v e d e n i m u s l o v i m a m o g u

Ako m i s l i t e da s te u I z v o r n o m kodu p r o n a l i poaljete preko sistema povratnih www.MindView.com.

g r e k u , m o l i m v as da i s p r a v k u koji m o e t e pr onai na lo ka ci ji

informacija

///:Kod moete koristiti u svojim nastavnim projektim a (ukljuujui i vae materijale za prezentaciju), dokle god zadravate poruku o autorskim pravim a koja se pojavljuje u svakoj izvornoj datoteci.

Nain pisanja korien u knjizi


U ovoj knjizi identifikatori (metode, prom enljive i im ena klasa) ispisani su polucrno. Veina rezervisanih rei takode je napisana polucrno, osim onih koje se toliko koriste da bi njihovo naglaavanje postalo zam orno. Za pisanje prim era u knjizi koristim odreden stil. On odgovara stilu koji se koristi u kom paniji Sun za praktino sve program e koje moete nai na njihovoj Web Iokaciji (pogledajte http://jiiva.siin.coin/docs/codcconr/indcx.html) i koji podrava veina razvojnih okruenja za favu. Ako ste itali moje druge radove, prim etili ste takoe da se stil pisanja program a koji koristi Sun poudara s m ojim stilom. Ovo mi je drago, iako ja s tim e (koliko znam ) nisam imao nikakve veze. Pitanje stila moe biti predm et vieasovnih rasprava, pa u samo rei da kroz moje prim ere ne elim da nam eem pravilan stil, nego imam Iine razloge za korienje takvog stila. Poto je Java program ski jezik slobodne forme, moete da koristite bilo koji stil koji vam ogovara. Da biste form atiranje doterali kako vam odgovara i reili pitanje stila, moete se posluiti alatkom kao to je Jalopy (www.triem ax.com ) dok sam pisao knjigu, koristio sam je. Programi u ovoj knjizi ubaeni su u tekst direktno iz datoteka koje su prevoene i ispitivane na jednom autom atskom sistemu. Stoga bi izvorni kod tam pan u knjizi trebalo da radi bez greaka pri prevoenju.

14

Misliti na Javi

Ova knjiga se zasniva na Javi SE5/6, i na njoj je i testirana. Ako hoete da nauite neto o ranijim varijantama jezika, a to nije obuhvaeno ovim izdanjem, prvo, drugo i tree izdanje knjige (na engleskom) moete besplatno preuzeti s lokacije www.MindView.net.

Greke
Bez obzira na to koliko trikova pisac koristi da otkrije greke, neke se uvek provuku, a nov italac ih esto odm ah primeti. Ako otkrijete bilo ta to sm atrate grekom, m olim vas da iskoristite hipervezu za ovu knjigu na lokaciji www.MindView.net, prijavite greku i poaljete predlog za ispravku. Ceniu vau pomo.

Upoznavanje sa objektima
Prirodu parceliemo i razvrstavamo u koncepte kojima pripisujemo znaenja, uglavnom zato to se drimo dogovora koji vai u naoj jezikoj zajednici i kodifikovan je u obrascima naeg jczika ... uoptc nc moetnogovoriti ukoliko se nepridravamo organizacije i klasifikacije podataka koje taj dogovorpropisuje . Benjamin Lee W h o rf ( 1897-1941)
R a u n a r s k a REVOLUCIj'A
je

n a s t a l a u m a S in i. N a S i p r o g r a m s k i je z ic i s t o g a t e e d a

izgledaju kao ta maina. Ali raunari nisu toliko maine koliko pojaala um a (tokovi za um kako Steve Jobs voli da kae) i drugi nain izraavanja. Zbog toga ti alati sve m anje lie na maine, a sve vie na delove naeg uma, i na druge oblike izraavanja, kakvi su pisanje, slikanje, vajanje, anim iranje i snim anje filmova. O bjektno orijentisano program iranje (OOP) deo je ovog pom eranja ka korienju raunara kao sredstva izraavanja. Ovo poglavlje vas uvodi u osnovne koncepte OOP-a, ukljuivi i pregled m etoda razvoja, uz pretpostavku da imate iskustva s program iranjem , iako to ne m ora biti na jeziku C. Ako mislite da treba da se jo priprem ate za program iranje pre nego to se uhvatite u kotac sa ovom knjigom, prouite multimedijski sem inar Thinking in C, koji moete preuzeti na adresi mvw.MitidView.net. Ovo pogtavlje je i podloga i dodatni materijal. M nogi se ne oseaju prijatno u svetu ol>iektno orijentisanog program iranja ako pre toga ne razum eju celinu. Stoga je ovde dat bogat pregled koncepata OOP-a. Drugi ne mogu da shvate glavne principe dok prvo ne upoznaju barem neke mehanizme. Ako pripadate toj grupi i etjni ste da otkrijete specifinosti jezika, slobodno preskoite ovo poglavlje - u ovom trenutku to vas nee spreiti da piete program e ili nauite jezik. M eutim, poeleete da se vratite na ovaj deo knjige da biste dopunili svoje znanje i shvatili zato su objekti vani i kako da ih koristite pri pisanju programa.

Razvoj apstrakcije
Svi programski jezici obezbeuju apstrakoiju. Moglo bi se raspravljati o tom e da li je sloenost problema koje ste u stanju da reite direktno povezana s vrstom i kvalitetom apstrakcije. Pod vrstom" mislim na to ta apstrahujete. Mainski jezik je mala apstrakcija maine na kojoj se programi izvravaju. Mnogi takozvani ,,proceduralni jezici koji su sledili mainski (kao FORTRAN, RASIC i C) bili su apstrakcija mainskog jezika. Ovi jezici su veliki napredak u odnosu na mainski jezik, ali njihova prim arna apstrakcija ipak zahteva od vas da razmiljate iz ugla strukture raunara um esto iz ugla probiem a koji reavate. Program er mora da uspostavi vezu izmedu modela tnaine (u prostoru reenja koji predstavlja mesto gde realizujete reenje problem a, na prim er, u raunaru) i modela problem a koji se reava (u prostoru problema" koji predstavlja mesto gde problem postoji, recimo poslovanje). Napor koji iziskuje ovo preslikavanje i injenica da je ono nebitno za programski jezik proizvode program e koji se teko piu i ije je odravanje skupo, a kao sporedni elekat nastaje celokupna industrija program skih metoda".

16

Misliti na Javi

Alternativa m odelovanju m aine je m odelovanje problem a koji pokuavate da reite. Rani jezici kao LISP i APL odraavali su pojedine predstave o svetu (Svi problem i se na kraju svode na liste ili Svi problem i su algoritamske prirode). Prolog prebacuje sve problem e u korake odluivanja. Stvoreni su jezici zasnovani na ogranienom program iranju i program iranju iskljuivo m anipulacijom grafikim sim bolim a (to se pokazalo kao previe restriktivno). Svaki ovaj p ristu p moe biti dobro reenje za odreenu klasu problem a kojoj su nam enjeni, ali kada istupite iz tog dom ena, oni postaju nezgrapni. O bjektno orijentisani pristup ide korak dalje, obezbeujui alate pom ou kojih program er predstavlja elemente u prostoru problem a. Ovo predstavljanje u principu ne ograniava program era na jednu vrstu problem a. Elemente u prostoru problem a i njihovo predstavljanje u prostoru reenja nazivamo ,,objekti. (Trebae vam i drugi objekti koji nemaju svoj par u prostoru problem a.) Ideja je da se program u dozvoli da se prilagodi nerazumljivom jeziku problem a tako to e se dodati novi tipovi objekata, te kada itate kod koji opisuje reenje, u isto vreme itate i rei koje izraavaju problem . Ovo je mnogo fleksibilnija i snanija apstrakcija od pretho d ne.1Stoga O O P dozvoljava da opiete problem iz ugla problem a, um esto iz ugla raunara na kom e e se to reenje izvravati. lo uvek postoji povratna veza ka raunaru: svaki objekat izgleda posve kao mali raunar - on ima unutranje stanje i operacije koje m oete zahtevati da izvri. Meutim, ovo i nije tako loa analogija sa objektima u stvarnom svetu - svi im aju svoje karakteristike i osobeno se ponaaju. AJan Kay je naveo pet osnovnih obeleja Smalltalka, prvog uspenog objektno orijentisanog jezika, i jednog od jezika na kom e je Java zasnovana. Ta obeleja predstavljaju ist i neiskvaren pristup objektno orijentisanom program iranju. 1. Sve je objekat. Posmatrajte objekat kao poboljanu promenljivu; on uva podatke, ali moete i da mu postavite zahteve koje ispunjava vrei operacije nad tim podacima. Teoretski, moete uzeti bilo koju idejnu kom ponentu problem a koji reavate (pse, zgrade, usluge itd.) i predstaviti je kao objekat u svom program u. 2. P rogram je skup objekata koji jed ni d ru g im a poru k am a saoptavaju ta da rade. Da biste uputili zahtev objektu, vi aljete poruku tom objektu. Konkretnije, moete zamisliti da je poruka zahtev da se pozove m etoda koja pripada odreenom objektu. 3. Svaki objekat im a svoj m em orijski p ro sto r koji se sastoji od d ru g ih objekata. Drugaije reeno, vi stvarate novu vrstu objekta pravei paket koji sadri neke postojee objekte. Stoga moete da uslonjavate program koji e biti skriven iza jednostavnih objekata. 4. Svaki objekat ima tip. Struno reeno, svaki objekat je instanca (primcrak) neke klase, pri em u su ,,klasa i ,,tip sinonim i. Najvanija odlika klase glasi: Koje poruke joj moete poslati?

N eki a u to ri p r o g r a m s k ih jezika su s m a tra li d a o b je k tn o o rije n tis a n o p ro g r a n iira n jc n ije d o v o ljn o da o m o g u i lak o re a v a n je sv ih p ro g r a m s k ih p ro b le m a , p a su p o d r a v a liu k o m b in o v a n je ra z li itih p ris tu p a k ro z m u ltis ta n a r d m ' p ro g ra m s k e jezike. P o g le d a jte M iiltipiiratligm lro g rtunm ing in Lctlti, Tim o th y B u d d (A d d iso n -W e sle y 1995).

Poglavlje !: Upoznavanje sa objektima

17

5. Svi objekti odreenog tipa m ogu da primaju iste poruke. Ovo je, u stvari, vieznana izjava, kao to ete kasnije videti. Kako je objekat tipa ,,krug istovremeno objekat tipa ,,oblik, krug e zasigurno moi da prim a poruke za oblik. To znai da moete da napiete kod koji kom unicira sa oblicima i autom atski podrava i sve drugo to potpada pod opis oblika. Ova zamenljivost je jedna od najm onijih osobina OOP-a. Booch daje jo krau definiciju objekta: O bjekat im a stanje, ponaanje i identitet. To znai da objekat m oe imati interne podatke (koji definiu njegovo stanje) i m etode (koje definiu njegovo ponaanje), i da je svaki objekat jedinstven (razlikuje se od svih drugih objekata) - konkretno, svaki objekat im a jedinstvenu adresu u m em oriji.2

Objekat ima interfejs


Aristotel je verovatno prvi poeo da paljivo prouava podelu na tipove - govorio je o klasi riba i klasi ptica. Ideja da svi objekti, prem da jedinstveni, istovrem eno pripadaju klasi objekata sa zajednikim karakteristikam a i ponaanjem , direktno je iskoriena u prvom objektno orijentisanom jeziku, Simula-67. Njegova osnovna rezervisana re class uvodi novi tip u program . Simula, kao to joj im e govori, projektovana je za razvoj simulacija poput klasinog problem a bankarskog blagajnika". U tom problem u imate vie blagajnika, klijenata, rauna, transakcija i novanih jedinica - puno ,,objekata. Objekti koji su identini po svemu osim po stanju tokom izvrenja program a, grupisani su uklase objekata" i odatle potie rezervisana re class. Stvaranje apstraktnih tipova podataka (klasa) osnovna je ideja u objektno orijentisanom program iranju. Apstraktni tipovi podataka rade gotovo isto kao ugraeni tipovi: moete stvarati promenljive datog tipa (koje se nazivaju objekti ili instance u term inologiji objektno orijentisanog program iranja) i raditi s tim promenljivama (to se naziva slanje poruka ili zahteva: vi poaljete poruku a objekat sam odredi ta e s njom da uradi). lanovi (elementi) svake klase imaju neke zajednike osobine: svaki raun ima saldo, svaki blagajnik moe da prim i depozit itd. Istovremeno, svaki lan ima vlastito stanje: svaki raun ima drugaiji salo, svaki blagajnik ima ime. Stoga blagajnici, klijenti, rauni, transakcije itd., pojedinano mogu biti predstavljeni jedinstvenim entitetom u raunarskom program u. Ti entiteti su objekti, a svaki objekat pripada odreenoj klasi koja definie njegove karakteristike i ponaanje. Dakle, iako mi u objektno orijentisanom program iranju stvaram o nove tipove podataka, svi objektno orijentisani program ski jezici koriste rezervisanu re,,class. Kada vidite re ,,tip, pomislite na ,,klasu i obrnuto.'

O v o jc z a p ra v o p rcv ic usko, p o to o b jc k ti m o g u p o sto ja ti u ra z n im r a u n a r im a i a d re s n im p ro s to rim a , a m o g u b iti s n im lje n i i na ciisk. U tim slu a jc v im a , id e n tite t o b je k ta se m o ra u tv rd iti na neki d ru g i n a in , a n c p o m o u n jegove a d rc se u m e m o riji. N ek i Ijudi p rav e ra z lik u i tv rd e d a tip o d re d u je in te rfejs, d o k jc k lasa p o s e b n a realizacija to g in te rfejsa .

18

Misliti na Javi

Poto klasa opisuje skup objekata koji imaju identine karakteristike (elementi s podacima) i jednako se ponaaju (funkcionalnost), klasa ie zaista tip podatka, jer i broj u form atu pokretnog zareza, na prim er, takoe im a skup karakteristika i ponaanja. Razlika je u tom e to program er definie klasu koja odgovara problem u, um esto da bude prinuen da koristi postojei tip koji predstavlja m em orijsku jedinicu unutar raunara. Programski jezik proirujete dodavanjem novih tipova podatka, specifinih za vae potrebe. Programski sistem prihvata nove klase, o njim a vodi rauna i proverava tipove, kao to radi i sa ugraenim tipovima. O bjektno orijentisan pristup ne ograniava se sam o na pravljenje simulacija. Bez obzira na to da li se slaete sa stavom da je svaki program simulacija sistema koji projektujete, upotrebom tehnika, O O P-a veliki skup problem a lako se moe da svesti na jednostavna reenja. Kada se klasa ustanovi, moete da napravite koliko god elite objekata te klase, a zatim da radite s tim objektim a kao da su elementi u problem a koji pokuavate da reite. Zaista, jedan od izazova objektno orijentisanog program iranja jeste ostvarivanje jednoznanog preslikavanja izm eu elemenata u prostoru problem a i objekata u prostoru reenja. Kako da naterate objekat da uradi koristan posao? M ora postojati nain da postavite zahtev objektu da neto uradi, na prim er, da zavri transakciju, iscrta neto na ekranu ili ukljui prekida. A svaki objekat moe da zadovolji samo izvesne zahteve. Zahtevi koje moete da postavite objektu efinisani su preko njegovog interfcjsa, koji je odreen tipom . Prost prim er bi mogla biti sijalica:

Ime tipa

Interfejs

S i j a l i c a sj = n ew S i j a l i c a ( ) ; sj .ukl juci ();

Interfejs odreduje koje zahteve moete da postavite odredenom objektu. Naravno, ne gde m ora postojati kod koji e zaovoljiti taj zahtev. 'Iaj kod, zajedno sa skrivenim podacima, ini implementaciju (realizaciju ili prim enu interfejsa). S take gleita proceduralnog program iranja to i nije tako komplikovano. Tip uz svaki mogui zahtev ima pridruenu m etodu i kada napravite oreeni zahtev, poziva se odgovarajua metoda. Za ovaj proces obino se kae da sm o poslali poruku" (postavili zahtev) objektu, a objekat odreduje ta e s porukom da uradi (izvri kod). U naem prim eru, ime tipa/klase je Sijalica, ime posebnog objekta tipa Sijalica je sj, a zahtevi koje m oem o postaviti objektu Sijalica su ukljui se, iskljui se, pojaaj svetlo ili prigui svetlo. Objekat tipa Sijalica pravite kada definiete njereniu (sj) na dati objekat i

Poglavlje I : Upoznavanje sa objektima

19

pozovete nevv, ime zahtevate novi objekat tog tipa. Da biste poslali poruku objektu, navedite ime objekta i poveite ga s porukom - zahtevom, razdvajajui ih takom. S take gledita korisnika unapred definisane klase, to je skoro sve to vam treba da biste program irali sa objektima. Gornja slika je napravljena u skladu s form atom koji koristi unifikovani jezik za modelovanje (engl. Unified Modeling Languagc, UML). Svaka klasa je predstavljena pravougaonikom , ime tipa je u gornjem delu, podaci lanovi koje treba opisati nalaze se u srednjem delu, a u donjem delu pravougaonika su metode (funkcije koje pripadaju tom objektu, koje prim aju poruke koje aljete tom objektu). esto se u UML dijagramima prikazuju sam o im e klase i javne m etode, a srednji deo ne tako je i na prethodnoj slici. Ako vas zanim a sam o ime klase, nem a potrebe da prikazujete ni donji deo.

Objekat prua usluge


D ok pokuavate da razvijete ili shvatite stru kturu nekog program a, bilo bi dobro da objekte sm atrate davaocima usluga. I va program e pruati usluge korisniku, i to pom ou usluga koje pruaju drugi objekti. Va cilj je da napravite (ili jo bolje, pronaete u bibliotekam a koda) skup objekata koji pruaju idealne usluge koje reavaju va problem. To biste mogli postii ako se zapitate: ,,Da ih mogu arobnim tapiem izvui iz eira, koji objekti bi odm ah reili moj problem?" Na primer, pretpostavim o da piete program za knjigovodstvo. Mogli biste zamisliti oreene objekte koji sadre unapred definisane slike ekrana za unos podataka, drugi skup objekata koji obavljaju knjigovodstvene proraune, i objekat koji tam pa ekove i raune na svirn moguim vrstam a tampaa. Neki od tih objekata m oda ve postoje? Kako bi izgledali oni koji ne postoje? Koje usluge bi ti objekti davali i koji bi im objekti bili potrebni za izvravanje zadataka? Ako tako budete radili, doi ete do take kada moete rei: Ovaj objekat izgleda dovoljno jednostavno da se moe napisati ili Mora da postoji ovakav objekat. To je racionalan nain razlaganja problem a na skup objekata. Kada se objekat sm atra davaocem usluga, stie se jo jedna prednost: time se poveava usklaenost objekta. Velika u$kladenost\e jedan od temeljnih kvaliteta projektovanja softvera: to /.nai da su razni aspekti softverske kom ponente (kao to je objekat, iako isto vai i za m ctodu iii biblioteku objekata) m edusobno dobro uklopIjeni. Prilikom projektovanja objekata, program eri esto trpaju previe funkcionalnosti u jedan objekat. Kada se radi o pom enutom m odulu za tam panje eko\ra, moete odluiti da vam treba objekat koji zna sve o form atiranju i tam panju. Verovatno e vas iskustvo nauiti da je to previe za jedan objekat i da vam treba tri ili vieobjekata. Jedan olijekat bi mogao biti katalog svih moguih izgleda ekova, kojem se mogu slati upiti radi podataka o tom e kako odtam pati odredeni ek. Jedan objekat ili skup objekata moe biti opti (generiki) interfejs za tam panje, koji zna sve o raznim vrstam a tampaa (ali nita o knjigovodstvu - taj je kandidat za kupovinu, um esto da ga sami piete). A trei objekat moe koristiti usluge prethodna dva da bi obavio posao. Tako bi svaki objekat imao uskladen skup usluga koje prua. U dobrom objektno orijentisanom dizajnu, svaki objekat dobro radi jedan posao, ali ne pokuava da radi vie poslova. Ne samo da se tako mogu pronai objekti koje treba kupiti (recimo, objekat interfejsa tampaa), nego se proizvode i novi objekti koji se m ogu ponovo upotrebljavati na drugim mestima (katalog izgleda ekova).

20

Misliti na Javi

Jako ete pojednostaviti sebi ivot ako objekte budete sm atrali davaocima usluga. To e koristiti ne sam o vama tokom procesa projektovanja, nego i drugim a koji b u d u pokuavali da razum eju va kod ili upotrebe neki od vaih objekata. Ako b u d u mogli da utvrde vrednost objekta na osnovu usluge koje on prua, bie im m nogo lake da ga uklope u svoje projekte.

Skrivena realizacija
Vrlo je korisno podeliti polje delatnosti na autore klasa (one koji prave nove tipove podataka) i programere klijente4 (korisnike klasa koji te tipove upotrebljavaju u svojim aplikacijama). Cilj program era kJijenta je da sakupi klase u kutiju sa alatom", koju e koristiti za brzi razvoj aplikacija. Cilj autora klasa je da napravi klasu koja otkriva sam o ono to je neophodno program eru klijentu, a sve ostalo dri sakriveno. Zato? P rogram er Idijent ne moe da koristi sakrivene delove, to znai da autor klase m oe da prom eni skriveni deo kad god hoe, ne razmiljajui da li e se to odraziti na ostale. Skriveni deo obino predstavlja osetljivu unutranjost objekta koju lako moe da oteti neobazriv ili neobaveten program er ldijent, pa sakrivanje realizacije sm anjuje m ogunost da se pojave greke u program im a. U svakom odnosu je vano da sve ukljuene strane potuju granice. Kada pravite biblioteku, vi stvarate odnos sa kJijentom koji je takoe program er, ali koji sastavlja aplikaciju koristei vau biblioteku, da bi, moda, napravio veu biblioteku. Ako bi svi lanovi klase bili dostupni svakome, onda bi program er klijent mogao da uradi bilo ta s klasom i ne bi postojao nain da se nam etnu pravila. Jako biste vi voleli da program er Jdijent ne radi direktno s nelcim lanicama vae klase, bez kontrole pristupa ne bi postojao nain da to spreite. Sve bi bilo izloeno javnosti. Stoga je prvi razlog za uvoenje kontrole pristupa onem oguavanje program era klijenta da pristupa delovima koje ne sme da dira, a koji su neophodni za interni rad s tipom podataka. Ti delovi nisu deo interfejsa koji je potreban korisnicim a za reavanje njihovih problem a. Ovo je u isto vreme usluga program erim a klijentima, jer lako rnogu da razlue ta je za njih vano, a o emu ne treba da misle. Kontrolu pristupa treba uvesti i da bi se dozvolilo projektantu biblioteke da prom eni nain na koji klasa interno radi, a da ne m ora da brine kako e se to odraziti na programere klijente. Na primer, moete realizovati neku klasu na jednostavan nain, a kasnije otkriti da je treba ponovo napisati kako bi radila bre. Ako su interfejs i realizacija jasno razdvojeni i zatieni, ovo moete lako da izvedete. Java koristi tri rezervisane rei da postavi granice un u tar klase: public, private i protected. Ovi specifikatoripristupa odreuju ko moe da koristi definicije koje slede iza njih. public (javni) znai da je naredni elem ent dostupan svakome. Rezervisana re private (privatni) znai da tom elementu ne moe da pristupi niko sem autora klase, unutar me toda tog tipa. private predstavlja zid izmeu autora i program era klijenta. Onaj ko poku a da pristupi lanici oznaenoj kao private, izazvae greku prilikom prevodenja.
' Z a h v a lju je m sv o m p rija te lju S c o ttu M ey ersu n a o v o m te rm in u .

Poglavlje I : Upoznavanje sa objektima

21

Rezervisana re protected (zatieni) deluje kao private, s tom razlikom to klase naslednice im aju pristup zatienim lanicama, ali ne i privatnim lanicama. O nasleivanju emo govoriti uskoro. Java ima i ,,podrazum evani pristup koji se koristi ako ne zadate nijedan od navedenih specifikatora. Ovo se obino naziva paketskipristup, jer klase m ogu da pristupe lanicama drugih klasa u istom paketu, ali izvan tog paketa te iste lanice vide se kao privatne.

Ponovno korienje realizacije


Kada se klasa napravi i testira, ona bi (u idealnom sluaju) trebalo da predstavlja korisnu jedinicu koda. Ispostavlja se da ponovno korienje ni izbliza nije tako lako ostvarljivo kako se mnogi nadaju; potrebno je iskustvo i pronicljivost da bi se napisao viekratno upotrebljiv objekat. Ali kada ga napiete, on moli da bude ponovo iskorien. Ponovno korienje koda je jedna od najveih prednosti objektno orijentisanih program skih jezika. Najjednostavniji nain da ponovo iskoristite klasu jeste da direktno koristite objekat te klase; m eutim , objekat te klase moete da stavite i u novu klasu. Ovo se naziva pravljenje objekta lana (engl. member object). Vaa nova klasa m oe biti sastavljena od m a koiiko drugih objekata, ma kojeg tipa i u bilo kojoj kombinaciji koja vam je potrebna da biste postigli eljenu funkcionalnost svoje nove klase. Novu klasu sastavljate od postojeih klasa, to se naziva kompozicija (ako se deava dinam iki, onda obino agregacija). Kompozicija se esto poredi s relacijom ,,ima, na p rim erau to im a m o to r.

(Na ovom UML dijagram u kompoziciju oznaava popunjen romb, koji kazuje da postoji jedan auto. Kad oznaavam spajanje, obino u koristiti jednostavniji oblik: sam o liniju, bez rom ba.)1 Kompozicija je vrlo fleksibilna. Objekti lanovi vae nove klase obino su privatni, to ih ini nedostupnim program erim a Idijentima koji koriste klasu. Ovo omoguava da izmenite te lanove ne remetei postojei klijentski kod. Moete m enjati objekte lanove i u vreme izvravanja da biste dinamiki menjali ponaanje svog program a. Nasleivanje koje malo dalje opisujemo, nem a ovu fleksibilnost poto prevodilac m ora da ugradi ogranienja za prevoenje u klase stvorene nasleivanjem. Poto je nasledivanje veoma bitno, u objektno orijentisanom program iranju esto se veoma naglaava, i nov program er moe pom isliti da nasleivanje treba koristiti svuda. Kaii rezultat moe se dobiti veoma nezgrapan i veoma sloen program . Umesto toga, prvo treba videti da li pri pravljenju novih klasa moe da se iskoristi kompozicija, poto je ona jednostavnija i fleksibilnija. Ako prim enite ovaj pristup, program e biti istiji. Kada steknete neto iskustva, bie vam oigledno kada treba da koristite nasleivanje.
O v o jc u p r in p u d o v o ljn o d e ta ljn o za v e in u d ija g ra m a i n e tre b a d a p re c iz ira te d a li k o ris tite a g re g a ju ili k o m p o /ic iju

22

Misliti na Javi

Nasleivanje
Sam po sebi, koncept objekta je veoma koristan. On omoguava da podatke i funkcionalnost grupiete po konceptu , tako da moete prestaviti odgovarajuu ideju u prostoru problem a, um esto da budete prinueni da koristite izraze raunara na kome radite. Kada se prim eni rezervisana re class, te ideje su izraene kao osnovne jedinice u ovom program skom jeziku. Bila bi teta da se nam uite i napravite neku klasu a da onda budete prim orani da pravite p otpuno novu klasu koja im a slinu funkcionalnost. Bilo bi jednostavnije da uzmem o postojeu klasu, da je kloniram o, a zatim dopunjujem o i m enjam o kloniranu klasu. Ovo se zapravo postie nasleivanjem (engl. inheritance ), osim kada se originalna klasa (koja se naziva osnovna klasa ili natklasa ili klasa roditelj) izmeni, a te izmene se odraze i na ,,klonu (koji se naziva izvedena ili nasleena klasa ili potklasa ili klasa naslednik ; engl. derived class).

(Strelica u ovom UML dijagram u polazi od izvedene klase ka osnovnoj klasi. Kao to ete videti, moe da postoji vie izvedenih klasa.) Tip ne oznaava samo ogranienja skupa objekata; on ima odnose i s drugim tipovima. Dva tipa m ogu imati neke zajednike karakteristike i jednako se ponaati, ali pri tom jedan tip moe imati jo neke dodatne karakteristike i moe obraivati vie poruka (ili ih drugaije obradivati). Kod nasleivanja, ova slinost izmeu tipova se izraava preko koncepta osnovnih i izvedenih tipova. Osnovni tip sadri sve karakteristike i ponaanja koja su zajednika za tipove izvedene iz njega. Pravei osnovni tip, izraavate sutinu svojih ideja o nekim objektim a u sistemu. Iz osnovnog tipa izvodite ostale tipove i pokazujete razliite naine realizacije ove sutine. Na prim er, maina za recikliranje rasporeuje komadie otpada. Osnovni tip je ,,otpad a svaki pojedini otpadak ima teinu, vrednost itd. i moe biti iseen, istopljen ili rastavljen. Iz osnovnog tipa izvodimo posebne vrste otpada s dodatnim karakteristikama (boca im a boju), ili ponaanjim a (alum inijum ska konzerva moe da se zdrobi, eline konzerve privlai magnet). Ponaanja mogu biti razliita (vrednost papira zavisi od njegove vrste i stanja). Koristei nasledivanje moete izgracliti hijerarhiju tipova. Ta hijerarhija opisuje problem koji pokuavate da reile preko tipova koji se u problem u pojavljuju. Drugi prim er je klasian ,,oblik koji moe da se koristi u CAD sistemu (engi. Computer-Aided Design) ili u nekoj kom pjuterskoj igri. Osnovni tip je ,,oblik a svaki oblik ima veliinu, boju, poziciju itd. Svaki oblik moe da se iscrta, obrie, pom era, oboji itd. Odavde izvodimo (nasledujem o) pojedine vrste oblika - krug, kvadrat, trougao i druge - od

Poglavjje 1: Upoznavanje sa objektima

23

kojih svaki moe imati dodatne karakteristike i ponaanje. Neki oblici, na prim er, m ogu da se okreu. Neka ponaanja m ogu biti drugaija, recimo kada elite da izraunate povrinu oblika. H ijerarhija tipova objedinjuje slinosti i razlike izmeu oblika.

Predstavljanje reenja i problem a u istom obliku veoma je korisno jer vam ne treba veliki broj m eum odela da biste od opisa problem a doli do opisa reenja. Kod objekata, hijerarhija tipova je osnovni model tako da sa opisa sistema u realnom svetu direktno prelazite na opis sistema kodom. Jedna od tekoa sa objektno orijentisanim program iranjem jeste ta da je previe jednostavno stii od poetka od kraja. Um treniran da trai sloena reenja esto spoetka zbunjuje ova jednostavnost. Kada iskoristite nasleivanje iz postojeeg tipa, stvarate novi tip. Taj novi tip ne samo da sari sve lanice postojeeg tipa (iako su privatne lanice sakrivene i neostupne), ve, to je mnogo vanije, kopira interfejs osnovne klase. Znai, sve poruke koje moete da poaljete objektima osnovne klase, takoe moete poslati i objektim a izvedene klase. Poto se tip klase odreduje na osnovu poruka koje joj m oem o poslati, to znai da je izvedena klasa istog tipa kao i osnovna klasa. U prethodnom prim eru krug je oblik. Ekvivalencija tipova dobijena nasledivanjem je osnovni korak na p utu ka razum evanju smisla objektno orijentisanog program iranja. Poto i osnovna klasa i izvedena klasa imaju isti osnovni interfejs, m ora da postoji realizacija koja ide uz taj interfejs. Znai, mora postojati kod koji se izvrava kada objekat prim i odreenu poruku. Ako nasledite klasu i nita vie ne uradite, m etode interfejsa osnovne klase e prei i u izvedenu klasu. To znai da objekti izvedene klase imaju i isti tip i isto ponaanje, to i nije previe korisno. Postoje dva naina da napravite raziiku izmeu svoje nove izvedene klase i osnovne klase. Prvi je jasan: izvedenoj klasi dodate potpuno nove metode. Te nove m etode nisu deo interfejsa osnovne klase, to znai da osnovna klasa nije radila sve ono to ste hteli, pa ste joj zato dodali jo m etoda. Ovaj jednostavan nain korienja nasledivanja ponekad je savreno reenje problem a. M edutim, paljivo prouite da li su i vaoj osnovnoj klasi potrebne te dodatne metode. Ovaj proces otkrivanja i iteracije program a redovna je pojava u objektno orijentisanom program iranju.

24

Misliti na Javi

Iako nasleivanje ponekad moe da nagovesti (posebno u Javi, gde rezervisana re koja oznaava nasledivanje glasi extends - proiruie) kako ete doavati nove m etode interfejsu, to nije uvek istina. Drugi i vaniji nain da napravite razliku u novoj klasi jeste da promcnite ponaanje postojee m etode osnovne klase. To se naziva redefinisanje metode (engl. overriding).

Da biste redefinisali m etodu, napravite novu definiciju te m etode u izvedenoj kiasi. Vi kaete: Koristim ovde istu m etodu interfejsa, ali elim da ona u m om novom tipu radi neto drugo.1 '

Poglav[je 1: Upoznavanje sa objektima

25

Relacije JE i JE-KAO
Moe se postaviti jedno pitanje o nasledivanju: ,,Da li nasleivanje treba da redefinie samo m etode osnovne klase (i da ne dodaje nove m etode koje ne postoje u osnovnoj klasi)? To bi znailo da je izvedena klasa potpimo istog tipa kao osnovna klasa, jer ima istovetan interfejs. Kao rezultat, objekat izvedene klase moe se svesti na tip objekta osnovne klase. Ovo je tzv. ista supstitucija , ili princip supstitucije. Unekoliko, ovo je idealan nain prim ene nasleivanja. U ovom sluaju, m i se esto pozivamo na relaciju izm eu osnovne i izvedene klasekao na relaciju je, jer m oem o reikrug;eoblik. Pokuaj da utvrdim o da li m oem o da uspostavim o relaciju je izm edu klasa, a da to ima smisla, predstavlja svojevrstan test nasleivanja. Ponekad m orate da dodate nove elemente u interfejs izvedenog tipa i tim e proirite postojei interfejs. Novi tip i dalje m oe biti sveden na osnovni tip, ali supstituja nije savrena, jer nove m etode nisu dostupne iz osnovnog tipa. Ovo se moe opisati kao relacija je-kao (moj term in). Novi tip ima interfejs starog tipa ali sadri i druge m etode, tako da se ne moe rei kako je potpuno isti. Uzmimo za prim er ureaj za klimatizaciju. Pretpostavim o da su po kui razvedene kontrole za hlaenje, odnosno, kua im a interfejs koji om oguava da kontroliete hlaenje. Zamislite da se ureaj za klimatizaciju pokvari i da ga zam enite toplotnom pum pom koja moe i da greje i da hladi. Toplotna pum pa je kao ureaj za hlaenje, ali ona moe i vie. Poto je kontrolni sistem vae kue projektovan sam o da kontrolie hlaenje, on je ogranien na kom unikaciju s delom za hlaenje novog objekta. Interfejs novog objekta je proiren, a postojei sistem ne poznaje nita drugo osim originalnog interfejsa.

Cim prouite ovaj plan, postae jasno da osnovna klasa sistem za hlaenje" nije dovoljno opta i da treba da se preim enuje u sistem za kontrolu temperature", kako bi m ogla da sadri i grejanje - nakon ega bi princip supstitucije vaio. Ovaj dijagram prikazuje ta u stvarnom svetu moe da se desi tokom projektovanja. Kada upoznate princip supstitucije, lako moete pomisliti da je taj pristup (ista supstitucija) jedini nain da se neto uradi, i zaista jeste dobro da program napravite na taj nain. Ali shvatiete da u interfejs izvedene klase ponekad m orate da dodate nove metode. Nakon to ispitate problem , trebalo bi da bude prilino oigledno o kojem se od ta dva sluaja radi.

26

Misliti na Javi

Virtuelizacija objekata preko polimorfizma


Ukoliko imate posla s hijerarhijom tipova, esto objekat ne tretirate kao specifian tip, ve kao osnovni tip. To vam omoguava da piete kod koji ne zavisi od specifinog tipa. U prethodnom prim eru sa oblicima, m etode m anipuliu generikim tipovim a (oblicima) bez obzira na to da li se radi o krugovima, kvadratima, trouglovim a ili ak oblim a koji jo nisu definisani. Svi oblici mogu biti iscrtani, obrisani i pom erani, tako da te m etode samo poalju poruku objektu tipa oblik i ne brinu kako e objekat izai na kraj s tom porukom. Takav kod se ne menja ni nakon dodavanja novih tipova, a dodavanjem novih tipova najee se proiruju objektno orijentisani program i kada treba da se savlada neka nova situacija. Na primer, moete da izvedete novi podtip oblika, pod im enom petougao, bez menjanja m etoda koje rade samo s generikim oblicima. M ogunost da se program lako proiri izvoenjem novih podtipova jeste jedan od osnovnih naina da se kapsuliraju promene. Time se znatno unapreuje program i istovrem eno sm anjuju trokovi odravanja softvera. Problem nastaje pri pokuaju da se objekti izvedenog tipa tretiraju kao generiki osnovni tipovi (krugovi kao oblici, bicikli kao vozila, korm orani kao ptice itd.). Ako metoda naredi generikom obliku da se iscrta ili generikom vozilu da sm ota volan ili generikoj ptici da se pom eri, prevodilac prilikom prevoenja ne moe tano da zna koji deo koda e biti izvren. To i jeste poenta - kada je poruka poslata, program er ne eli da zna koji deo koda e biti izvren. M etoda za crtanje moe podjednako da se prim eni na krug ili na kvadrat ili na trougao, a objekat e izvriti odgovarajui ko u zavisnosti od svog specifinog tipa. Ako ne m orate znati koji e deo koda biti izvren, dodajte nov podtip; kod koji e taj novi tip izvravati moe biti drugaiji a da pri tom ne m crate nita da menjate u metodi koja ga poziva. Znai, prevodilac ne zna tano koji e deo koda biti izvren. ta onda radi? Na primer, u sledeem dijagram u objekat K o ntrolerP tica radi samo s generikim objektim a tipa Ptica i ne zna kog su oni tipa. Ovo je pogodno iz perspektive K ontroleraPtica jer ne treba pisati poseban kod koji bi odredio s kojim se tano tipom Ptice radi, ili kakvo je ponaanje te odreene Ptice. Kako se dogaa da se, kada se pozove metoda pom eriSe() uz ignorisanje specifinog tipa Ptice, prim eni pravilno ponaanje (Guska hoda, leti ili pliva, a Pingvin hoda ili pliva)?

Poglav[je 1: Upoznavanje sa objektima

27

Odgovor lei u osnovnoj zakoljici objektno orijentisanog program iranja: prevodilac ne moe da pozove funkciju na tradicionalan nain. Prevodilac koji nije objektno orijentisan izaziva rano vezivanje (engl. early binding). Ovaj term in m oda niste ranije uli, jer o pozivanju funkcija niste razmiljali ni na koji drugi nain. To znai da prevodilac generie poziv funkcije odreenog im ena, a izvrni sistem kasnije razrei ovaj poziv ubacivanjem apsolutne arese koda koji treba da se izvri. U OO P-u, program ne moe da odredi adresu koda sve do trenutka izvravanja, tako da je neophodna neka druga ema kada se poruka alje generikom objektu. Da bi reili ovaj problem , objektno orijentisani jezici koriste koncept kasnog vezivanja (engl. late binding). Kada poaljete poru ku objektu, kod koji se poziva ne biva odreen sve do trenutka izvravanja. Prevodilac ipak proverava da li telo specifine m etode postoji te koji su tipovi argum enata i povratne vrednosti, ali ne zna koji e kod izvriti. Da bi razreila kasno povezivanje, um esto apsolutnog poziva Java koristi specijalan deli koda koji adresu tela m etode izraunava pom ou inform acija ugraenih u konkretan objekat. (Ovaj proces je detaljno opisan u poglavlju o polim orfizm u.) Tako svaki objekat moe da se ponaa razliito, u skladu sa sadrajem svakog pojedinog specijalnog delia koda. Kada poaljete poruk u objektu, on sam odluuje ta e s njom da uradi. U nekim jezicima m orate izriito navesti da odreenu m etodu treba kasno povezati (u jeziku C ++ to se radi pom ou rezervisane rei v irtu al). U tim jezicima, m etode se podrazum evano Hepovezuju dinamiki. U Javi se dinam iko povezivanje podrazum eva, tako da ne m orate dodavati posebne rezervisane rei da biste dobili polimorfizam. Posm atrajm o prim er oblika. Dijagram porodice klasa (koje su zasnovane na istom u niform nom interfejsu) dat je ranije u ovom poglavlju. Da bism o prikazali polim orfizam, napisaemo naredbe koje ignoriu specifine detalje tipova i obraaju se samo osnovnoj klasi. Te naredbe nisii povezane sa inform acijam a specifinim za pojedine tipove, pa se stoga jednostavnije piu i lake razumevaju. Ako pom ou nasleivanja dodam o nov tip - Sestougaonik, na prim er - te naredbe radie jednako dobro s novim tipom O blika kao to su radile sa ve postojeim tipovim a. Znai, program je proiriv. Alco napiete m etodu u Javi (to ete uskoro nauiti):
v oid r a d i N e s t o ( O b l i k oblik) o b 1 i k.obri si S e ( ) ; {

II
}

...

obli k.i s c r t a j S e ( ) ;

ta m etoda se obraa bilo kom O bliku, pa ne zavisi od specifinog tipa objekta koji se iscrtava i brie. Ako u nekom drugom delu program a pozovemo funkciju radiN esto():
K rug krug = n e w Krug(); linija = n ew L i n ija();

T r o u g a o t r o u g a o = n ew T r o u g a o ( ) ; Linija radiNesto(krug); radiNesto(trougao); radi Nes t o ( 1 i n i j a ) ;

pozivi m etode radiN esto() autom atski rade ispravno, bez obzira na taan tip objekta.

28

Misliti na Javi

Ovo je prilino zadivljujui trik. Posmatrajte red:


radiNesto(krug);

Ovde je Krug prosleden metodi koja oekuje Oblik. Poto Krug jeste Oblik, funkcija radiNesto() moe tako da ga tretira. O dnosno, bilo koju poruku koju m etoda radiNesto() m oe da poalje Obliku, Krug moe da prihvati. Zato je gornji poziv p o tp u n o siguran i logian. Proces pri kome izvedeni tip tretiram o kao osnovni tip, nazivamo svoenje navie (engl. npcasting). Ime castje iskorieno zato to oznaava ubacivanje u kalup (engl. casting into a tnold -ubacivan je u kalup), a up potie od naina na koji se obino organizuje dijagram nasleivanja, sa osnovnim tipom na vrhu i izvedenim klasama koje se lepezasto ire nanie. Prema tome, konverzija u osnovni tip je penjanje uz dijagram nasleivania: svoenje navie ili upcasting.

U objektno orijentisanom program u uvek postoji svoenje navie, jer pri tom ne morate znati taan tip s kojim radite. Pogledajte funkciju radiN esto():
o b l i k.obri s i S e ( ) ;

II

...

o b l ik . is c r t a j S e f ) ;

O bratite panju na to da se nigde ne govori:,, Ako si Krug, radi ovo, ako si Kvadrat radi onoitd." Tako napisan kod koji proverava sve mogue tipove koje O blik moe da pokrije prljav je i m orate da ga m enjate svaki put kada dodate novu vrstu O blika. U naem sluaju, kaemo: ,,Ti si oblik, ja znam da moe da se iscrta i obrie, to i uradi, i sam vodi rauna o detaljima". Vano je da se naredbe u funkciji raiN esto() izvravaju na pravi nain. Sam poziv m etode iscrtajSe() za K rug prouzrokuje izvravanje drugaijeg koda nego kada pozivamo m etodu iscrtajSe() za Kvadrat ili Liniju; ali kada poruku iscrtajSe() poaljemo nepoznatom O bliku, dobija se ispravno ponaanje, zasnovano na stvarnom tipu Oblika. Ovo je dobra osobina, kao to je ranije pom enuto, jer kada prevodilac prevodi m etodu radiN esto(), on ne zna tip s kojim radi. Stoga bismo obino oekivali da on pozove verziju m etoda obrisiSe() i iscrtajSe() za osnovnu klasu O blik, a ne za odreeni Krug, Kvadrat

Poglavjje 1: Upoznavanje sa objektima

29

ili Liniju. Zbog polim orfizm a se sve ipak ispravno odvija. Prevodilac i sistem za izvravanje vode rauna o svim detaljima. Zasa je dovoljno da znate da polim orfizam funkcionie i kako da piete program e koristei taj pristup. Kada poaljete poru k u objektu, objekat e uraditi ta treba, ak i kada treba svoditi navie.

Hijerarhija s jedinstvenim korenom


Jedno od pitanja u O O P-u koje je postalo veoma znaajno od uvoenja C + + -a glasi: da li sve klase treba da b u d u izvedene iz jedinstvene osnovne klase. U Javi (i gotovo svim ostalim O O P jezicima) odgovor je potvrdan. Ova osnovna klasa se naziva Object. Ispostavlja se da postoje brojne prednosti hijerarhije s jedinstvenim korenom . Svi objekti u hijerarhiji s jedinstvenim korenom im aju zajedniki interfejs i, prem a tome, svi su istog osnovnog tipa. Postoji i druga m ogunost (koju nudi C + + ), da svi objekti ne budu istog osnovnog tipa. Po vertikalnoj kom patibilnosti ovo vie odgovara m odelu jezika C i moe se sm atrati manje restriktivnim; ali kada elite da se bavite objektno orijentisanim program iranjem u celini, m orate napraviti svoju hijerarhiju da biste obezbedili istu fleksibilnost koja je ve ugraena u druge O O P jezike. Takoe, u svakoj novoj biblioteci klasa koju nabavite, bie korien neki drugi nekom patibilni interfejs. Treba uloiti napor (i verovatno viestruko nasleivanje) da biste ugradili taj novi interfejs u svoj program . Da li je dodatna ,,fleksibilnost C + + -a vredna toga? Ako vam treba - zato to ste p u no uloili u C - prilino je vredna. Ukoliko poinjete od nule, drugi jezici, poput lave, esto rnogu biti mnogo produktivniji. Svi objekti u hijerarhiji s jedinstvenim korenom (kakvu obezbeuje Java) zasigurno imaju odreenu zajedniku funkcionalnost - znate da moete da izvrite odreene osnovne operacije nad svakim objektom u svom sistemu. Hijerarhija s jedinstvenim korenom , uz stvaranje dinamikih objekata, veoma pojednostavljuje prosleivanja argum enata. Hijerarhija sa jedinstvenim korenom olakava realizaciju sakupljaa smea, to jeje d n a od temeljnih prednosti Jave nad jezikom C++. Poto je pri izvravanju u svim objektim a garantovano postojanje informacija o tipu, nikada neete naii na objekat iji tip ne m oete da utvrdite. To je naroito vano kod operacija na sistemskom nivou, kao to je obrada izuzetaka, i obezbeuje veu fleksibilnost pri program iranju.

Kontejneri
Po pravilu, ne moete znati koliko objekata e vam biti potrebno za reavanje nekog zadatka, niti koliko dugo oni treba da postoje u m emoriji. Ne znate ni kako da uskladitite te objekte. Ako pre izvravanja program a ne znate ni broj objekata ni njihov vek trajanja, kako da odredite koliinu memorijskog prostora za njihovo skladitenje? Reenje veine problem a u objektno orijentisanom program iranju ponekad deluje neozbiljno: stvoriti jo jedan tip objekta. Novi tip objekta koji reava pom enuti problem uva reference na druge objekte. Naravno, to isto moete da uradite i pom ou nizova , koji postoje u veini jezika. Ali taj novi objekat, obino pod im enom kontejner (naziva se i fcolekcija, ali poto Javine biblioteke koriste taj term in u drugom smislu, ova knjiga e se

30

Misliti na Javi

drati im ena ,,kontejner), irie se po potrebi da bi prim io sve to u njega stavite. Stoga ne m orate da znate koliko ete objekata uvati u kontejneru. Samo napravite kontejnerski objekat i prepustite njemu da se stara o detaljima. Sreom, dobar OOP jezik sadri skup kontejnera kao deo paketa. U jeziku C ++ on je deo standardne C ++ biblioteke i esto se naziva standardna biblioteka ablona (engl. Standard Template Library, STL). Smalltalk ima prilino dobar skup kontejnera. I Java ima m nogo kontejnera u svojoj standardnoj biblioteci. U nekim bibliotekama jedan ili dva generika kontejnera dovoljni su za sve potrebe, dok u drugim bibliotekama (na prim er u Javi) postoje razliiti tipovi kontejnera za razliite potrebe: vie razliitih klasa spiskova (tipova Lista) za uvanje sekvenci, tipova M apa (koje neki nazivaju asocijativni tiizovi) zapridruivanje jednih objekata drugim a, tipova skupova (Set) za uvanje po jednog prim erka raznih tipova objekata i druge kom ponente m eu kojima su redovi ekanja, stabla, stekovi itd. Sa stanovita program a, za vas je vaan kontejner s kojim moete da radite i reite problem. Ako kontejner jednog tipa zadovoljava vae potrebe, nem a razloga da uvodite druge. Izbor treba da vam bude ponuen iz dva razloga. Prvo, kontejneri obezbeuju razliite tipove interfejsa i naina rada. Stek ima interfejs i nain rada razliit od reda ekanja, koji se razlikuje od skupa ili liste. Za reenje vaeg problem a jedan od njih je obino bolji od ostalih. Drugo, razliiti kontejneri obavljaju iste operacije s razliitom efikasnou. Na primer, postoje dve osnovne vrste lista: ArrayList i LinkedList. I jedna i druga su jednostavne sekvence koje mogu imati identine interfejse i, spolja gledano, naine rada. No, postoji bitna razlika u trajanju i zahtevima odredenih operacija nad njima. Prim era radi, nasum ino pristupanje elementima kontejnera ArrayList je operacija s konstantnim vremenom izvravanja; bez obzira na to koji element izaberemo, potrebno je isto vreme. Meutim, pom eranje kroz listu LinkedList do nasum ino izabranog elementa je komplikovanije i treba vie vremena da se pristupi elementim a to su oni dalje od poetka liste. S druge strane, ako elite da ubacite element usred sekvence, to je mnogo jednostavnije i bre u iisti LinkedList, nego u ArrayList. Ove i druge operacije nisu jednako efikasne, to zavisi od strukture koja se nalazi u osnovi pojedine sekvence. U fazi pisanja program a, moete krenuti od kontejnera LinkedList a kada budete popravljali perform anse, prebacite se na kontejner ArrayList. Zahvaljujui apstrakciji preko interfejsa List, prebacivanje iz jedne strukture 11 drugu imae minim alan uticaj na va kod.

Parametrizovani (generiki) tipovi


Pre Jave SE5, kontejneri su mogli da sadre samo jedini univerzalni tip eleinenata koji postoji u Javi: Object. H ijerarhija s jedinstvenim korenom znai da je sve tipa Object, tako da kontejner koji moe da uva Object, moe da uva bilo ta." Ovo pojednostavljuje ponovno korienje kontejnera. Da biste koristili takav kontejner, odajte u njega reference na objekte, a kasnije ih traite naza. M eutim, poto kontejner uva samo tip Object, kada dodate referencu na svoj
N e m o e da u v a p ro s te tip o v e , ali a u to m a ts k o p a k o va n je (en g l. <iumboxitig) koje je d o n e la Java SF.5 g o to v o d a p o tp u n o u k la n ja to o g ra n i e n je . O to m e e jo biti rei u ovoj knjizi.

Poglavlje I : Upoznavanje sa objektima

31

objekat u kontejner, ona se svodi navie na O bject i gubi identitet. Kada je izvadite iz kontejnera, dobijate referencu na Object, a ne referencu na tip koji ste stavili unutra. Kako onda da tu referencu vratite u neto to ima konkretan tip objekta koji ste stavili u kontejner? Ovde se ponovo koristi eksplicitna konverzija (pretvaranje) tipova, ali ovog p u ta se ne penjete uz hijerarhiju nasleivanja do optijeg tipa, ve se sputate niz hijerarhiju do odreenijeg tipa. Ovaj nain eksplicitne konverzije naziva se svoenje nanie (engl. dovvncasting). Pri svoenju navie, na primer, znate da je K rug tipa O blik, pa moete slobodno da izvrite svoenje navie; m eutim , neki O bject nije obavezno K rug ili O blik, pa svoenje nanie nije ba sigurno, osim ako znam o s im im am o posla. Ovo i nije previe opasno: ako izvrite pogreno svoenje nanie, javlja se greka pri izvravanju - izuzetak (engl. exception), o kome uskoro govorimo. Kada iz kontejnera uzim ate reference, m orate obezbediti neki nain da zapam tite na ta se te reference odnose kako biste mogli da izvrite pravilno svoenje nanie. Svoenje nanie i provere pri izvravanju zahtevaju dodatno vreme za izvravanje program a i dodatni napor za program era. Zar ne bi bilo logino da nekako napravim o kontejner tako da on zna koje tipove uva, ime se iskljuuje potreba za svoenjem nanie kao i m ogue greke. Ovo se reava pom ou parametrizovanih tipova, a to su klase koje prevodilac autom atski moe da prilagodi tako da rade sa odreenim tipom . Na prim er, param etrizovani kontejner prevodilac bi mogao da prilagodi tako da prihvata i vraa sam o klasu Oblik. ledna od velikih prom ena koje je donela Java SE5 jesu param etrizovani tipovi, koje u Javi nazivamo generiki tipovi. Prepoznaete ih po uglastim zagradam a u n u tar kojih se navode; recimo, ovako se moe napraviti ArrayList koji sadri Oblik:
A r r a y L i s t < 0 b l i k > oblici = new A r r a y L i s t < 0 b i i k > ( );

Da bi se generiki tipovi dobro iskoristili, izmenjene su i mnoge standardne kom ponente bibilioteke. Kao to ete videti, generiki tipovi su uticali na dobar deo koda u ovoj knjizi.

Pravljenje objekata i njihov ivotni vek


Jedan od najvanijih inilaca jeste nain na koji se objekti stvaraju i unitavaju. Da bi m ogao da postoji, svakom objektu su neophodni neki resursi, prvenstveno m em orija. Kada objekat vie nije potreban, on m ora da se poisti kako bi se resursi oslobodili i uinili dostupnim za ponovno korienje. U jednostavnim program skim situacijama pitanje kako oistiti objekat ne ini se previe komplikovanim: napravite objekat, koristite ga koliko vam je potreban, a onda neka se uniti. M eutim, moguce su i sloenije situacije. Pretpostavim o, na primer, da piete program za upravljanje vazdunim saobraajem na nekom aerodrom u. (Isti model bi mogao da se prim eni i na rukovanje sanducim a u skladitu, ili na sistem i/.najmljivanja video kaseta, ili na smetaj za kune ljubimce.) Na prvi pogled, ovo izgleda jednostavno: napravite kontejner za uvanje aviona, a zatim napravite novi avion i stavite ga u kontejner svaki put kada avion ue u zonu kontrole vazdunog saobraaja. L)a biste oistili sistem kada avion napusti zonu, poistite iz m em orije odgovarajui objekat avion.

32

Misliti na Javi

Moda imate i neki drugi sistem za zapisivanje podataka o avionim a o kojima ne treba neposredno voditi rauna. M oda je to evidencija o planovim a leta malih aviona koji naputaju aerodrom . Znai, trebao bi vam drugi kontejner za male avione i kad god napravite objekat aviona, ako je to mali avion, stavili biste ga takoe i u ovaj drugi kontejner. Zatim bi neki pozadinski proces obavljao operacije nad objektim a iz tog kontejnera kada raunar ne radi nita drugo. Sada je problem neto tei: kako uopte moete znati kada da unitite objekte? Kada zavrite sa objektom, nekom drugom delu sistema m oda je jo uvek potreban. Isti problem se moe pojaviti i postati vrlo sloen u m nogim drugim situacijam a i u program skim sistemim a (kao to je C++) u kojima m orate izriito da obriete objekat kada s njim zavrite. Gde se nalaze podaci iz objekta i kako se kontrolie trajanje objekta? C ++ zauzima stav da je najvanija efikasnost, pa program eru preputa izbor. Da bi se postigla maksimalna brzina izvravanja, uvanje i trajanje m ogu biti odreeni prilikom pisanja program a, tako to se objekti stavljaju na stek (oni se ponekad nazivaju i automatske ili vidljive- engl. scoped -pro m enljive) ili u statiku oblast za uvanje. Ta mesta im aju viso k p rio ritet i prostor se u njim a brzo zauzima i oslobaa, pa je kontrola nad njim a veoma znaajna u nekim situacijama. M eutim , tim e rtvujete fleksibilnost jer m orate da znate taan broj, trajanje i tip objekata prilikom pisanja program a. Ako pokuavate da reavate optiji problem , na prim er projektovanje pom ou raunara (CAD), upravljanje skladitem ili kontrolu vazdunog saobraaja, ova m etoda je previe restriktivna. Drugi pristup je dinamiko stvaranje objekata u dinamikoj oblasti memorije (engl. heap). Pri ovom pristupu, sve dok ne pone izvravanje, ne znate koliko vam objekata treba, koliko e trajati, niti kog su tipa. Sve se to odreuie kada program ve radi (ovakav nain pravljenja objekata zove se dinam iki). Ako vam zatreba novi objekat, napravite ga u dinamikoj memoriji, u trenutku kada vam je zatrebao. Poto se oblau za uvanje upravlja dinamiki, prilikom izvravanja, zauzimanje memorijskog prostora traje znatno due od odvajanja prostora na steku. Odvajanje prostora na steku esto se postie samo asem blerskom naredbom da se pokaziva steka pom eri nanie i da se vrati nazad. Vreme za koje se odvoji prostor u dinamikoj m em oriji, zavisi od dizajna m ehanizm a za skladitenje. Dinamiki pristup se zasniva na najee opravdanoj pretpostavci da su objekti sloeni, tako da dodatni reijski trokovi za nalaenje prostora i njegovo oslobaanje nee bitno uticati na stvaranje objekata. Vea fleksibilnost dinam ikog pristupa je neophodna za reavanje optijih program skih problem a. Java iskljuivo koristi drugi pristup.' Svaki p u t kada elite da stvorite objekat, koristite rezervisanu re new da biste napravili dinam iki prim erak tog objekta. Namee se jo i pitanje trajanja objekata. U jezicima koji dozvoljavaju da se objekti stvaraju na steku, prevodilac odreuje koliko dugo objekti traju i moe automatski da ih uniti. Meutim, kada objekat stvorite dinamiki, prevodilac nema informacije o njegovom ivotnom veku. U jeziku kao to je C ++ m orate programski da odredite kada eteda unitite objekat, to moe dovesti docurenja" m em orije ako se to ne uradi ispravno (to je est problem u program im a pisanim na C + + -u). Java obezbeuje tzv. sakuplja smea (engl. garbage collector) koji automatski otkriva koji se objekat vie ne upotrebljava i unitava ga.
P ro sti tip o v i, o k o iitn a ete k a sn ije s a z n a ti vie, p re s ta v lja ju sp ec ija la n sluaj.

Poglavlje I : Upoznavanje sa objektima

33

Sakupljanje smea je veoma korisno, jer sm anjuje broj stavki na koje m orate da mislite i koliinu koda koji m orate da napiete. Jo je bitnije to sakupljanje smea obezbeuje m nogo vii nivo zatite od podm uklih problem a sa curenjem" m em orije (koje je mnoge projekte pisane na jeziku C++ oborOo na kolena). U Javi, sakuplja smea vodi rauna o problem u oslobaanja m em orije'(iako u to ne spadaju drugi aspekti ienja objekta). Sakuplja smea ,,zna kada se objekat vie ne koristi i autom atski oslobaa m em oriju koju je zauzim ao taj objekat. To, uz injenicu da su svi objekti izvedeni iz jedne osnovne klase O bject, kao i da postoji samo jedan nain za pravljenje objekata - dinam iki - ini proces program iranja u Javi m nogo jednostavnijim od program iranja na jeziku C ++. M nogo je m anje odluka koje treba da donesete i prepreka koje m orate da prevaziete.

Obrada izuzetaka: postupanje s grekama


Jo od prvobitnih program skih jezika, obrada greaka je bila jedna od najteih oblasti. Poto je teko napraviti dobru em u za obradu greaka, u m nogim jezicima zanemarena je ta oblast i to pitanje, a problem se preputa projektantim a bibJioteka. Projektanti biblioteka su iznali polovine mere prim erene m nogim situacijama, ali koje se lako mogu zaobii, obino ignorisanjem. O snovni problem sa svim emam a za obradu greaka je to to se oslanjaju na obazrivost program era, i na ugovorene konvencije koje nisu nam etnute jezikom. Ako program er nije obazriv - to se esto deava kad uri - te eme lako moe ispustiti iz vida. O brada izuzetaka ugrauje obradu greaka direktno u program ski jezik i ponekad ak i u operativni sistem. Izuzetak je objekat ,,baen (engl. thrown) s mesta greke, koji odgovarajui upravlja izuzecima koji obrauje oreeni tip greaka, moe da ,,uhvati (engl. catch). Kao da je obrada izuzetaka drugi, paralelni put izvravanja, kojim moe da se krene kada stvari pou naopako. Zato to koristi poseban put za izvravanje, obrada izuzetaka ne m ora da se mea s vaim kodom koji se norm alno izvrava. Poto ne morate stalno da proveravate da li je biio greaka, pisanje koda e najee biti jednostavnije. Sem toga, baen izuzetak se razlikuje od vrednosti greke koja je vraena iz m etode i od indikatora stanja koji ukazuje na greku, po tom e to oni mogu da se ignoriu. Izuzetak se ne moe ignorisati i garantuje se da e biti obraen u nekom trenutku. Konano, izuzeci obezbeuju da se pouzdano izvuete iz loe situacije. Umesto da samo izaete iz progrania, esto ste u prilici da sredite stvari i nastavite sa izvravanjem, ime dobijate mnogo robusnije program e. Javina obrada izuzetaka se izdvaja od drugih program skih jezika jer je ugradena od poetka, pa ste prinueni da je koristite. To je jedini prihvatljivi nain prijavljivanja greaka. Ako ne napiete kod tako da pravilno obrauje izuzetke, javie se greka pri prevoenju. Ova garantovana doslednost katkada znatno pojednostavljuje obradu greaka. Treba napom enuti da obrada izuzetaka nije sam o objektno orijentisana mogunost, iako su u objektno orijentisanim jezicima izuzeci obino predstavljeni objektima. O brada izuzetaka je postojala i pre objektno orijentisanih jezika.

34

Misliti na Javi

Paralelni rad
Osnovni koncept u raunarskom program iranju jeste obrada vie od jednog zadatka istovremeno. Mnogi programski poslovi zahtevaju da program moe da zaustavi rad, pozabavi se nekim drugim problem om a zatim se vrati glavnom procesu. Problem u se pristupalo na vie naina. Na poetku, program eri koji su poznavali raunar do najsitnijih pojedinosti, pisali su prekidne servisne rutine, a suspenzija glavnog procesa je inicirana preko hardverskog prekida. Iako je sve to dobro radilo, bilo je teko i neprenosivo, jer je prebacivanje program a na novi tip raunara bilo sporo i skupo. Ponekad su za obradu zadataka koji se m oraju odm ah izvriti prekidi rada neophodni, ali postoji velika klasa poslova u kojoj problem pokuavam o da izdelimo na vie delova koji se izvravaju zasebno i paralelno, da bi ceo program bre reagovao. Delovi koji se zasebno izvravaju unutar program a nazivaju se niti (engl. thread), a ceo koncept paralelni rad (engl. concurrency). Tipian p rim er paralelnog rada je korisniko okruenje. Zbog podele program a na niti, korisnik m oe da pritisne dugm e i da dobije brz odziv, umesto da bude prinuen da eka dok program ne zavri tren u tn i zadatak. N itim a se obino raspodeljuje vreme jednog (i jedinog) procesora. M eutim , ako operativni sistem podrava vieprocesorski rad, svaka nit moe biti dodeljena razliitim procesorima, i tada one zaista m ogu da rade paralelno. Jedna od korisnih osobina paralelnog rada na nivou jezika jeste sledea: program er ne m ora da brine postoji li jedan ili vie procesora. Program je logiki podeljen na niti i ako raunar ima vie od jednog procesora, program e se bre izvravati, bez ikakvih posebnih prilagoavanja. Nakon svega ovoga, paralelni rad zvui prilino jednostavno. Postoji ipak jedna zakoljica: deljeni resursi. Ako se sim ultano izvrava vie niti koje oekuju da pristupe istom resursu, pojavie se problem. Na prim er, dva procesa ne mogu istovrem eno da alju podatke istom tampau. Da bi se reio problem , resursi koji mogu biti deljeni, kao to je tam pa, m oraju biti zakljuani dok se koriste. Znai, nit zakljua resurs, zavri zadatak, a zatim otkljua resurs, posle ega neko drugi moe da ga koristi. Paralelni rad je ugraen u Javu, a Java SE5 ga d odatno podrava svojom bililiotekom.

Java i Internet
Ako je Java zapravo samo jo jedan raunarski program ski jezik, m oete se zapitati zato je toliko vana i zato se predstavlja kao revolucionarni korak u raunarskom programi ranju. Odgovor nije odmah oigledan, ukoliko se posm atra iz tradicionalne program erske perspektive. Iako je Java veoma korisna za reavanje uobiajenih zasebnih program skih problem a, jo je vanije to to pom ou nje moete reiti i program ske probleme koji se tiu Weba.

ta je Web?
Isprva Web moe da deluje pom alo tajanstveno, uz celu priu o krstarenju, ,,prisustvu i Iinim prezentacijama. Korisno je malo se udaljiti i sagledati ta Web zaista jeste, ali za to m orate da razumete sisteme klijent/server, jo jedan aspekt raunarske obrade koji je pun zbunjujuih tema.

Poglavlje I : Upoznavanje sa objektima

35

Klijent/server obrada
O snovna ideja sistema klijent/server jeste postojanje centralnog skladita inform acija neke vrste podataka, obino u bazi podataka - koje hoete da aljete, po zahtevu, grupi ljudi ili raunara. U konceptu klijent/server kljuno je to to je skladite inform acija centralizovano tako da informacije m ogu da se m enjaju i da se te prom ene prenose svim korisnicima informacija. Skladite inform acija, softver koji alje informacije i raunar (raunari) gde se softver i inform acije nalaze, nazivaju se server. Softver koji se nalazi na korisnikom raunaru, sarauje sa serverom, preuzim a informacije, obrauje ih i prikazuje na udaljenom raunaru, naziva se klijent. Osnovni koncept klijent/server obrade, znai, nije previe sloen. Problemi se javljaju jer im ate jedan server koji pokuava da opslui vie klijenata u isto vreme. Uglavnom se koristi sistem za upravljanje bazam a podataka, tako da program er ,,uravnoteava raspored podataka u tabelama da bi postigao optim alno korienje. Sistemi esto dozvoljavaju klijentim a i da dodaju informacije na server. To znai da m orate obezbediti da novi podaci jednog klijenta ne pregaze'" nove podatke drugog klijenta, ili da se ti podaci ne izgube u procesu dodavanja u bazu. (Ovo se zove obrada transakcija - engl. transaction processitig). Kada klijentski softver treba izmeniti, on m ora da se prevede, oisti od greaka i instalira na klijentskim raunarim a, to je u stvarnosti znatno komplikovanije i skuplje nego to mislite. Posebno je problem atino podrati vie tipova raunara i operativnih sistema. Konano, tu je i veoma bitno pitanje perform ansi: stotine klijenata mogu da postavljaju zahteve vaem serveru u bilo kom trenutku, pa je i najmanje zakanjenje problematino. Da bi se kanjenje svelo na najm anju m eru, program eri naporno rade da rasterete procesne zadatke, prebacujui deo obrade na klijentski raunar, a ponekad i na druge raunare na serverskoj strani, koristei takozvane posrednike (engl. middleware). (Posrednici se takoe koriste da bi se olakalo odravanje.) Realizacija tako jednostavnog postupka - slanja informacija - toliko je slojevita i sloena da ceo problem eluje beznadeno zagonetno. I pored toga je veoma bitna: klijent/ server obrada nosi otprilike polovinu svih program skih aktivnosti. O na je zaduena za sve, poev od uzimanja narudbina, transakcija s kreditnim karticama, pa do slanja raznih vrsta podataka - berzanskih, naunih, dravnih; ta god vam padne na pam et. U prolosti sm o se suoavali sa individualnim reenjima individualnih problema; svaki put sm o smiljali novo reenje. Bilo je teko osmisliti ih, teko su se upotrebljavala i korisnik ie m orao da ui nov interfejs za svako od tih reenja. Ceo klijent/server problem trebalo bi sveobuhvatno da se rei.

Web kao dinovski server


Web je u sutini jedan dinovski sistem klijent/server, i neto vie od toga, jer svi serveri i svi klijenti postoje zajedno i u isto vreme 11 samo jednoj mrei. To ne m orate da znate jer u datom trenutku brinete samo o povezivanju i interakciji s jednim serverom (iako moda pri tom lutate Mreom traei odgovarajui server). U poetku je to bio jenostavan jednosmerni proces. Vi ste ispostavljali zahtev serveru i on vam je prosleivao datoteku koju je softver za itanje (klijent) prevodio i formatirao na vaem lokalnom raunaru. Ali, ubrzo su ljudi poeleli da rade vie od istog prosleivanja

36

Misliti na Javi

stranica sa servera. eleii su punu klijent/server kompatibilnost kako bi klijent bio u mogunosti da vraa podatke mogunost na server, na primer, da pretrauje baze na serveru, dodaje nove informacije na server, ili da ostavi narudbinu (to je sve zahtevalo posebne bezbednosne mere). Bili smo svedoci tih prom ena u razvoju Weba. ita Weba je oznaio veliki korak napred - uveo je m ogunost da se delii inform acija neizm enjeni prikazuju na bilo kom tipu raunara. Ti itai su ipak bili prilino prim itivni i ubrzo su se zaglibili u zahtevima koji su im ispostavljani. Oni nisu bili previe interaktivni; zaguivali su server i sam Internet jer ste, svaki p u t kada je trebalo uraditi neto to zahteva program iranje, m orali da vraate informacije serveru na obradu. Moglo je da proe vie sekundi ili m inuta dok ne otkrijete da ste neto pogreno otkucali. Poto je ita bio predvien samo za pregled, nije m ogao da izvri ni najjednostavnije zadatke obrade. (S druge strane, bio je po tp un o bezbedan, jer na vaem raunaru nije mogao da izvri nijedan program , dakle ni da m u potencijalno donese greku ili virus.) Prim enjeno je vie pristupa reenju ovog problema. Za poetak, poboljani su grafiki standardi da bi omoguili bolju animaciju i video prikaz u itaima. Drugi deo problema mogao je biti reen samo ugraivanjem u ita mogunosti za pokretanje program a na klijentskoj strani. To se naziva programiranje s klijentskestrane (engl. client-sideprogramming).

Programiranje s klijentske strane


Poetni dizajn Weba (server-ita) om oguavao je interaktivne sadraje, ali je celokupnu interaktivnost obezbeivao server. Server je pravio statike stranice za klijentski ita koji ih je tum aio i prikazivao. Elem entarni jezik za oznaavanje hiperteksta (HTML) ima jednostavne m ehanizm e za prikupljanje podataka: polja za tekst, polja za potvrdu, radio-dugm ad, liste i padajue liste, kao i dugm e koje moe biti program irano samo da obrie podatke u obrascu ili da poalje (engl. submit) podatke iz obrasca nazad serveru. Ovo slanje se obavlja preko interfejsa CGI (engl. Common Gateway Interface), koji postoji na svim Web serverima. Tekst koji se nalazi u poslatom paketu kazuje serveru ta s tim paketom da uradi. Najea akcija je da se pokrene program koji se nalazi na serveru, u direktorijum u koji se obino zove ,,cgi-bin. (Ako budete gledali adresno polje na vrhu svog itaa kada pritisnete neko dugm e na Web stranici, ponekad ete videti ,,cgi-bin negde u nu tar sveg onog zameateljstva.) Ti program i mogu biti napisani na skoro svim jezicima. esto se pisalo na Perlu, jer je on napravljen za rad s tekstom, a uz to se interpretira, pa moe biti instaliran na bilo koji server, bez o b /ira na procesor ili operativni sistem. Danas se sve vie koristi Python ( www.Python.org ), zato to je jai i jednostavniji. M noge dananje m one VVeb stranice izgraene su iskljuivo na CGI program im a, s kojima moete da uradite gotovo sve. Ipak, odravanje Web stranica izgraenih na CGI program im a moe brzo da postane suvie sloeno, a postoji i problem s vrem enom od/.iva. Odziv CGI program a zavisi od toga koliko podataka se alje, kao i od optereenja servera i Interneta. (Uz to, pokretanje CGI program a um e da bude sporo.) Prvi projektanti Weba nisu predvideli koliko brzo e njegova propusnost da postane premala za nove vrste aplikacija. Na primer, skoro je nem ogue dosledno ostvariti bilo koju vrstu dinamikog crtanja grafika jer za svaku verziju grafika m ora da se napravi GIF slika a zatim prenese sa servera do klijenta. (GIF je akronim od Graphic Interchange Format, form at za razmenu

Poglavlje 1: Upoznavanje sa objektima

37

grafika.) Pored toga, bez sum nje ste stekli neposredno iskustvo s proverom ispravnosti podataka na ulaznom obrascu. Pritisnete dugm e za slanje na stranici, server pokrene CGI program koji otkrije greku, form atira HTML stranicu obavetavajui vas o greci, a zatim vam vrati tu stranicu. O nda vi m orate da se vratite na p reth o d n u stranicu i pokuate ponovo. Ovo ne samo da je sporo ve je i zam orno. Reenje je program iranje s klijentske strane. Veina stonih raunara na kojima rade itai Weba sposobni su da urade ogrom an posao, a s prvobitnim statikim HTM L pristup om sam o su stajali i besposleno ekali da server isporui sledeu stranicu. Program iranje s klijentske strane znai da ita Weba radi sav posao koji moe da obavi, korisnik m nogo bre dobija rezultat a oseaj zajednitva p ri radu s Web stranicom je potpuniji. Rasprave o program iranju s klijentske strane malo se razlikuju od rasprava o program iranju uopte. Param etri su gotovo isti, ali je platform a drugaija: ita Weba je slian ogranienom operativnom sistemu. Na kraju, opet m orate da program irate, i zato program iranje s klijentske strane povlai vrtoglav niz problem a i reenja. U daljem tekstu dajem o pregled pitanja i pristupa pri program iranju klijentske strane.

Dodaci
Razvoj dodataka (engl. plug-ins) predstavlja jedan od najduih koraka napred u program iranju s klijentske strane. Program er dodaje novu funkcionalnost itau tako to korisnik u potrebnom trenutku preuzme deo koda koji se ugradi na odgovarajue m esto u itau. Taj kod govori itau: ,,Od sada moe da obavlja i ovu novu aktivnost. (D odatak treba da preuzm ete sam o jednom .) Preko dodataka su itaima pridodata brza i m ona proirenja, ali pisanje dodataka nije jednostavan zadatak i nije neto to biste eleli da radite u okviru izgradnje odredene stranice. Vrednost odataka za program iranje s klijentske strane jeste to to oni omoguavaju strunjaku da pravi nova proirenja i da ih dodaje itau bez odobrenja proizvoaa itaa. Stoga, dodaci predstavljaju zadnja vrata koja om oguavaju stvaranje novih jezika za program iranje s klijentske strane (iako nisu svi jezici realizovani kao dodaoi).

Skript-jezici
Dodaci su prouzrokovali razvoj skript-jezika za itae. Pomou skript-jezika ugradujete izvorni kod program a za klijentsku stranu direktno u HTML stranicu, a dodatak koji tumai taj jezik autom atski se aktivira pri prikazivanju HTML stranice. Skript-jezici se obino prilino lako shvataju i poto se piu u delu HTML stranice, uitavaju se veoma brzo, jednim pristupom serveru na kome se nalazi ta stranica. Nedostatak je to to je va kod izloen i svako moe da ga vidi (i ukrade). U principu, ipak ne pravite neke sofisticirane stvari pom ou skript-jezika, pa ovaj nedostatak i nije neki problem . Postoji jedan skript-jezik koji veina itaa Weba podrava i bez ikakvog dodatka - to je JavaSoript (koji ima tek prolaznu slinost s Javom, i m oraete ga uiti zasebno). Tako je nazvan sam o da bi prigrabio deo Javinog marketinkog uzleta. Naalost, veina itaa Weba je svoju podrku za JavaScript realizovala na svoj jedinstveni nain, drugaije od ostalih itaa, pa ak i od ostalih svojih verzija. Neto je popravila standardizacija JavaScripta u obliku ECMAScipta, ali je raznim itaima trebalo m nogo vrem ena da dou do

38

Misliti na Javi

tog nivoa (tom e je doprineo i M icrosoft, gurajui svoj VBScript koji pomalo lii na JavaScript). Da bi program m ogao da se izvrava na svim itaim a,po pravilu ga m orate pisati koristei najm anji zajedniki imenilac svih postojeih verzija JavaScripta. Programska obrada greaka, kao i otkrivanje i otldanjanje greaka u toku pisanja program a, mogu se opisati sam o kao muenje. Dokaz tili potekoa je injenica da je na JavaScriptu tek nedavno napisan zaista sloen program (to je Googleov GMail), to je zahtevalo veliki trud i strunost. Ovim se naglaava da su skript-jezici koji se koriste u nutar itaa Weba predvieni da ree odreene vrste problem a, prvenstveno da se naprave bogatija i interaktivnija grafika korisnika okruenja. Skript-jezik m oe da rei i do 80 procenata problem a koje sreemo pri program iranju s klijentske strane. Vai problem i verovatno spadaju u tih 80 procenata, a kako skript-jezici om oguuju laki i bri rad, trebalo bi da razmislite o njim a pre nego to se upustite u m nogo zapetljanija reenja, kao to je program iranje na Javi.

Java
Ako skript-jezici mogu da razree 80 procenata problem a pri klijentskom program iranju, ta je s preostalih 20 procenata zaista tekih stvari? Java je popularno reenje za tih 20%. Pre svega, to je m oan program ski jezik, siguran, m euplatform ski i internacionalan. Java se neprekidno iri: dodaju se nove m ogunosti jezika i biblioteke koje mogu elegantno da ree problem e teke i u tradicionalnim program skim jezicima, kao to su istovremeni rad, pristup bazama, m reno program iranje i distribuirana obrada. Java doputa program iranje s klijentske strane preko apleta i pom ou lava Web Starta. Aplet je mali program koji moe da se izvrava samo unutar itaa VVeba. Aplet se autom atski preuzim a kao deo Web stranice (kao to se, na prim er, automatski preuzima grafika). Kada se aplet aktivira, on izvrava program . Deo njegove pogodnosti je to to omoguava da autom atski distribuirate klijentski softver sa servera tek u trenutku kada je korisniku klijentski softver potreban, a ne ranije. Korisnik bespogovorno dobija najnoviju verziju klijentskog softvera i to bez sloene ponovne instalacije. Zbog naina na koji je Java projektovana, program er treba da napravi samo jedan jedini program, a taj program autom atski radi na svim raunarim a koji imaju itae s podrkom za Javu. (Ovo, bez ikakve brige, obuhvata ogrom nu veinu raunara.) Poto je Java potpun programski jezik, moete uraditi sav posao koji je m ogu na klijentskoj strani, pre i posle postavljanja zahteva serveru. Na prim er, nem a potrebe da aljete obrazac sa zahtevom preko Interneta kako biste otkrili da li ste pogreili pri upisivanju datum a ili nekog drugog param etra; va raunar moe brzo da iscrtava grafike na osnovu podataka, um esto da eka da ih server iscrta i vrati sliku. Pored trenutnog poboljanja brzine i odziva, sm anjuju se ukupni mreni saobraaj i optereenje servera i spreava usporavanje celog Interneta.

Druge mogunosti
Poteno reeno, Java apleti nisu opravdali velika poetna oekivanja. Kada se Java tek pojavila, najvie se govorilo upravo o apletim a, jer je trebalo da oni najzad omogue ozbiljno program iranje na klijentskoj strani, poveaju brzinu odziva i smanje protok podataka potreban Internet aplikacijama. Predviale su se ogrom ne mogunosti.

Poglavlje 1: Upoznavanje sa objektima

39

Na Webu se zaista m ogu nai i veoma pam etni apleti, ali do sveopteg prelaska na aplete nije dolo. Verovatno je najvei problem bila veliina Javinog izvrnog okruenja (Java Runtim e Environm ent, JRE) od 10 MB, koje je trebalo preuzeti s Weba i instalirati, to je uplailo prosenog korisnika. Sudbinu im je m oda zapeatila injenica da je Microsoft odluio da JRE ne isporuuje kao deo svog Internet Explorera. U svakom sluaju, Java apleti se nisu proirili posvuda. Bez obzira na to, u nekim situacijama Java aplete i Java Web Start aplikacije jo uvek vredi imati. Ukoliko upravljate raunarim a korisnika, recimo unutar preduzea, bilo bi p am etno da distribuciju i auriranje klijentskih aplikacija obavljate pom ou ovih tehnologija, jer ete tim e utedeti znatnu koliinu vrem ena, tru d a i novca, naroito ako m orate esto da aurirate njihov softver. U poglavlju Grafika korisnika okruenja upoznaem o Flex, novu Adobeovu tehnologiju koja obeava - u njom e se prave Flash apleti. Poto vie od 98 procenata svih itaa Weba im a Flash Player (na W indowsu, Linuxu i M acu), m oem o sm atrati da je on usvojen standard. Flash Player se instalira i aurira lako i brzo. Jezik ActionScript je napravljen na osnovu ECMAScripta i stoga je prilino poznat, ali Flex omoguava program iranje bez brige o specifinostima raznih itaa - zato je daleko privlaniji od JavaScripta. Pri program iranju na klijentskoj strani, ovu m ogunost vredi razm otriti.

.NETi C#
Neko vrem e je glavni konkurent Java apleta bio Microsoftov ActiveX, iako samo na W indovvs raunarim a. O tad je M icrosoft napravio prvog pravog konkurenta celoj Javi u liku platform e .NET i program skog jezika C#. Platform a .NET je priblino isto to i Javina virtuelna maina (Java Virtual Machine , JVM) plus Java biblioteke. JVM je softverska platform a na kojoj se izvravaju java program i, a C# ima velike slinosti s Javom. Bez sum nje, radi se o dosad najboljem Microsoftovom proizvodu na podruju program skih jezika i program skih okruenja. Naravno, Microsoft je imao veliku prednost jer je mogao da ui na tuim grekama, ali je bogami nauio. Prvi put od svog nastanka, Java je dobila pravu konkurenciju. Zato su projektanti Jave u Sunu dobro pogledali C#, dobro razmislili o tom e zato bi program eri hteli da preu na C#, i odgovorili: nainili su fundam entalna poboljanja Jave - Javu SE5. T renutno je najvea siabost .NET-a vano pitanje da li e Microsoft dozvoliti njegovo potpm io prenoenje na ruge platforme. Microsoft tvrdi da se to moe napraviti bez problema, i ve postoji delimina realizacija .NET-a koja radi na Linuxu (projekat Mono, www.gomono.com ), ali dok se ne napravi potpuna realizacija iz koje M icrosoft nije nita izbacio, .NET je rizino sm atrati reenjem za sve platforme.

Internet i intranet
Wcl> je najoptije reenje problem a klijent/server, pa je logino da istu tehnologiju upotrebite i da biste reili podgrupu tog problem a, posebno klasini problem klijent/server um itar preduzea. Pri tradicionalnom pristupu klijent/server, postoji problem zbog raznih tipova raunara kao i problem zbog tekog instaliranja novog klijentskog softvera. Oba problem a dobro su reena pom ou itaa Weba i program iranja s klijentske strane.

40

Misliti na Javi

Kada se tehnologija Weba koristi za inform acionu m reu ogranienu na odreeno preduzee, to nazivamo intranet. Intranet obezbeuje m nogo veu sigurnost nego Internet jer pristup serverima u preduzeu moete fiziki da kontroliete. Izgleda da korisnici, kadajednom shvate osnovni princip rada itaa, m nogo lake izlaze na kraj s razliitim izgledima stranica i apleta, pa bre ue nove sisteme. Problem s bezbednou svrstava nas u jednu od grupa koje se obrazuju, ini se, po autom atizm u, u svetu klijent/server program iranja. Ako se va program izvrava na Internetu, ne znate na kojoj platform i e on raditi i elite da budete sasvim sigurni da ne irite neispravan kod. Treba vam neto nezavisno od platform e i bezbeno, kao to su skript-jezik ili Java. Ako radite na intranetu, pred vas se postavljaju drugaija ogranienja. Nije neuobiajeno da svi raunari budu platform e Intel/W indows. Na intranetu ste sami odgovorni za kvalitet svog program a i moete da otklanjate greke kako se koja otkriva. Pored toga, moda ve imate dosta nasleenog koda koji ste koristili za tradicionalniji klijent/server pristup, pri emu m orate fiziki da instalirate ldijentske program e svaki p u t kada radite na poboljanju. Vreme protraeno na instaliranje poboljanja prua razlog vie da preem o na itae gde su poboljanja nevidljiva i autom atska. (Ovaj problem reava i Java Web Start.) Ako imate posla s takvim intranetom , najrazboritiji pristup je da krenete najkraim putem koji vam omoguava da koristite postojeu bazu koda, um esto da ponovo piete program e na novom jeziku. Kada se suoite sa ovim zbunjujuim nizom reenia za program iranje s klijentske strane, najbolji plan napada je analiza isplativosti. Razm otrite ogranienja koja va problem namee i najkrai put do reenja. Poto je program iranje s Jdijentske strane ipak programiranje, uvek je dobro odabrati pristup koji omoguava najbri razvoj u odreenoj situaciji. Ovo je hrabar stav koji vas priprem a za neizbene susrete s problem im a u razvoju programa.

Programiranje sa serverske strane


Do sada je pitanje program iranja sa serverske strane bilo zanem areno, a ba tu je po miljenju mnogih Java imala najvee uspehe. ta se deava kada poaljete zahtev serveru? Najee zahtevi glase: Poalji mi ovu datoteku". Va ita zatim tum ai tu datoteku na odgovarajui nain: kao HTML stranicu, sliku, Java aplet, skript itd. U sloenije zahteve serveru obino spadaju i transakcije s bazama podataka. O bino se zahteva kompleksna pretraga baze podataka, koju server zatim form atira u HTML stranicu i alje kao rezultat. (Naravno, ako je klijent inteligentniji, zato to sadri kod na Javi ili skript-jeziku, mogu se slati neobraeni podaci, a potom form atirati na strani klijenta, to bi bilo bre i manje bi optereivalo server.) M oda biste poeleli da registrujete svoje ime u bazi podataka kada pristupate nekoj grupi ili ostavljate narudbinu, to e izazvati prom ene u toj bazi. Pisanje program a koji obrauju takve zahteve na serveru naziva se program iranje sa serverske strane (engl. server-side programming). Program iranje sa serverske strane obavlja se na Perlu, Pythonu, C + + -u ili nekom drugom jeziku za pisanje CGI program a, ali pojavili su se i napredniji sistemi. M eu njim a su i Web serveri zasnovani na Javi, koji omoguavaju pisanje takozvanih servleta. Kompanije koje razvijaju Web

Poglav[je 1: Upoznavanje sa objektima

41

stranice prelaze na Javu zbog tehnologije servleta i iz njih izvedenih JSP strana, najvie zato to se tim e iskljuuju problem i pri radu sa itaima razliitih m ogunosti. Programiranje sa scrverske strane razm otreno je u knjizi Thitikingin Enterprise Java, koju moete nai na adresi www.MindView.net. Uprkos tom e to se o Javi pria sam o u vezi sa Internetom , ona je programski jezik opte nam ene pom ou kogae moete reiti bilo koji tip problem a, kao i pom ou drugih jezika. Tu Javina snaga nije samo u prenosivosti, ve i u lakoi program iranja, robusnosti, velikoj standardnoj biblioteci i brojnim bibliotekama drugih proizvoaa koje se stalno razvijaju.

Saetak
Znate kako izgleda proceduralni program : definiciie podataka i pozivi funkcija. Da biste pronali svrhu takvog program a, m orate malo da se pom uite i pregledate pozive funkcija i koncepte niskog nivoa kako biste stvorili m odel u glavi. Zbog toga nam treba posredno predstavljanje kada projektujem o proceduralni program : sami za sebe, ti program i m ogu da zbunjuju jer su naini izraavanja usm ereni vie ka raunaru nego ka problem u koji reavamo. Poto OOP dodaje m noge nove ideje onim a koje nalazite u proceduralnim jezicima, moda mislite da e Java program biti m nogo komplikovaniji nego ekvivalentni C program. Biete prijatno iznenaeni: dobro napisan Java program je uglavnom daleko jednostavniji i m nogo razumljiviji od ekvivalentnog C program a. Imaete definicije objekata koji predstavljaju ideje u vaem prostoru problem a (um esto pitanja raunarskog prikaza) i poruke poslate tim objektima koje predstavljaju aktivnosti u tom prostoru. D obra strana u objektno orijentisanom program iranju jeste ta to je uz dobro projektovan program lako razumeti kod pri itanju. O bino ima i znatno m anje koda, jer ete m noge svoje probleme reiti ponovnim korienjem postojeeg koda iz biblioteka. O O P i Java ne m oraju da budu za svakoga. Vano je da procenite svoje potrebe i odluite da li e ih lava optim alno zadovoljiti ili bi bilo bolje da radite s nekim drugim programskim sistemom (ukljuujui onaj koji tren u tno koristite). Ako znate da e vae potrebe biti usko specijalizovane u bliskoj budunosti i ako imate specifina ogranienja koja Java m oda ne moe da zadovolji, onda ispitajte ostale raspoloive m ogunosti. Konkretno, preporuujem da razm otrite Python; posetite www.Python.org. Ako ipak izaberete Javu, barem ete znati koje ste m ogunosti imali i zato ste ba nju izabrali.

Sve je objekat
,,Da govorimo drugaiji jezik, percipirali bismo tieto drugaiji svet. Ludwig Witgenstein (1889-1951) Iako je zasnovana na C++-U, Javaje istiji objektno orijentisani jezik.
JAVA I C ++ SU H IB R ID N IJE Z IC I ALI SU PROJEKTANTI JAVE SMATRALI DA HIBRID IZA CIJA NIJE toliko bitna kao u C++-U. U h ibridnom jeziku moe se koristiti vie program skih stilova; C++ je hibridan da bi obezbedio povratnu kom patibilnost s jezikom C. Poto je C++ nadskup jezika C, on sadri i m noge nepoeljne nasleene osobine koje previe komplikuju neke aspekte C++-a. Jezik Java je napravljen za one koji ele da se bave samo objektno orijentisanim program iranjem . Pre nego to se uopte upustite u program iranje, m orate poeti da razmiljate objektno-orijentisano (osim ako ve ne razmiljate tako). Ovaj poetni napor e vam se isplatiti jer ete biti sposobni da program irate na jeziku koji se ui i koristi jednostavnije od mnogih drugih O O P jezika. U ovom poglavlju razm atraem o osnovne delove Java program a i nauiemo da je u Javi (gotovo) sve objekat.

Rad sa objektima preko referenci


Svaki program ski jezik ima svoje naine za rad sa elem entim a u m emoriji. Program er esto ne sme da gubi iz vida nain rada koji se primenjuje. Da li sa elem entom radite direktno ili preko posrednog predstavljanja (pokaziva u C-u ili C + + -u ), koje se opisuje posebnom sintaksom? Sve ovo je pojednostavljeno u Javi. Sve tretirate kao objekat, istom doslednom sintaksom koju koristite svuda. Iako vi sve tretirate kao objekat, identifikator s kojim radite je, u stvari, ,,referenca na objekat.1Zamislite televizor (objekat) s daljinskim upravljaem (referenca). Sve dok drite tu referencu, imate vezu s televizorom, ali kada neko kae da promenite kanal ili da utiate zvuk, vi radite s referencom koja menja objekat. Ako elite da se etate po sobi, a da i dalje podeavate televizor, poneete daljinski/referencu, a ne televizor.
O v d e m o e d o i d o n esu g lasica. Im a lju d i koji k au : O ito , to jc pokaziva", ali tak av iskaz ve sadri p re tp o s ta v k e o realizaciji. Po svojoj s in ta k si, Java refe re n c e su m n o g o s ro d n ije C + + re fe re n c a m a nego p o k a ziv a im a . U p rv o m iz d a n ju ove k n jig e o d a b ra o sa m n o v te rm in id e n tifik a to r (en g l. hnm U c ) jcr iz m e u re fe re n c i u C + + - u i re fe re n c i u Javi p o s to je b itn e razlike. Izlazio sa m iz C + + - a i n isa m eleo d a z b u n ju je m p ro g ra m e re u C + + - u , za k oje sa m s m a tra o d a e b iti n a jb ro jn iji k o risn ic i Jave. Pre n e g o to sam p rip re m io d ru g o iz d a n je, v id e o sa m d a je ee k o ri en te rm in re fe re n c a, p o to svako ko p relazi sa C + + - a m o ra d a savlada m n o g c d ru g e p o jm o v e p o rc d te rm in o lo g ije re fe re n c i, jo jc d a n p o ja m nee p rev ie s m e ta ti. M e d u tim , neki se n e slau ak ni sa te rm in o m re fe re n c a. U je d n o j knjizi tv rd i se kak o je p o tp u n o p o g re n o rci d a Java p o d r a v a p ro s le d iv a n je p re k o rc fe re n c i1 1je r su id e n tifik ato ri o b je k a ta u Javi (p o to m a u to r u ) u s /iw / o b je k ti re fe re n c i. A p o to se svc (k a k o d aljc tv rd i) u stva ri p ro s le d u je p o v re d n o s ti, vi n e o b a v lja te p ro s le d iv a n je p re k o rc fc rc n c i ve p ro s le u jc te o b je k te re fe rc n c i p o v re d n o s ti. M o e se d is k u to v a ti o p re c iz n o sti ta k o z a m re n ih o b ja n je n ja , ali sm a tra m d a m o j p ris tu p p o je d n o sta v lju je s h v a ta n je k o n c e p ta , bcz ik akvih g u b ita k a (p a , te o re ti a ri jezika m o g u d a tv rd e d a vas Iaem , ali ja u rei d a o b e z b e d u je m o d g o v a ra ju u a p s tra k c iju ).

Poglavjje 2: Sve je objekat

43

Daljinski upravlja moe da postoji sam za sebe, bez televizora. O dnosno, to to imate referencu, ne znai da obavezno im ate i objekat povezan s njom . Ako elite da uvate re ili reenicu, treba da napravite referencu na objekat ldase String:
String

s;

Ali, ovim ste napravili satno referencu, ne i sam objekat. Ako pokuate u ovom trenutku da poaljete poruku preko s, dobiete greku poto s u stvari nije povezano ni sa im (nem a televizora). Zato je sigurnije da uvek inicijalizujete referencu kada je pravite:
String s = "asdf";

Ovde se koristi posebna osobina Jave: znakovni niz se m oe inicijalizovati tekstom pod navodnicim a. Obino, za objekte m orate da koristite optiji tip inicijalizacije.

Morate da napravite sve objekte


Kada napravite referencu, elite i da je poveete s novim objektom . Po pravilu, to radite pom ou rezervisane rei new kojom se kae: Napravi m i ovakav nov objekat. Znai, u gornjem prim eru moete napisati:
String

s =

n ew S t r i n g

("asdf");

Ovo ne samo da znai: Napravi mi nov objekat klase S trin g , ve daje i inform aciju o tom e kako da se taj objekat napravi, tako to se navodi poetni znakovni niz. Pored tipa String Java ima obilje drugih unapred definisanih tipova. M nogo je vanije da moete napraviti svoje tipove. U stvari, to je osnovna aktivnost u program iranju na Javi i o tome ete uiti u ovoj knjizi.

Gde se nalazi skladite


Korisno je predstaviti neke aspekte izvravanja program a, a posebno kako je organizovana memorija. Postoji pet razliitih mesta za skladitenje podataka: 1. Registri: Tu se najbre skladite poaci jer su registri ovojeni od ostalih skladita: nalaze se unutar procesora. M eutim, broj registara je veoma ogranien, pa se registri automatski dodeljuju u skladu s potrebam a. Vi nem ate direktnu lcontrolu, niti u vaem program u vidite ijedan dokaz da registri uopte postoje. (S druge strane, C i C ++ omoguavaju da prevodiocu sugeriete dodelu registara.) 2. Stek: Naiazi se u radnoj m emoriji, ali ga procesor direktno kontrolie preko pokazivaa stcka (engl. stack pointer). Pokaziva steka se pom era nanie da bi zauzeo novu m emoriju, a pom era se navie da bi tu m em oriju oslobodio. Ovo je veoma brz i efikasan nain za dodeljivanje skladita, od koga su bri samo registri. )ava sistem mora da zna, dok pravi program , taan ivotni vek svih stavki koji se uvaju na steku. Ovo ogranienje omeava fleksibilnost vaeg program a, pa dok se neka Javina skladita nalaze na steku - posebno reference na objekte - sami objekti se ne uvaju na steku.

44

Misliti na Javi

3. Dinamika memorija: To je oblast m em orije opte nam ene (takoe u radnoj mem oriji) gde obitavaju svi objekti. D obra strana dinamike m em orije jeste to to, za razliku od steka, prevodilac ne m ora da zna koliko dugo dodeljeno skladite m ora da ostane u dinamikoj m em oriji. Stoga im am o veliku fleksibilnost pri korienju dinam ike memorije. Kad god vam zatreba neki objekat, napiite kod za njegovo pravljenje koristei new, i skladite e se napraviti u dinamikoj memoriji. Naravno, m orate platiti ovu fleksibilnost: skladite u dinamikoj m em oriji due se pravi nego skladite na steku (kad biste uopte mogli da napravite objekte na steku u Javi, kao to moete u C + + -u). 4. Konstantno skladite: K onstantne vrednosti esto se um eu direktno u kod program a to je bezbedno jer se one nikad ne m ogu prom eniti. Ponekad se konstante izdvojeno grupiu kako bi opciono mogle da se stave u ROM m em oriju (m em orija samo za itanje; engl. read-only memory), to se radi u raunarim a ugraenim u druge ureaje.2 5. Skladita van radne memorije: Ako su podaci p otpuno izvan program a, oni mogu da postoje i kada se program ne izvrava, izvan kontrole program a. Dva osnovna prim era za ovo su objekti u tokovima podataka (engl. streamed objects), to su objekti pretvoreni u tok bajtova, obino da bi se poslali na drugu m ainu, i trajni objekti (engl. persistent objects), to su objekti stavljeni na disk da bi sauvali svoje stanje ak i kada se program zavri. U ovim skladitima teko je pretvoriti objekte u neto to moe da postoji na drugom m edijum u, a to moe da postane regularni m em orijski objekat kada je potrebno. Java obezbeuje podrku za laku trajnost (engl. lightvveight persistencc), a m ehanizm i kao to su JDBC i Hibernate obezbeuju sofisticiraniju podrku za skladitenje i preuzim anje inform acija o objektima u bazama podataka.

Specijalan sluaj: prosti tipovi


Jedna grupa tipova koji se esto koriste u program iranju ima poseban tretm an; moete ih sm atrati osnovnim tipovima. Taj poseban tretm an je neophodan iz sledeeg razloga: ako napravite objekat operatorom new - posebno malu, jednostavnu promenljivu - to nee biti ba efikasno jer new postavlja objekte u dinam iku mem oriju. Za ove tipove Java se vraa na pristup koji imaju C i C++: um esto da napravi promenljivu korienjem operatora new, pravi autom atsku prom enljivu koja nijc referenca. Promenljiva direktno uva vrednost i rad s njom je m nogo efikasniji jer se uva na steku. Java unapred odreduje veliinu svakog prostog tipa. Ove veliine se ne menjaju od jednog do drugog raunara, kao to je sluaj u veini jezika. Nepromenljivost veliina je jedan od razloga zbog kog su program i na Javi prenosiviji nego oni na veini drugih jezika.

P r im e r za to je sk lad ite z n a k o v n ih n izo v a. U p o s e b n o m d e lu m e m o rije s n e p ro m e n ljiv o m (statik o m ) a d re so m , a u to m a ts k i se s ld a d ite svi lite ra ln i z n a k o v n i nizovi i k o n s ta n tn i izrazi iji je re z u lta t zn ak o v n i niz.

Poglavlje 2: Sveje objekat

45

Prost tip

Veliina u bitovima

M inimum

Maksimum

Om otaki tip

boolean char byte short int long float double void

16 8 16 32 64 32 64 -

Unicode 0 -128 -2'5 -23' -26 3 IEEE754 IEEE754 -

Unicode 2'6 -l + 127 +2I5 -I +23 '-l +26 3 -l IEEE754 IEEE754 -

Boolean Character Byte Short Integer Long Float Double Void

Svi num eriki tipovi su oznaeni (sadre i pozitivne i negativne vrednosti), stoga ne traite neoznaene tipove. Veliina tipa boolean nije izriito definisana; zadato je samo da moe da im a vrednosti true ili false. Om otakc klase za proste tipove podataka omoguavaju da u dinamikoj m em oriji napravite objekat koji predstavlja odreen prost tip. Na primer:
char c = 'x 1;

C h a r a c t e r ch = n e w C h a r a c t e r ( c ) ;

Moete takoe da koristite:


C h a r a c t e r ch = n e w C h a r a c t e r ( 'x ');

Java SE5 automatskim pakovanjem pretvara (omotava) proste tipove u objekte:


C h a r a c t e r ch = 'x ';

i automatskim raspakivanjem ponovo ih pretvara u proste tipove: char


c

= ch;

Razlozi za om otavanje (pretvaranje) prostih tipova u objekte bie prikazani dalje u knjizi.

Brojevi visoke preciznosti


Java sadri dve klase za obavljanje aritm etikih operacija visoke preciznosti: B iglnteger i BigDecimal. Iako one otprilike potpadaju pod istu kategoriju kao i om otake klase, nijedna nema analogni prost tip. O be klase imaju m etode koje obezbeduju operacije analogne onim a koje obavljate nad prostim tipovima. O dnosno, s klasama B iglnteger i BigDecimal moete uraditi sve to i sa tipovim a in t ili float, samo m orate koristiti m etode umesto operatora. A poto je zaatak komplikovaniji, operacije e liiti sporije. Brzinu zamenjujete tanou.

46

Misliti na Javi

Biglnteger podrava celobrojne vrednosti proizvoljne veliine. To znai da moete tano predstaviti celobrojne vrednosti bilo koje veliine, a da pri operacijam a ne izgubite informacije. BigDecimal slui za brojeve s pokretnim zarezom proizvoljne veliine; njih moete, na primer, koristiti za precizna novana izraunavanja. U dokum entaciji JDK potraite pojedinosti o konstruktorim a i m etodam a ove dve klase.

Nizovi u Javi
Praktino svi program ski jezici podravaju neku vrstu nizova. Korienje nizova u C -u i C + + - U rizino je jer su ti nizovi samo blokovi m em orije. Ako program pristupi nizu izvan njegovog m em orijskog bloka ili koristi m em oriju pre inicijalizacije (to su este greke u program iranju), doi e do nepredvidljivih situacija. Jedan od osnovnih ciljeva Jave je sigurnost; u njoj se uopte ne pojavljuju m nogi problemi koji mue program ere u C-u i C + + -u. U Javi je niz garantovano inicijalizovan i ne moe m u se pristupiti van njegovog opsega. Opseg se proverava po cenu male koliine dodatnog utroka m em orije u svakom nizu, kao i uz proveru indeksa pri izvravanju, ali se pretpostavlja da su sigurnost i poveana produktivnost vredni toga (a Java katkada moe da optim izuje te operacije). Kada pravite niz objekata, vi, u stvari, pravite niz referenci, i svaka od tih referenci autom atski se inicijalizuje na posebnu vrednost. Za tu vrednost koristi se rezervisana re null. Kada Java naie na null, ona prepoznaje da dotina referenca ne pokazuje na objekat. Pre nego to je upotrebite, svakoj referenci m orate da dodclite objekat, i ako pokuate da koristite referencu koja jo uvek ima vrednost null, problem e biti prijavljen pri izvravanju. Na taj nain su tipine greke s nizovima predupreene 11 Javi. Moete da napravite i niz prostih tipova. Ponovo, prevodilac garantuje ispravnu inicijalizaciju jer m em oriju za taj niz puni nulama. Nizovi e biti detaljnije objanjeni u narednim poglavljima.

Nikada ne morate da unitite objekat


Ostvarenje koncepta ivotnog veka prom enljive u veini program skih jezika vrlo je naporno. Koliko dugo traje promenljiva? Ako treba da je unitite, kada bi to trebalo da uradite? Konfuzija zbog ivotnog veka prom enljive moe da ovede do mnogih greaka, a u ovom odeljku pokazujem o da Java znatno pojednostavljuje ovo pitanje, obavljajui umesto vas sve ienje.

Oblast vaenja
Veina proceduralnih jezika poiva na oblasti vacnjn (engl. scope). O na odreuje vidljivost i ivotni vek imena definisanih u n u tar te oblasti. U C-u, C + + -u i Javi oblast vaenja je odreena vitiastim zagradam a { }. Na prim er:

Poglavjje 2: Sveje objekat

47

{
int X = 12; / / d o s t u p n a j e s am o p r o m e r l j i v a x

{
int q = 96; // d o s t u p n e su i x i q / / d o s t u p n a j e sa mo x / / q j e " i zvan ob l a s t i vaenja"

} Promenljiva definisana unutar oblasti vaenja dostupna je samo do kraja te oblasti. Sav tekst nakon / / pa do kraja reda predstavlja komentar. Uvlaenje pom ae da se program pisan u Javi lake ita. Poto je Java jezik slobodne forme, dodatni razmaci, tabulatori i novi redovi ne utiu na rezultujui program. U Javi ne tnoete da uradite sledee, iako je to dozvoljeno u C-u i C++-u: {
int x = 12;

{
int x = 96; // n e p r o p i s n o

} } Prevodilac e ol)javiti da je prom enljiva x ve definisana. Stoga mogunost C-a i C++a da sakrije promenljive u veoj oblasti nije dozvoljena, je rsu projektanti Jave mislili da to vodi do program a koji zbunjuju.

Oblast vaenja objekata


O bjekti u (avi nem aju isti ivotni vek kao prosti tipovi. Kada napravite objekat u Javi koristei operator new, on postoji i nakon kraja oblasti vaenja u kojoj je napravljen. Stoga, ako koristite: {
S t r i n g s = n ew S t r i n g } // kraj oblasti ("neki znakov ni niz"); vaenja

referenca s e nestati na kraju oblasti vaenja. M eutim, objekat klase String na koji je s ukazivala jo uvek zauzima mem oriju. U ovom deliu koda neina naina da pristupite objektu nakon oblasti vaenja, jer je jedina referenca na njega nedostupna izvan oblasti vaenja. Dalje u knjizi videete kako reference na objekte mogu da se prosleuju i um noavaju celim tokom programa. Poto se objekti napravljeni pom ou operatora new zadravaju dokle god su vam potrebni, ispostavlja se da mnotvo problem a u program iranju u C + + -u ne postoji u Javi. U C + + -u se m orate pobrinuti ne sam o za to da objekti postoje olde god su vam potrebni, nego ih m orate i unititi kada s njim a zavrite.

48

Misliti na Javi

Zbog toga se postavlja vano pitanje. Ako Java ostavlja objekte da lee unaokolo, ta ih spreava da prepune m em oriju i zaustave program? Upravo bi se ta vrsta problem a pojavila u C ++-u. Na ovom mestu deluje malo magije. Java ima sakuplja smea (engl. garbage collector); on vodi rauna o svim objektima koji su napravljeni operatorom new i otkriva one na koje vie nema referenci. Zatim oslobaa m em oriju koju su zauzimali ti objekti, pa ta m em orija moe da se koristi za nove objekte. To znai da nikada ne m orate sami da se brinete o oslobaanju memorije. Samo napravite objekte, a kada vam vie nisu potrebni, oni e sami otii. Ovo eliminie jednu klasu program skih problem a, takozvano curenje memorije (engl. memory leak), pri kome program er zaboravi da oslobodi m em oriju.

Pravljenje novih tipova podataka: kfasa


Ako je sve objekat, ta odreuje kako odreeni objekti izgledaju i kako se ponaaju? D rugim reima, ta uspostavlja tip objekta? Moete oekivati da postoji rezervisana re ,,tip, i to bi svakako imalo smisla. H ronoloki posm atrano, veina objektno orijentisanih jezika koristila je rezervisanu re class da naznai: Upravo u ti rei kako novi tip objekta izgleda. Tii rezervisanu re (koja je toliko esta da nee biti pisana polucrno dalje kroz knjigu) prati ime novog tipa. Na primer:
class NekoImeTipa { /* t e l o kl a s e ide o v d e */ }

Ovo uvodi nov tip, iako se telo klase sastoji samo od kom entara (zvezdica i kosa crta i ono to je izm eu tih znakova, o em u e biti rei alje u ovom poglavlju), pa malo ta moete s njom da uradite. M eutim, rnoete da napravite objekat ovog tipa koristei new:
NekoI m e T i p a a = new N e k o I m e T i p a ( ) ;

Ali klasi ne moete rei da mnogo toga uradi (tj. ne moete joj poslati neke vane poruke) sve dok ne definiete neke njene metode.

Polja i metode
Kada definiete lclasu (a sve to radite u Javi jeste definisanje klasa, pravljenje objekata tih klasa i slanje poruka tim objektim a), u nju moete staviti dve vrste elemenata: polja (koja se ponekad nazivaju i podaci lanovi) i metode (koje se katkada nazivaju fimkcije lanice). Polje je neki od prostih tipova ili objekat bilo kog tipa, s kojim moete da se poveete putem njegove reference. Reference na objekte m orate da indjalizujete (koristei new, kao to ste videli ranije), da biste ih povezali sa stvarnim objektima. Svaki objekat ima zasebno skladite za svoja polja; obino se polja ne dele izmeu objekata iste klase. Evo prim era klase s nekim poljima:
cla s s S a m o P o d a c i int i ; d o u b l e d; b o o l e a n b; {

Poglavlje 2 : Sve je objekat

49

Ova klasa ne radi nita, sem to uva neke podatke. Ali ovako moete da napravite objekat: SamoPodaci podaci = new SamoPodaci(); Poljima moete dodeliti vrednosti, ali prvo m orate da znate kako da se obratite lanu nekog objekta. To se postie tako to se navede im e reference na taj objekat, zatim sledi taka pa im e lana u nu tar objekta:
r e f e r e n c a N a O b j e k a t .polj e

Na prim er:
podaci .i = 4 7 ; p o d a c i . d = 1.1; p o d a c i . b = false;

Va objekat moe da sadri i druge objekte koji sadre podatke. Samo nastavite da ,,nadovezujete take. Na prim er:
m o j A v i o n . l e v i R e z e r v o a r . k a p a c i t e t = 100;

Klasa SamoPodaci ne moe skoro nita da uradi osim da uva podatke, jer nem a metode. Da biste shvatili metode, prvo m orate da nauite ta su argumenti i povratne vrednosti (ubrzo e biti opisani).

Podrazumevane vrednosti za podatke prostog tipa


Ako podatak prostog tipa ne inicijalizujete, on e dobiti podrazum evanu vrednost:
Prost tip Podrazumevana vrednost

boole<an char byte short mt long float double

talse "\u0000" (nullj |byte)0 (short)O 0 0L O.Of O.Od

Dobro obratite panju na to da Java jami dodelu podrazum evanih vrednosti samo kada se promenljiva koristi kao lanica klase. To obezbeuje da prom enljive lanice prostog tipa uvek budu inicijalizovane (to C ++ ne radi), ime se sm anjuje izvor greaka. Ipak, ova poetna vrednost ne m ora da bude ispravna niti odgovarajua za program koji piete. Najbolje je da uvek izriito inicijalizujete svoje promenljive.

50

Misliti na Javi

O va garan cija se ne o d n o si na lokalne pro m en ljiv e - o n e koje nisu polja klase. Stoga, ako u n u ta r definicije m eto d e im ate:
in t x;

x e im ati p ro izv o ljn u v red n o st (kao i u C -u i C + + -u ) i nee au to m a tsk i biti inicijalizovan n a nulu . Pre nego to u p o treb ite x, sam i m u m o ra te d o d e iiti o d g o v araju u v red n o st. Ako ste zaboravili, Java je uvela pob o ljan je u o d n o su na C + + : d o b iete greku p ri p rev o en ju koja vam govori da p ro m en ljiv a m o d a nije bila inicijalizovana. (M n o g i C + + prevodioci e vas u p o z o riti na neinicijalizovane prom enljive, ali u Javi se o n e sm a tra ju grekam a.)

Metode, argumenti i povratne vrednosti


U m n o g im jezicim a (recim o, C -u i C + + -u ), term in fu n kcija o p isu je im en o v an i p o tp ro g ram . U Javi se ee k o risti te rm in m etoda , kao n ain da se neto u ra d i. A ko elite, m o ete nastaviti da razm iljate o funkcijam a", ali e se u ovoj knjizi nad alje k o ristiti u Javi u obiajen te rm in ,,m eto d a. M etode u Javi o d re u ju p o ru k e koje objekat m oe da p rim i. O sn o v n i delovi m eto d e su im e, a rg u m en ti, p o v ra tn i tip i telo. O sn o v n i oblik ovako izgleda:
Po vratn iT ip imeMetode( /* l i s t a argumenata */ ) { /* te lo metode */

} P ovratni tip opisuje tip vred n o sti koja se vraa kao rezu ltat m eto d e koju ste pozvali. Lista a rg u m en ata zadaje tipove i im ena inform acija koje elite da p rosledite m etodi. Im e m etode i lista a rg u m en ata (koji zajedno ine njen potpis) jed in stv en o id entifikuju m etodu. M etode u Javi m o g u biti nap rav ljen e sam o kao deo klase. M etoda m oe da se poziva sam o za neki o b je k a t, i taj objek at m o ra da b u d e sp o so b an da izvri poziv. Ako po k u ate da pozovete p ro g re n u m e to d u objekta, d obiete p o ru k u o greci p rilik o m prevoenja. M eto du objekta pozivate tako to navedete im e olijekta iza koga je taka, zatim im e m etod e i n jenu listu a rg u m en ata, recim o ovako:
imeObjekta. im eMetode(argl, arg2, arg3);

Na p rim er, p re tp o stav im o da im ate m eto d u f() koja nem a arg u m e n te i vraa v red n o st tip a in t. Z atim , ako im ate o b jek at pod im en o m a koji im a m eto d u f(), m oete napisati sledee:
in t x = a . f ( ) ;

T ip p o v ra tn e v red n o sti m o ra da b u d e kom patib ilan s tip o m p rom enljive x. O vo p ozivanje m eto d e esto se naziva i slartjeporuke objektu. U p re th o d n o m p rim e ru , p o ru k a je f() i olijekat je a. O b je k tn o o rijen tisan o p ro g ra m ira n je esto se uk ratk o p red stavlja kao je d n o sta v n o slanje p o ru k a objektim a".

sta tic m etode, o kojim a ete uskoro uiti, rnogu hiti pozvauezd klnsii, bez objekta.

Poglavlje 2: Sveje objekat

51

Lista argumenata
Lista arg u m en ata m etode o dreduje koje inform acije pro sled u jete m etodi. Kao to p re tp o stavljate, te inform acije - kao i sve d ru g o u Javi - im aju fo rm u objekata. Stoga u listi argum en ata m o ra te da navedete tipove objekata koji se p ro sle u ju i im en a koja e se ko ristiti za svaki od njih. Kao i u d ru g im situacijam a u Javi gde se ini da rad ite sa o b jektim a, vi, u stvari, prosleujete reference.4 Tip reference m o ra da b u d e ispravan. A ko a rg u m e n t treba da b u d e objekat klase String, o nda m o rate da prosledite objek at tip a String ili e prevodilac prijaviti greku. R az m o trim o m e to d u iji arg u m en t je objek at tip a String. Sledi definicija m eto d e koja m o ra biti postavljena u n u ta r definicije ldase d a bi bila prevedena:
in t s k la d is te (S trin g s) { return s .le n g th () * 2;

} O va m eto d a pokazuje koliko bajtova je p o tre b n o za u vanje in fo rm acije u o d re e n o m o b jek tu tip a String. (Svaki znak u o b jek tu tip a String d u gaak je 16 b itova, o d n o sn o dva bajta, zbog p o d ravanja stan d ard a U nicode.) A rg u m en t je o b jek at tip a String p o d im en o m s. Kada se s prosledi u m eto d u , m oete ga tre tirati kao i svaki d ru g i objekat. (M oete m u slati p o ru k e.) O vde se poziva m eto da length(), kao jed n a o d m eto d a klase String; ona vraa bro j znakova u znakovnom nizu. O b ra tite panju na u p o tre b u rezervisane rei return koja obavlja dva zadatka. Prvo, o n a znai napusti m e to d u , zavrio sam . D rugo, ako m etod a proizvodi v red n o st, ta vrecJn o st se stavlja o d m a h iza rezervisane rei return. U ovom sluaju, p o v ra tn a v red n o st se proizvodi izraun avanjem izraza s.length() * 2. M oete da vratite p o d ata k bilo kog tipa, ali ako ne elite n ita da v ratite, o n d a n azn aite da m eto d a vraa void. Evo p rim era:
boolean in d ik a to r() { return tru e; } double osnovaPrirodnogLog() { return 2.718; } void n is t a () { retu rn ; } void n is ta 2 () { }

Kada je po vratni tip void, rezervisana re re tu r n se koristi sam o da bi se nap u stila m etoda i stoga je n ep o treb n a kada se d o e do kraja m etode. Iz m eto d e se m oete vratiti iz bilo koje take, ali ako ste naveli p ovratni tip koji nije void, prevodilac e vas, bez o bzira na m esto sa kog vraate, naterati (p o ru k am a o grekam a) da v ratite odgovarajui tip vred n o sti. U ovom tre n u tk u m oda izgleda da je p ro g ram sam o gom ila o bjekata sa m e to d a m a iji sli arg u m en ti dru gi objekti i koji alju po ru ke tim d ru g im o bjektim a. To se uglavnom i deava, ali u nared n o m poglavlju nauiete kako da urad ite posao niskog nivoa, d o n o en jem o dluk a u n u ta r m etode. Ako u ovom poglavlju savladate slanje p o ru k a - bie dovoljno.

Uz uobiajeno izuzim anje ranijc pom en utih prostih tipova podataka boolean, char, byte, short, int, iong, float i double. Io pravilu, vi prosletijete objekte, to znai da prosleujete reference na objekte.

52

Misliti na Javi

Pravljenje programa na Javi


Postoji jo nekoliko oblasti koje m o ra te savladati p re nego to sastavite svoj prvi p ro gram u Javi.

Vidljivost imena
K ontrola im en a je p ro b le m u svim p ro g ra m sk im jezicim a. Ako k o ristite im e u je d n o m delu p ro g ra m a, a d ru g i p ro g ra m e r k o risti isto im e u d ru g o m delu, kako da razlikujete je d n o im e o d d ru g o g i spreite d a se su d a re ? O vo je p o seb n o ozbiljan p ro b lem u C -u jer p ro g ram esto sadri m o re im en a n e p o d e sn ih za rad . C + + klase (n a k o jim a su zasnovane Java klase) funkcije u g n e u ju u n u ta r Idasa, pa zato i ne m o g u da se su dare sa im enim a funkcija u g n e en im u n u ta r d ru g ih ldasa. M e u tim , u C + + -u se i dalje koriste globalni p o d a ci i g lobalne funkcije p a je su d a ra n je jo uvek m ogue. D a bi se reio taj p ro blem , u C + + je p o m o u d o d a tn ih rezervisan ih rei uveden itnenski prostor (engl. namespace). Z ahvaljujui n ov o m p ristu p u , Java je sve to m ogla da izbegne. Za p ravljenje je d n o zn an ih im en a za biblioteke, p ro je k ta n ti Jave ele d a ko ristite im e vaeg In te rn e t d o m ena u o b rn u to m p o retk u , p o to su ta im en a sig u rn o jedinstvena. Poto je im e m og d om en a MindView.net, m oja u slu n a b ib lio tek a za o tld an jan je n ed o statak a dobila bi im e net.m indview.utility.foibles. Iza o b rn u to g im en a d o m e n a , ostale take treba da predstavljaju p o d d irek to riju m e. U Javi 1.0 i Javi 1.1, oznake d o m e n a com, edu, org, net itd. po konvenciji su pisane velikim slovim a, pa bi se b ib liotek a zvala: NET.mindview.utility.foibles. Tokom razvoja Jave 2 o tk riv en o je da to izaziva pro b lem e, pa se sada celo im e paketa pie m alim slovim a. O vaj m eh an izam znai da sve vae dato teke au to m atsk i ive u svojim im enskim prosto rim a i da svaka klasa u n u ta r d ato tek e g aran to v an o im a jed in stv en iden tifikator - o to m e se jezik b rin e u m esto vas.

Korienje drugih komponenata


Kad god elite da ko ristite u n a p red d efin isan e klase u svom p ro g ram u , prevodilac m ora znati da ih p ro n a e . N aravno, klase m o g u ve da postoje u istoj datoteci i/ koje se i pozivaju. U to m sluaju, sam o k o ristite klasu - ak i ako se o n a defim e tek kasnije u datoteci. Java elim inie p ro b le m s tak o zv an im istu ren im referencam a. ta ako klasa p ostoji u nekoj d ru g o j datoteci? Pom isliete kako bi p revodilac trebalo da b u d e dovoljno p a m eta n i da k rene i n a e je, ali tu postoji prob lem . Z am islite da hoete da koristite klasu o d re e n o g im en a, ali za nju postoji vie od jed n e definicije (p od pretp ostavkom da su razliite). Ili jo gore, zam islite da piete p ro g ram i u svoju biblioteku dodajete novu klasu, ije se im e sukobljava sa im en o m neke postojee klase. Da biste reili ovaj p ro b lem , m o rate da elim iniete sve potencijalne dvosm islenosti. To se postie tako to p o m o u rezervisane rei import, Java prev od io cu saoptite koje klase elite da koristite. im port govori prev o d io cu da uveze p a ket (engl. package) koji predstavlja b iblioteku klasa. (U d ru g im jezicim a, biblioteka p o red klasa m oe da sadri funkcije i pod atke, ali setite se da u Javi sav ko d m o ra biti pisan u n u ta r klasa.)

Poglavlje 2: Sve je objekat

53

N ajee ete k o ristiti k o m p o n e n te iz sta n d a rd n ih biblioteka koje se isp o ru u ju zajedn o s p rev o diocem . Uz njih, ne m o ra te d a b rin e te o dugakim , inv erto v an im im e n im a d o m en a; sam o napiete, n a p rim er:
import j a v a . u t i l, A r r a y L is t ;

kako b iste saoptili p rev o d io cu da elite d a k o ristite Javinu klasu ArrayList. M e u tim , p aket util sadri vie kJasa i m o ete poeleti da ko ristite nekoliko n jih a d a ih p ri to m izriito ne deklariete. To se lako p ostie o zn ak o m * za slo b o d an izbor:
import j a v a . u t i l

Skupove klasa ee ete uvoziti na ovaj n ain nego p o jed in an o .

Rezervisana re static
Kada n ap ra v ite klasu, o b i n o opisu jete kako o bjekti te klase izgledaju i kako e se p o n a ati. O b jek a t zapravo ne d o b ijate sve d o k ga ne n ap rav ite o p e ra to ro m n e w - u toj taki se o b je k tu d o d eljuje p ro s to r u m e m o riji (sldadite) i tek tad a m eto d e p o staju d o stu p n e . Postoje dvc situacije u ko jim a ovaj p ristu p nije dovoljan. Jedna je ako elite da im ate isto skladite za o d re d e n o polje, Liez o b zira na to koliko o bjekata te ldase n ap rav ite, ili ak elite da skladite p ostoji iako nije nap ravljen nijedan objekat. D ru g a je ako vam treba m e to d a koji nije p rid ru e n a n ijed n o m o d re e n o m o b jek tu te klase. O d n o sn o , tre b a vam m eto d a koju m oete da pozovete iako n ijedan o bjek at nije napravljen. O b a ova efekta m o ete postii p o m o u rezervisane rei static. Kada neto o znaite kao static, to znai da o d re d e n o polje ili m e to d a nisu povezani ni s je d n im o b jek to m te Jdase. Stoga m oete da pozovete m e to d u static ili da p ristu p ite polju static, iako nik ad a niste n aprav ili objekat te klase. U sluaju uob iajen ih , n estatin ih polja i m eto d a, m o ra te da n ap ra v ite objekat i da ga ko ristite za p ristu p po lju ili m e to d i. U n ekim o b je k tn o o rijen tisa n im jezicim a koriste se te rm in i podaci klase 'i m etode klase, to znai da podaci i m e to d e posto je sam o za klasu u celini, a ne i za o d re en e objekte klase. P onekad se u litera tu ri o Javi tak o e koriste ti term in i. l)a biste polje ili m eto d u proglasili za statin e, stavite rezervisanu re static p re definicije. Na p rim er, sledei oblik proizvodi i inicijalizuje statin o polje:
c la s s S ta tic T e s t { s t a t i c i n t i = 4 7;

} Cak i ako posle ovoga n ap rav ite dva objekta klase StaticTest, i dalje e posto jati sam o je d a n p rim e ra k skladita za p ro m en ljiv u StaticTest.i. O ba objekta e deliti istu p ro m e n Ijivu i.
N aravno, poto sta tic m etode nc zalitcvaju da ijedan objekat bude napravljen pre nego to e se koristiti, one ne m ogu iliivkliu) da pristupe nestatinim lanicam a ili m etodam a. Poto nestatini lanovi i m etode m oraju biti povezani sa odgovarajuim objektom , iz statinih m etoda m oete pristupati nestatinim lanovim a sam o preko nekog im en ovanogobjekta.

54

Misliti na Javi

R azm o trim o sledee:


S ta tic T e s t s t l = new S t a t i c T e s t ( ) ; S ta tic T e s t st2 = new S t a t ic T e s t O ;

U ovom tre n u tk u i stl.i i st2.i im aju istu v red n o st, 47, p o to se o d n o se na isti deo inem orije. Postoje dva nain a za p ristu p statin o j p ro m en ljiv o j. Kao to je po k azan o u p re th o d n o m p rim e ru , m o ete joj p ristu p a ti p rek o objekta, n a p rim e r st2.i. M oete joj p ristu p a ti i d ire k tn o preko im en a klase, to ne m o ete d a u rad ite sa n e stati n o m lanicom .
StaticT e st.i+ + ;

O p e ra to r + + uveava p ro m en ljiv u za 1. U o v o m tre n u tk u i stl.i i st2.i im ae i v red n o st 48. N avoenje im en a klase je poeljniji n ain za p ristu p a n jc statinim lanovim a. Ne sam o d a se tim e naglaava n jih o v a sta tin a p riro d a , n ego se u n ek im sluajevim a prevod io c u daje vea m o g u n o st za o p tim izo v an je. Slina logika se p rim en ju je i n a statin e m eto d e. S tatinoj m e to d i m oete da p ristu p a te bilo p rek o objekta kao i svakoj m eto d i, ili p o m o u p o seb n e d o d a tn e sintakse ImeKlase.metoda(). Statina m eto d a se definie na slian nain:
class MozeSeUvecati ( s t a t ic void u ve c a j() { S ta tic T e s t.i+ + ; )

} O b ra tite pan ju na to da u klasi MozeSeUvecati m eto d a uvecaj() p o m o u o p erato ra + + uveava statian p o d atak i. M eto d u uvecajf) m oete pozvati na uobiajen nain, preko objekta:
MozeSellvecati sp = new MozeSeUvecati ( ) ; s p .u v e c a j( ) ;

Poto je uvecaj() statina m eto d a, m o ete je pozvati d ire k tn o p rek o njene klase:
MozeSeUvecati. u ve caj( ) ;

Iako rezervisana re static, p rim e n je n a na polje, defin itiv n o m enja nain na koji se p o d a tak stvara (po jed an za svaku klasu u o d n o su na po jedan nestatian za svaki objekat), kada se p rim en i na m eto d u , p ro m e n e nisu tako velike. M etode se oznaavaju rezer visan o m reju static kako bi se m ogle pozvati iako nije naprav ljen objekat. O vo je veom a vano, kao to em o videti, za efinisanje m eto d e main() koja je p o etn a taka za pokretan je svih aplikacija.

Va prvi program na Javi


K onano, evo prv o g p o tp u n o g p ro g ram a. O n poinje ispisivanjem znakovnog niza, i datu m a, koristei klasu Date iz sta n d a rd n e |ava biblioteke.

Poglavlje 2: Sve je objekat

55

// Zdravo.java import ja v a . u t i l p ub lic c lass Zdravo { public s t a t ic void m a in (S trin g [] args) { S y ste m .o u t.p rin tln ("Z d ra v o , danas j e : " ) ; System .o ut.p rin tln(new D a t e ());

} } N a p o etk u svake dato tek e s p ro g ra m o m , m o ra te da navedete sve p o tre b n e n ared b e

im port d a biste uvezli sve p o tre b n e d o d a tn e klase. O b ra tite p a n ju n a to da sam rekao


,,do d atne je r po sto ji je d n a b ib lio tek a klasa koja se au to m a tsk i uvozi u svaki Java p ro gram : java.lang. P ok ren ite svoj W eb ita i p o g led ajte d o k u m en tac iju o Javinom razvojn o m o k ru en ju (JD K ). (Ako d o k u m e n ta c iju o JD K -u n iste ve preuzeli s lokacije h ttp :// java.sun.com , u ra d ite to sada.6 Im ajte u vid u da se ova d o k u m en tac ija ne isp o ru u je zajed n o s JD K -om , p a je m o rate zasebno p reu zeti.) U listi p aketa videete razne biblioteke klasa koje se isp o ru u ju u p ak etu s Javinim razv o jn im ok ru en jem . Izaberite p ak et java.Iang i dob iete listu svih klasa koje su deo te biblioteke. Poto je paket java.lang im plicitn o ukljuen u svalcu d a to tek u s Java k o d o m , te klase su a u to m atsk i d o stu p n e. Klasa Date nije deo paketa java.lang, to znai da je m o ra te uvesti kako biste je koristili. Ako ne zn ate u kojoj se biblioteci o d re e n a klasa nalazi, ili ako elite da v idite sve klase, o d ab erite ,,Tree u d o k u m en taciji o Javi. Saa ete videti sve klase Javine sta n d a rd n e biblioteke. Z atim m oete iskoristiti funkciju itaa ,,find da p ro n a ete klasu Date. Kada to urad ite, videete da se o n a na listi nalazi kao java.util.Date, to znai d a je u pak etu util i da m o rate da zadate im p o rt java.util.* kako biste koristili klasu Date. Ako se v ratite na poetak, izaberete java.lang i zatim System, videete da ldasa Systern im a nekoliko poija, a ako o d ab erete out, o tk riete da je to statian objekat ldase PrintStream. Poto je on statian , ne m o ra te nita da p rav ite p o m o u new. O bjekat out je uvek tu i m oete ga jednostavno k oristiti. O n o to m oete da rad ite sa ovim objektom out, o dreeno je njegovim tip o m : PrintStreani. P rak tin o sti radi, PrintStream je u opisu p rikazan kao hiperveza, stoga ako ga p ritisn ete, videete listu svih m eto d a koje m oete pozvati za tu klasu. Njih im a relativno m n o g o i bie o b jan jen e dalje u knjizi. Zasad nas zan im a sam o println(), to znai ispii o n o to ti dajem na konzoli i p red i u novi re d . Dakle, kad god u bilo kojem p ro g ram u neto elite da ispiete na konzoli, m o ete napisati:
System .out. p r in t ln ( "Znakovni niz za iz la z " )

im e klase i im e datoteke su isti. Kada pravite nezavisan p ro g ra m kao to je ovaj, je n a od klasa m ora da ima isto im e kao datoteka. (Prevodilac se b u n i ako tako ne uradite.) Ta klasa m ora da sadri m etodu pod im en o m main() sa sledeim p o tp iso m i p o v ratn im tipom :
p ublic s t a t ic void main (S tr in g G args) {

Prevodilac za Javu i Suiiova dokum tntacija stalno se m enjaju i najbolje ih je preuzim ati neposredno od Suna. Ako je sam i preuzm etc, dobiete najnoviiu verziju.

56

Misliti na Javi

R ezervisana re public oznaava d a je m e to d a jav n o d o stu p n a (to je d etaljn o o p isan o u poglavlju Kontrola pristupa). A rg u m e n t m eto d e main() jeste niz objek ata ldase String. A rg u m ente args ne ko ristim o u ovom p ro g ra m u , ali prevodilac in sistira da se tu nalaze jer se u njih sm etaju arg u m e n ti p ro sle en i s k o m a n d n e linije. Red sa ispisom d a tu m a p rilin o je zanim ljiv:
System .ou t.p rintln (n ew Date( ) ) ;

A rg u m en t je objekat klase Date koji se p rav i sam o da bi poslao svoju v re d n o st (koja se au to m atsk i p retv ara u objekat tip a String) m eto d i println(). im se ovaj izraz zavri, taj objek at vie nije p o tre b a n i sakuplja sm ea m oe da p ro e i p o k u p i ga u bilo kom tren u tk u . M i ne m o ra m o d a v o d im o rau n a o njeg o v o m ienju. K ada p ro u ite d o k u m e n ta c iju o JD K -u sa lokacije http://java.sun.com , videete d a Syste m im a m n o g e d ru g e m eto d e koje om o gu avaju izvoenje zan im ljiv ih efekata. (Jedna o d najveih p red n o sti Jave jesu njene b ro jn e stan d a rd n e biblioteke.) Na p rim er:
//: object/Show Properties.java pu b lic c la s s ShowProperties { public s t a t ic void m a in (S trin g [] args) { Syste m .g etPro p erties() .1 is t(S y s te m .o u t); System .out.pri n tl n(System .getProp erty("user.nam e")) ; Sy ste m .o u t.p rin tln ( System .getProperty( " j a v a . 1i b ra ry .p a th ")) ;

) } ///:-

Prvi red u m eto d i m a in () p rikazu je sva svojstva (engl. properties) sistem a na kojem izvravate p ro g ram , dakle daje p o d atk e o o k ru en ju . M etoda list() alje te rezu ltate svom a rg u m e n tu S y stem .o u t. V ideete u n astavku knjige da ih m oete poslati i na d ru g a m esta, recim o u neku d ato tek u . M oete zatraiti i o d re e n o svojstvo - u ovom sluaju, korisniko im e (engl. user nam e) i p u ta n ju (engl. path) do Javine biblioteke (engl. library). (M alo kasnije o b jasniem o n eo b in e k o m en tare na p o etk u i na kraju.)

Prevoenje i izvravanje
Da biste preveli i po k ren u li ovaj p ro g ram , kao i sve d rug e u ovoj knjizi, prvo m o ra te da im ate razvojno o k ruen je za Javu. Postoji vie nezavisnih razvojnih o k ru en ja, ali p retp o staviem o d a koristite b esp latn o razvojno ok ru en je JDK, k o m p an ije Sun M icrosystem s. Ako koristite neki dru g i razvojni sistem ,' m o raete da pogledate d o k u m en ta ciju za taj sistem d a biste saznali kako da p rev o d ite i pokreete p ro g ram e. Poveite se na In te rn et i p o gledajte W eb lokaciju http://iava.suii.coin. Tam o ete nai in form acije i veze koje e vas voditi kroz proces p reu zim an ja i in staliran ja )D K -a za vau p latfo rm u .
O bin o je to IBM -ov prevodilac jikes, koji je zn atn o bri od Sunovog javac (iako razlika nije velika kada p o m o u Anta pravite grupe datoteka). Postoje i progranii otvorenog koda za pravljenje Java prevodilaca, izvrnih okruenja i biblioteka.

Poglav[je 2: Sve je objekat

57

Kada instalirate JDK i podesite p u tan je na svom ra u n a ru tako da p ro g ra m i javac i java b u d u d o stu p n i, preu/.m ite i rasp ak u jte izvorni ko d za ovu knjig u (m o ete ga nai na adresi w w w .M indV iew .net). Tako ete n ap rav iti p o d d ire k to riju m za svako poglavlje iz ove knjige. Idite u p o d d ire k to riju m object i upiite:
ja va c H ello D ate.java

O va k o m a n d a ne bi trebalo da proizvede nikakav odziv. Ako d o bijete bilo k akvu p o ru k u o greci, znai da niste p ro p isn o instalirali JDK i tre b a da isp itate taj p rob lem . S d ru g e stran e, ako dobijete nazad sam o k o m an d n u liniju, m o ete d a otkucate:
ja v a HelloDate

i kao rezultat dob iete p o ru k u i d atu m . N avedeni proces m oete da koristite za p rev o en je i p o k re tan je svakog p ro g ra m a iz ove knjige. M e u tim , videete da izvorni ko d ove knjige u svakom p oglavlju tak o e im a i d a to tek u p o d im en o m bild.xml, koja sadri ,A n t k o m a n d e za a u to m atsk o prev o enje d ato tek a iz tog poglavlja. A utom atski prevedene d ato teke i A n t (kao i to odak le d a ga p reu zm ete) detaljnije su opisani u d o d a tk u koji ete nai na http ://M ind V iew .n et/B oo ks/ Betterjava , ali nakon to instalirate A nt (sa http://jakarta.apache.org/ant ), d o v o ljno je da otk u cate ant n akon k o m a n d n o g odzivnika i p ro g ra m i iz svakog poglavlja bie prevedeni i p o k re n u ti. U koiiko jo niste instalirali A nt, k o m a n d e javac i java o tk u ca jte run o.

Komentari i ugraena dokumentacija


Postoje dva tipa k o m en tara u Javi. Prvi je tra d icio n aln i stil pisanja k o m e n ta ra jezika C, koji je nasledio i C + + . Ti k o m en tari p o in ju sa I* i nastavljaju se, m og ue i u vie redova, sve d o */. O b ra tite panju na to da m nogi p ro g ram eri svaki red k o m e n ta ra nastavljaju sa 4. C esto ete videti:
/* * * Ovo j e komentar koji se n a s ta v lja u v i e redova

*/ D o bro z ap am tite da se sve to se nalazi u n u ta r /* i */ ignorie, stoga n em a razlike ako napiete:


/* Ovo j e komentar koji se n a s ta v lja u v i e redova */

D ruga vrsta k o m en tara potie iz C + + -a. To je k o m e n ta r u je d n o m redu , koji poinje sa // i nastavlja se d o kraja reda. Ovaj nain k o m en tara je p rak tian i esto se koristi jer je jed n o stav an . Ne m o ra te da lovite p o tastatu ri i da traite / a zatim * (u m esto toga, sam o d v a p u t p ritisn ite isti taster) i ne m o rate da zatvorite k o m en tar. Z ato ete esto susretati:
// ovo je komentar u jednom redu

58

Misliti na Javi

Dokumentacioni komentari
V erovatno da je najvei p ro b lem p ri d o k u m e n to v a n ju koda bilo o d ravanje d o k u m e n ta cije. A ko su k o d i d o k u m en tacija razdvojeni, po staje n ezg o d n o m en jati d o k u m en tac iju svaki p u t kada p ro m e n ite kod. Reenje izgleda jed n o stav n o : poveite k o d i d o k u m e n ta c iju . N ajlake ete to u ra d iti kad sve stavite u istu d ato tek u . D a biste zaokruili p o stu p ak , p o tre b n a je p o seb n a sintaksa za d o k u m e n ta c io n e k o m en tare, kao i alat kojim ete te kom e n ta re izdvojiti i preb aciti ih u k o ristan oblik. To je u rad ila Java. Alat za izdvajanje k o m en tara naziva se Javadoc i deo je instalacije JDK-a. O n koristi neke o d tehnologija Java prevodioca da p o trai posebne oznake k o m en tara u p ro g ram u . Izdvaja oznaene inform acije i izvlai im e klase ili m etode koja ide uz taj kom entar. Na ovaj nain m oete sa m in im aln o m koliinom rad a generisati p risto jn u doku m en taciju p rogram a. R ezultat ovog p o stu p k a je H T M L d atotek a ko ju m oete da p regledate p o m o u itaa W eba. Tako Javadoc om oguava d a na p ra v ite i odrav ate sam o je d n u izv o rn u d ato te k u i a u to m atsk i generiete k o risn u d o k u m en taciju . Z ahvaljujui p ro g ra m u Javadoc im am o je d n o stav an sta n d a rd za pravljenje o k u m en tacije, pa m o em o da o ekujem o ili ak zah tev am o d o k u m en ta c iju iz svih Java biblioteka. Uza sve, m o ete n ap isati sopstvene Javadoc id en tifikato re (engl. handlers ), doclete, ukoliko k o m en tare koje je izdvojio Javadoc hoete p o seb n o da o b rad ite (recim o, da ih ispiete u d rug aijem fo rm a tu ). O d ocletim a itajte u d o d a tk u na h ttp ://M in d V iew .n et/ Books/Betterjava. Sledi uvod u Javadoc i pregled njegovih obeleja. P o tp u n opis nai ete u d o k u m e n ta ciji o JD K-u. Kada je raspakujete, p o tra ite p o d d ire k to riju m tooldocs" ili p ritisn ite istoim en u hipervezu.

Sintaksa
Sve k o m and e p rog ram a Javadoc nalaze se iskljuivo u n u ta r k o m entara /**. K om entar se zavrava sa */ kao i obino. Postoje dva osnovna naina za korienje ovog sistem a: ugraeni H TM L ili korienje d o k u m en tacio n ih oznaka (engl. doc tags). Samostojee dokum entacion eo zn a k e su ko m an d e koje p o in ju sa @ i koje se nalaze na poetku reda kom entara. (Zanem aruje se vodea zvezdica.) Nesam ostalnc dokum entacionc oznake m ogu biti napisane bilo gde u n u ta r Javadoc k o m en tara i takode poinju sa @, ali se nalaze izm eu vitiastih zagrada. Postoje tri tipa d o k u m e n ta c io n ih k o m en tara koji o d govaraju elem en tu k om e kom enta r p reth o di: klasi, polju ili m etodi. K om en tar klase stoji o d m ah ispred d e fin i je klase; k o m e n ta r polja se pojavljuje o d m a h ispred definicije polja, a k o m e n ta r m etode se javlja n e p o sred n o ispred definicije m etode. O vo je jed n o stav an prim er:
//: object/Docum entationl.java /** Komentar klase */ public c lass Docum entationl.java { /** Komentar p o lja */ publi c i nt i ; /** Komentar metode */ p ublic void f ( ) {}

} ///: -

Poglavlje 2: Sveje objekat

59

O b ra tite p an ju na to d a e Javaoc o b rad iti d o k u m en tac io n e k o m en tare sam o za javne i zatiene lanice. K o m entari za privatne lanice i lanice k ojim a se p ristu p a u pak etu (videti poglavlje Kontrola pristupa ) ig n o riu se i neete v id eti nikakav izlaz. (M e u tim , m o ete k o ristiti in d ik a to r -private da b iste ukljuili i privatne lanice.) O vo im a sm isla, p o to su sam o javne i zatiene lanice d o stu p n e izvan d atoteke, to je i p ersp ek tiv a p ro g ra m e ra klijenata. R ezultat o b ra d e p re th o d n o g p rim e ra je H TM L d ato tek a koja im a isti sta n d a rd n i form a t kao i celok u pna ostala d o k u m en tacija, stoga e k o risn icim a delovati p o z n a to i m oi e lako da se kreu k ro z klase. P rekucajte taj p rim er, p ro p u stite ga k ro z Javadoc i pregled ajte rezu ltu ju u H T M L d ato tek u , d a biste sam i videli o p isan e rezultate.

Ugraeni HTML
Javadoc prep isu je H TM L oznake iz k o m en tara u H T M L d o k u m e n t koji generie. To o m og u ava p o tp u n o korienje H TM L-a; m e u tim , o sn ov n i cilj je da se o m o g u i fo rm atira n je koda, kao to je:
// : object/Documentation2.java

* <pre> *System .out. p rin tln(n ew Date( ) ) ; * </pre>

*/ ///: '
p u b lic c lass Documentation2 {}

Takoe, H TM L m oete koristiti kao i u svakom d ru g o m W eb d o k u m e n tu , da fo rm atirate obian tekst u svojim opisim a:
//: object/Documentation3.java

/**
* * * * * * Moete u b aciti <em>ak</em> i li s t u : <ol> <li> Prva taka <1i> Druga taka <1i> Trea taka </ol>

*/
pu b lic c lass Documentation3 {}

O b ra tite panju na to da u n u ta r d o k u m en tacio n o g k o m e n ta ra , Javadoc o d b acu je zvezdice na poetk u reda, kao i vodee prazn in e. Javadoc sve p o n o v o fo rm a tira tako da se prilagod i sta n d a rd n o m izgledu d o k u m en tacije. Ne koristite naslove kao to su < h l > ili oznake kao < h r> , je r Javadoc ubacuje svoje naslove i vai e ih o m etati. Svi tipovi d o k u m en tac io n ih k o m en tara - k o m e n ta r klase, polja i m e to d e -p o d r a v a ju ug ra d en i HTM L.

60

Misliti na Javi

Primeri oznaka
Evo p rim e ra n ekih Javadoc ozn ak a koje se m o g u stavljati u d o k u m e n ta c iju koda. Pre nego to p o m o u Javadoca p o k u ate d a u ra d ite bilo ta ozbiljno, treb alo bi da p ro itate n jem u posveen odeljak u d o k u m e n ta ciji JD K -a i tam o vid ite sve m ogue nain e u p o tre b e Javadoca.

@see:
O va o zn ak a slui za u p u iv an je na d o k u m e n ta c iju u d ru g im klasam a. Javadoc e generisati HTML sa o zn ak am a @see kao hip erv ezam a po vezanim s d ru g o m d o k u m en tacijo m . O blici su:
@see imeklase Psee potpuno-opisano-imeklase @see potpuno-opisano-imeklase#ime-metode

Svaki o d njih do d aje g en erisan o j d o k u m en taciji h ipervezu Pogledaj tak o e (engl. See Also). Javadoc ne p ro v erav a isp rav n o st hiperveza koje joj predajete.

{@link

paket.klasa#!an labela}

V eom a slino o znaci @see, sem to se m oe ko ristiti d ire k tn o u istom nivou i to kao tekst hiperveze ispisuje labelu , a ne Pogledaj tako e.

{docRoot}
D aje relativ n u p u ta n ju d o koren sko g d irek to riju m a d o k u m en tacije. Koristi se za izriito h iperpovezivanje sa stra n ic a m a u stablu do k u m en tacije.

{@inheritDoc}
Tekui d o k u m en tac io n i k o m e n ta r n asleuje d o k u m en tac iju najblie o sn o v n e klase ove klase.

@version
N jen oblik je:
Pversion in fo rm a c ija - o - v e rz iji

gde je informacija-o-verziji bilo koja b itn a inform acija koju treba ukljuiti. Kada sc ind ik a to r -version navede na k o m a n d n o j liniji p ro g ram a javadoc , inform acija o verziji e biti p ro sle en a u gen erisan u H T M L d o k u m en taciju .

@author
N jen oblik je:
@author inform acija-o-autoru

Poglavlje 2: Sveje objekat

61

gde je informacija-o-autoru v ero v atn o vae im e ali m oe da b u d e i vaa elektronska adresa ili bilo koja d ru g a p rig o d n a inform acija. Kada se in d ik a to r -author navede u kom a n d n o j Iiniji p ro g ra m a Javadoc, inform acija o a u to ru bie p rosleena u g en erisan u H T M L d o k u m en tac iju . M oete im ati vie auto rsk ih oznaka za listu au tora, ali one m o ra ju da b u d u p o re an e uzastopno. Sve inform acije o au to rim a bie spojene u jed an pasus u g enerisanom H TM L-u.

@since
O va oznaka o m og u ava da naznaite verziju klase koja je poela da k oristi o d re e n u m o gunost. V ideete da se pojavljuje u Java H T M L d o k u m en taciji da naznai koja je verzija JD K -a koriena.

param
K oristi se za d o k u m e n to v a n je m e to d a u obliku:
@param ime-parametra opis

gde je ime-parametra id en tifik ato r u listi p a ra m e ta ra m eto d e, a opis tekst koji se m oe p ro te g n u ti na nekoliko redova. S m atra se da je opis zavren kada se naie n a n o v u d o k u m e n ta c io n u o zn aku . M oete im ati koliko ho ete ovih oznaka, najee p o je d n u za svaki param etar.

@return
Koristi se za d o k u m e n to v a n je m eto d a, a pie se u obliku:
Pretu rn opis

gde o p is daje znaenje p o v ra tn e v red n o sti. O n m oe da se p ro teg n e na nekoliko redova.

@throws
O izuzecim a g o v o rim o u poglavlju O brada grcaka pom ou izuzctaka. U kratko, to su objekti koji m og u biti ,,baeni iz m eto d e u sluaju greke. Iako sam o je d an objekat izuzetka m oe d a se pojavi kada pozovete m eto d u , o d re en a m eto d a m oe da proizvede vie razliitih tipova izuzetaka a svi m o raju biti u n a p red naznaeni. Stoga oznaka za izuzetak im a sledei oblik:
@throws potpuno-opisano-imeklase opis

gde p o tp u n o -o p is a n o -im e k la s e nedvosm isleno daje iine klase izuzetka koja je negde definisana, a o p is (koji m oe da se p ro teg n e na nekoliko redova) pokazuje zbog ega odreeni tip izuzetka m oe da se pojavi pri pozivu inetoe.

62

Misliti na Javi

deprecated
O vo se koristi da naznai m o g u n osti koje su zastarele. O znaka @deprecated pokazuje da vie ne treba da koristite tu m ogunost, p o to e uskoro v erovatno biti u klonjena. Prevodilac e vas upozoriti ako koristite m e to d u oznaenu sa @deprecated. U Javi SE5, @deprecated je zam enjena anotacijom @Deprecated (anotacijam a je posveeno poglavlje Anotacije).

Primer dokumentacije
P onovo dajem o prvi p ro g ra m n a Javi, ovog p u ta s d o d a tn im d o k u m e n ta c io n im k o m en tarim a:
//: o b ject:Z d ravo .java import j a v a .u t il /** Prvi primer programa iz knjige M i s l i t i na J a v i . * Is p is u je znakovni niz i dananji datum. * @author Bruce Eckel * @author www.MindView.net * @version 4.0

*/
public class Zdravo { /** Ulazna taka u klasu i a p lik a c iju * @param args niz argumenata tip a strin g * @throws exceptions ne baca izuzetke

*/
public s t a t ic void m a in (S trin g [] args) { Syste m .o u t.p rin tln ("Z d ravo , danas j e : " ) ; System .out. p rin tln(new D a te O );

1
} /* Is p is : (55% match) Zdravo, danas je : Tue Feb 06 14:39:36 M DT 2007

* ///:U prvom redu datoteke koristi se m oja tehnika: stavlja se //: kao posebne oznake za red s kom entarom koji sadri ime izvorne datoteke. Taj red sadri inform aciju o p utanji do datoteke (object oznaava ovo poglavlje), iza koje sledi ime datoteke. Poslednji red takode se zavrava kom entarom ( / / / : - ) koji oznaava kraj listinga izvornog koda i om oguava da se taj kod autom atski aurira u tekstu ove knjige (nakon to ga prevodilac p ro v eri) i da se izvri. O znaka /* Ispis: naznauje poetak izlaza koji e ovaj p ro g ram generisati. U ovom obliku, (55% nratch) naznauje sistem u za testiranje da se izlaz prilin o razlikuje od jed n o g do drugog izvravanja i da treba da oekuje korelaciju od sam o 55 pro cen ata sa ovde prikazanim izlazom. O d onih p rim era u ovoj knjizi koji im aju neki izlaz, veina e sadrati ovaj oblik izlaza, pa ete m oi da izvrite svaki pro g ram i da proverite da li m u je izlaz ispravan.

Poglavlje 2: Sve je objekat

63

Stil programiranja
P rem a stilu o p isano m u knjizi Codc C onventions fo r the Java Program m ingLatiguage8 prvo slovo im en a klase treba da b u d e veliko. A ko se im e klase sastoji o d nekoliko rei, o n e se piu zaje dn o (tj. ne koristite d o n je crte d a razdvojite im en a ), i prvo slovo svake u g rad en e rei je veliko, na p rim er:
c lass SveBojeDuge { // . . .

Tako n ap isano im e klase lii n a grbe kam ile. Za skoro sve ostalo: m eto d e, p olja (p ro m enljive lanice) i im ena referenci n a objekte, usvojeni stil je isti kao i za klase, osim to se p rv o slovo identifikatora pie m alo. N a p rim er:
c lass SveBojeDuge { in t c e o B ro jK o jiP re d s ta v lja B o je ; void promeniNijansuBoje ( in t novaNijansa) {

// } / / } M islite na to kako k o risn ik tak o e m o ra da kuca sva ova ugaka im ena, stoga im ajte m ilosti. U Java kodu iz Sunovih b iblioteka k oristi se isti stil p isanja o tv o ren ih i zatv o ren ih vitiastih zagrada p o p u t stila u p o treb ljen o g u ovoj knjizi.

Saetak
Cilj ovog poglavlja je da nau ite tek toliko Jave koliko je dov o ljn o da biste napisali je d n o stavan p ro g ram . Stekli ste i uvid u jezik i njegove o sn o v n e pojm ove. Do sada su ipak svi p rim eri bili u znaku uradi ovo, zatim u rad i o n o , a p o to m neto tree. U n a re d n a dva p oglavlja upoznaete o sno v n e o p e rato re koji se u p o treb ljav aju u p ro g ra m ira n ju na Javi i nauiete da upravljate tok o m p ro g ram a.

Vebe
U b ud ue e vebe biti raspodeljene po celom tek stu poglavlja, ali u ovom su sve vebe stavljene na kraj, poto ste tek nauili da piete najjed n o stav n ije p ro g ram e. Iza red n o g broja vebe nalazi se broj u zag rad am a, koji u op seg u od 1 do 10 pokazuje njenu teinu. Reenja o d a b ra n ih vebi nalaze se u elek tro n sk o m d o k u m e n tu The T hinking in Java A nno tatcd Solution Guide , koji se m oe k u p iti na adresi w w w .M indV iew .net.

luip://javii.sitii.Coiii/iIocs/ca<.coiiv/indcx.litinl. N isam m ogao da sledim sve sm ernice iz ove preporuke jer bi m i to uzelo previe prosfora u knjizi i na prezentacijam a, ali uveriete se da ovde upotrebljeni stil odgovara stan d ardu Jave koliko god je to m ogue.

64

Misliti na Javi

Veba 1: (2) N apiite klasu koja e sad rati neinicijalizovana p o lja tip a int i char, i ispiite
njihove v red n o sti kako biste se uverili da ih Java p o d ra z u m e v a n o inicijalizuje.

Veba 2: (1) N akon p rim e ra Zdravo.java u ov om poglavlju, n a p ra v ite p ro g ra m zdravo,


svete koji p rikazu je d a tu n a re d b u na ek ran u . P o treb n a vam je sam o je d n a m e to d a u klasi (m a in koja se izvrava kada se p ro g ra m p o k re n e). Setite se da tre b a d a o stan e statina i da ukljuite listu a rg u m en ata, iako je neete koristiti. Prevedite p ro g ra m k o m a n d o m javac i p o k ren ite ga k o m a n d o m java. A ko k o ristite razvojno o k ru en je koje je d ru g aije od JD K -a, n au ite kako d a prevedete i izvrite p ro g ra m e u to m o k ru e n ju .

Veba3: (1) P ro n a ite delove k o d a koji o b u h v ataju klasu NekoImeTipa i p re tv o rite ih u


p ro g ra m koji m oete d a p revedete i izvrite.

Veba 4: (1) P retvo rite delie k od a koji o b u h v ataju klasu SamoPodaci u p ro g ra m koji
m oete d a prevedete i izvrite.

Veba 5: (1) Izm enite p re th o d n o vebanje tako d a v red n o sti p o d a ta k a u klasi SamoPodaci b u d u d o d eljen e i ispisane iz m eto d e main(). Veba 6: (2) N apiite p ro g ra m koji o b u h v ata i poziva m e to d u skladiste(), d efm isan u kao
deo k o da u ovom poglavlju.

Veba 7: (1) Pretvorite MozeSeUvecati delove k o d a u p ro g ra m koji radi. Veba 8: (3) N apiite p ro g ra m koji po k azu je da postoji sam o je d a n p rim e ra k o d re en o g statinog polja u klasi, bez o b zira na to koliko objekata te klase n apravite.
Veba 9: (2) N apiite p ro g ra m koji po k azu je da a u to m atsk o pakovanje fu n k cio n ie za sve pro ste tipove i njihove o m otae. V eba 10: (2) N apiite p ro g ra m koji ispisuje tri a rg u m e n ta p re u ze ta s k o m a n d n e linije. Da biste to uradili, pozabavite se ind ek sim a u nizu objekta klase S trin g s k o m a d n e linije. Veba 11: (1) Pretvorite p rim er SveB ojeD uge u pro g ram koji m oete da prevedete i izvrite. Veba 12: (2) P ro n a ite ko za d ru g u verziju p ro g ram a Z d rav o .ja v a, koji je jednostavan p rim e r d o k u m e n ta c io n ih k o m e n ta ra. P rop ustite tu d ato tek u k ro z Jav ad o c i pogledajte rezultate p o m o u itaa W eba. Veba 13: (1) P ro p u stite kroz Jav ad o c dato teke D o k u m en tacija 1.java, D o k u m en tacija2.java i D okum entacija3 .jav a, i p ro v erite rezultate p o m o u itaa W eba. V eba 14: (1) D od ajte H T M L listu d o k u m en taciji u p re th o d n o m vebanju. Veba 15: (1) U zm ite p ro g ra tn iz vebe 2 i d o d ajte m u d o k u m e n ta c io n e k o m en tare. Izdvojite te d o k u m e n ta c io n e k o m e n ta re u H TM L daiOteku koristei Jav ad o c i pregledajte ih p o m o u itaa W eba. V eba 16: (1) U poglavlju Inicijnlizacijn i ienje p ro n a ite p rim e r P re ld a p a n je .ja v a i dodajte m u d o k u m e n ta c io n e k o m en tare . Izdvojite te d o k u m e n ta c io n e k o m e n ta re u H TM L d ato te k u koristei Jav ad o c i p regledajte ih p o m o u itaa W eba.

Operatori
N a n ajniem nivou, u Javi se s podacim a radi pom ou operatora.
KAKO JE JAVA IZVEDENA IZ C + + - A , VEINA OVIH OPERATORA BIE POZNATA C I C + + p ro g ram e rim a . U Javi su d o d ata neka pob o ljan ja i pojednostavljenja. U koliko poznajete sin tak su C -a ili C + + -a, m o ete p releteti kroz ovo i sledee poglavlje i zadrati se sam o na m estim a gde se Java razlikuje o d tih jezika. M e u tim , ako m alo zapnete u ovom poglavlju, p ro ite kroz m u ltim ed ijaln i se m in a r na C D -u: T h in k in g in C k o ji m oete b esp latn o preuzeti s Web lokacije w w w .m in d view .n et. O n sadri a u d io lekcije, slajdove, vebe i reenja iz kojih se m o g u stei osnove za u en je Jave.

Jednostavnije naredbe za ispisivanje


U p re th o d n o m pogiavlju, u p o zn ali ste Javinu n a re d b u za ispisivanje:
S y s te m .o u t.p rin tln (''P r ili n o mnogo kucanja1 ') ;

Irim etiete da tu im a ne sam o p rilin o m n o g o k u can ja (i suvinog n a p o ra za tetive), nego je i p rilino nerazum ljivo kada se p roita. Veina jezika starijih i n ovijih o d Jave im a m n o g o jednostavniji p ristu p tako esto u p o treb ljav an o j naredbi. U poglavlju Kontrola pristupa , govori se o p o jm u static im port iz Jave SE5; uz to je navedena i m ala biblioteka koja p o jednostavljuje pisan je n ared b e za ispisivanje n a e k ra n u / p a p iru . M eu tim , ne m o rate znati te p o jed in o sti da biste m ogli da je upotreb ljav ate. Koristei je, p rerad iem o p ro g ram Iz p re th o d n o g poglavlja:
//: operatori/ZdravoDatum .java import j a v a .u t il import s t a t ic n et.m in d view .u ti1. P r in t . *; p ub lic class ZdravoDatum { public s t a t ic void main( S t r i ng[] args) { p rin t("Z d ra v o , danas je : " ) ; print(new Date( ) ) ;

}
} /* Is p is : (55% match) Zdravo, danas je : Wed Oct 05 14:39:05 MDT 2005

* ///:R ezultati su m nogo jasniji. O b ratite p anju na u m e tn u tu rezervisanu re sta tic u d ru goj n ared b i im p o rt. Da biste tu biblioteku m ogli da koristite, m o rate preuzeti paket koda ove knjige s lokacije w w w .M iiidV icw .net ili s nekog od n jenih p reslik an ih servera. R airite stablo koda i d o d ajte njegov korenski d irek to riju m sistem skoj pro m en ljiv o j CLASSPATH svog ra u n a ra. Jednom em o stii i do p o tp u n o g opisa p u ta n je klasa (engl. classpath), ali nee koditi

66

Misliti na Javi

ako o d m a h ponete d a se privikavate n a b o rb u s n jo m . N aalost, s Javom ete m o rati poee d a se borite. Iako u p o tre b a biblioteke net.mindvievv.util.Print lepo p ojenostavljuje veinu p ro gram a, ne m oe se o p rav d ati n jen o korienje ba posvu da. U koliko u p ro g ram u im a m alo n a red ab a za ispisivanje, p resk aem uvoz (engl. irnport ) te biblioteke i piem dugaki

System.out.println (). Veba 1: (1) N apiite p ro g ra m u k o jem up otreb ljav ate i skraen i i n o rm a ln i oblik naredaba za ispisivanje.

Korienje operatora u Javi


O p e ra to r k o m b in u je je d a n ili vie a rg u m en ata i daje no vu v red n o st. A rgu m enti se zadaju u obliku drugaijem o d uobiajen ih poziva m e to d a, ali efekat ostaje isti. V erovatno vam je d o b ro p o z n at o p ti k o n cep t o p e rato ra . S abiranje i u n a rn i plus (+ ), o d u zim an je i u n arn i m in u s (-), m n oenje (*), deljenje (/) i do dela v red n o sti (=) m anje-vie su isti u svakom p ro g ram sk o m jeziku. Svi o p e ra to ri d aju n ek u v red n o st u zavisnosti o d svojih o p e ra n a d a . Pored toga, o perato r m oe da p ro m en i i v re d n o st o p eran d a. To se naziva sporedan efckat (engl. sidc effect). O p erato ri koji m enjaju svoje o p e ra n d e najee se u p o treb ljav aju ba radi generisanja tog sp o red n o g efekta, ali treb a im ati na u m u d a i ti o p e ra to ri proizvode v redn ost, kao i operato ri bez sp o red n o g efekta. Skoro svi o p e ra to ri rade sam o s p ro stim tip o v im a. Izuzeci su =, = = i !=, koji rade sa svim o bjektim a (i izazivaju n e d o u m ic e u ra d u sa o b jek tim a). Pored toga, klasa S trin g podrava i o p e rato re + i +=.

Priori teti
P rioriteti o p erato ra definiu kako e izraz biti izrau n at kada se u n jem u javlja vi.e operato ra. Java im a specifina p ravila koja o d re u ju p o red ak izraunavanja. Najlake se p am ti pravilo da se m n o en je i deljenje vre p re sab iranja i o d u zim an ja. P ro gram eri esto zaborave ostala pravila o p rio rite tim a , p a stoga nije na o d m e t da koristite zagrade za izriito navoenje p o retk a izraun av an ja. Na p rim er, p ogledajte n ared b e (1) i (2):
//: o p e r a t o r i/ P r io r it e t i.java public c lass P r io r i t e t i { public s t a t ic void main ( S t r i ng[] args) { in t x = 1, y = 2, z = 3; in t a = x + y - 2/2 + z; // (1) in t b = x + (y - 2)/(2 + z ); // (2) System .o u t.p rin t1 n ("a = " + a + " b = " + b ) ;

I
} /* Is p is : a = 5 b = 1

* ///:-

Poglav[je 3: Operatori

67

Te n ared b e izgledaju gotovo jed n ak o , ali iz izlaza vidite d a zbog u p o treb e zagrada u (2) im aju sasvim razliita znaenja. O b ra tite pan ju na u p o tre b u o p e ra to ra + u n ared b i System.out.println(). U to m k o n tekstu , + znai nadovezivanje zn ak o v n ih nizova i, ako treb a, konverzija zn ak o v n ih nizova. Kada p revodilac n ai e na String + ne-String, p ok u ae d a k o n v ertu je ne-String u String. Kao to vid ite iz izlaza, a i b je u sp en o konv erto vao iz tip a int u String.

Dodela vrednosti
V red n ost se dodeljuje o p e ra to ro m = . O n znai u zm i v red n o st s d esn e stra n e koja se esto naziva dvrcdnost (engl. rvalue), i kopiraj ie n a levu stra n u , koja se esto naziva Ivrednost (engl. lvalue) . D vrednost je bilo koja k o n sta n ta, p ro m en ljiv a ili izraz koji m oe d a se izraun a, ali Ivrednost m o ra da b u d e p o seb n a im en o v an a pro m en ljiv a. (O d n o sn o , m o ra p o sto jati fiziki p ro sto r za sm etan je te v red n o sti.) N a p rim er, m o ete d odeliti k o n sta n tn u v red n o st prom enljivoj a = 4; ali k o n sta n tn o j v red n o sti ne m o ete n ita da d o d elite - o n a n e m oe b iti Ivrednost. (N e m o ete n apisati 4 = a;.) D odela v red n osti prosto g tip a je p rilin o oigledna. Poto p ro sti tipovi uvaju stvarnu v red no st, a ne referencu na objekat, kada d o d elite v red n o st p rom enljivoj pro sto g tipa, vi k o p irate sadraj s jed n o g m esta na d ru g o . Na p rim er, ako napiete a = b za proste tipove, o n d a se saraj p rom enljive b ko p ira u sadraj pro m en ljiv e a. Ako n akon toga nastavite da m en jate a, ta izm ena, narav n o , nee uticati na b. Kao p ro g ram er, u veini situacija ovo i oekujete. M e u tim , kada d odelju jete v re n o sti o b jek tim a, stvari se m en jaju . D ok rad ite sa ob jek to m , vi radite s referencom , a kada d o d elju jete jedan objekat d ru g o m , zapravo kopirate referencu s je d n o g rnesta na drugo. To znai da e, kada napiete c = d za objekte, i c i d pokazivati na objekat na koji je p rv o b itn o pokazivao d. Evo p rim e ra koji e pokazati to ponaanje:
//: operatori/D odela . java // Dodela vrednosti objektima moe ponekad da p r e v a r i. import s t a t ic n e t,m in d view .u ti1. P r i n t . c lass Rezervoar { in t nivo;

}
p ub lic c lass Dodela { p ub lic c t a t ic void m a in (S trin g [] args) Rezervoar r l = new R e z ervo arf); Rezervoar r2 = new R ez erv o ar(); r l.n iv o = 9; r2 .n ivo = 47;

68

Misliti na Javi

p r i n t ( " l : r l.n iv o : " + r l.n iv o + ", r2 .n iv o : " + r2 .n iv o ); r l = r2; p r in t("2 : r l.n iv o : ", r2 .n iv o : r l.n iv o = 27; p r in t("3 : r l.n iv o : " , r2 .n iv o : 1 1 + r l.n iv o + " + r2 .n iv o ); " + r l.n iv o + " + r 2 .n iv o );

}
} /* Is p is : 1: r l.n iv o : 9, r2 .n iv o : 47 2: r l.n iv o : 47, r2 .n iv o : 47 3: r l.n iv o : 27, r2 .n iv o : 27

* ///:Klasa Rezervoar je je d n o sta v n a i njena dva p rim e rk a (rl i r2) n ap rav ljen a su u m eto d i m ain(). Polju nivo u svakom p rim e rk u klase Rezervoar d od eljen e su razliite v rednosti, zatim je v red n o st r2 d o d eljen a p rim e rk u rl i p o to m je r l p ro m en jen . U m n o g im p ro g ram sk im jezicim a oekivali b iste da su r l i r2 nezavisni sve vrem e, ali, p o to ste d odeljivali reference, kada se izm eni o b jek at rl, m enja se i objekat r2, zato to i r l i r2 sare istu referencu koja pokazuje na isti objekat. (P rv o b itn a referenca koja se nalazila u rl i koja je

pokazivala na objekat koji je uvao v red n o st 9, izm en jen a je p rilik o m odele i efektivno izgubljena; n jen objekat e p o istiti sakuplja sm ea). O vaj fenom en esto se naziva pojavap scu d o n im a (engl. aliasing ), i on je u o snovi nain na koji Java rad i sa o b jek tim a. Ali ta ako neete da se u ovom sluaju pojavi pseu d o n im ? M oete da precizirate d o d elu i d a napiete:
r l.n iv o = r2 .n ivo ;

Ov'ako e se zadrati dva o dvojena objekta, u m esto da jed an b u d e o d b aen i da se obe reference poveu sa istim o b jek to m . U skoro ete shvatiti da je ra s p oljim a u n u ta r objekata prljav posao i da je u su p ro tn o sti s p rin c ip im a d o b ro g o b jek tn o o rijen tisan o g p ro jektovanja. Pitanje p seu d o n im a nije jed n o stav n o , pa im ajte na u m u da d odela kod objekata m oe da dovede d o iznenadenja. V eba 2: (1) N apiite klasu koja sadri broj tip a flo at i p o m o u nje pokaite pojavu p seu d o n im a.

Pojava pseudonima pri pozivanju metode


P seu d o n im e se takoe pojaviti kada objekte prosleujete m eto d am a:
/ / : op eratori/Prosl edi O bjekat. java // Pro sle ivan je objekata metodama moe vas iz n e n a d iti. import s t a t ic n e t.min dvie w .u ti1. P r in t .* ; cla s s Slovo { char c;

Poglavlje 3: Operatori

69

p u blic c lass ProslediO bjekat { s t a t ic void f(S lo v o y ) { y .c = ' z ' ;

}
public s t a t ic void m ain (Strin g [] args) { S1ovo x = new S 1 o vo (); x . c = ' a '; p r i n t ( " l : x .c : " + x . c ) ; f(x ); p r in t (" 2 : x .c : " + x .c );

}
} /* Is p is : 1: x .c : a 2: x .c : z

* ///:U m n o g im p ro g ra m sk im jezicim a, m eto d a f() bi napravila kop iju svog a rg u m e n ta

Slovo y u n u ta r oblasti vaenja m etode. Ali, poto je prosle ena referenca, red
y .c = ' z ' ;

u stvari m enja objekat izvan f(). P itanje p seu d o n im a i njegovo reavanje je sloeno; ra z in o tre n o ie u jed n o m o d m renih d o d a ta k a ove knjige. Zasad sam o d o b ro pazite da izbegnete zam ke. Vcba 3: ( 1) N apiite klasu koja sadri broj tipa flo at i p o m o u nje pok aite p ojavu p se u d o n im a p rilik o m pozivanja m etoda.

Matematiki operatori
O sn ov ni m atem atik i o p e ra to ri su isti kao i u veini p ro g ram sk ih jezika: sabiranje (+ ), o d u z im an je (-), deljenje (/), m n o en je (*) i m o d u lo (% ), koji daje o statak p ri celobrojn o m deljen ju. Pri celo b ro jn o m deljenju odseca se realni deo, a ne zaok ru u je rezuitat. Java tako d e koristi skraeni C /C + + zapis za isto v rem en o ob avljanje o p eracije i doele. To se naznaava na sledei nain: iza o p erato ra sledi znak jedn ako sti, i d o sled n o se m oe p rim e n iti na sve o p era to re u jeziku (kada to im a sm isla). Na p rim e r: da biste d o dali 4 p ro m enljivoj x i rezultat dodelili x, piite: x + = 4. O vaj p rim e r p okazuje korienje m atem atik ih op erato ra:
/,/: o peratori/M atO peratori. java // Prikaz matematikih operatora import j a v a . u t i 1.* ; import s t a t ic n e t.min d view .u ti1. P r in t .* ; p ub lic c lass MatOperatori { p ublic s t a t ic void m a in (Strin g [] args) { // Napravimo i n i c i ja liz o v a n generator s lu a jn ih brojeva Random slucajan = new Random(47); i nt i , j , k ;

70

Misliti na Javi

// Izaberimo broj od 1 do 100: j = s1 ucajan.nextlnt(100) + 1; p r in tfj : " + j ) ; k = s lu c a ja n .n e x tln t(100) + 1; p r in t (" k : " + k ) ; i = j + k; p r in t("j + k + i); i = j - k; p rin t("j - k i = k / j; p r in t (" k / j i = k * j; p r in t (" k * j i = k % j; p r in t (" k % j + i); + i); + i);

+ i ); j %= k; p r i n t ( j %= k : " + j ) ; // Test za brojeve s pokretnim zarezom flo a t u,v,w ; // Prim e n ljiv o i na double v = s lu c a ja n .n e x tF lo a t(); p r in t ( " v : " + v ) ; w = s lu c a ja n .n e x tF lo a t(); p rin t("w : " + w ); u = v + w; p r in t ( " v + w : " + u ) ; u = v - w; p r in t ( " v - w + u ); u = v * w; p r in t ( " v * w + u ); u = v / w; p r in t ( " v / w : " + u ) ; // Sledee radi i za char, // byte, sh ort, in t , long i double: u + = v; p rin t("u + = v : + u ); u -= v; p r in t("u -= v : + u); u *= v; p r in t("u *= v : + u );
U /= V ;

p r in t("u /= v : ) /* Is p is : j k j j k k k j : 59 : 56 + k : 115 - k : 3 / j : 0 * j : 3304 % j : 56 %= k : 3

+ u );

Poglavlje 3: Operatori

71

v : 0.5309454 w : 0.0534122 v + w : 0.5843576 v - w : 0.47753322 v * w : 0.028358952 v / w : 9.940527 u += v : 10.471473 u -= v : 9.940527 u *= v : 5.2778773 u /= v : 9.940527 * ///:Za generisanje brojeva, p ro g ra m p rv o pravi objekat klase Random. U koliko p ri njegovom pravljen ju ne p rosledite n ijed an arg u m en t, Java uzim a tekue v rem e kao v red n o st za inicijalizaciju g e n erato ra p seu d o slu ajn ih brojeva, p a p rilik o m svakog izvravanja p ro g ram a daje drugaiji izlaz. M e u tim , u ovoj knjizi vano je da izlaz p rik azan n a k raju p rim era b u d e to u jednaeniji, da bi se m ogao prov eriti p o m o u spo ljn ih sredstava. U koliko p rilik o m p ravljenja Random objekta zadate isto seme (broj za inicijalizaciju g en erato ra p seu d o slu ajn ih brojeva), isti e p seudosluajni brojevi b iti generisani svaki p u t, p a se izlaz p ro g ra m a m oe p ro v e riti1. U sluaju da elite drugaiji izlaz svaki p u t kada p o k ren ete p ro g ra m , slo b o d n o izbacite sem e. P ro g ram generie razne tipove sluajnih brojeva p o m o u objekta klase Randoni, p o zivajui m etode: nextlnt(), nextLong(), nextFIoat() ili nextDoubIe(). A rg u in en t m eto d e nextlnt() zadaje g o rn ju granicu gen erisan ih brojeva. D onja granica je nula, a p o to je neem o zbog n e m o g u n o sti deijenja s n u lo m , rezultatu sm o o d ali 1. V eba 4: (2) N apiite p ro g ra m koji izraunava b rzin u iz k o n sta n tn o g p u ta i k o n sta n tn o g v rem ena.

Unarni operatori minus i plus


U n arni m in u s (-) i u n a rn i plus (+ ) isti su o p erato ri kao i b in a rn i m in u s i plus. Prevodilac p rep o z n aje ta ste eleli da u p o tre b ite na o sn o v u naina kako piete izraz. Na p rim er, zn aenje izraza
x = -a ;

o ig led n o je. Prevodilac m oe da raspozna: x = a * -b; ali bi italac m ogao da se zbuni, stoga je k atkada jasnije ako se napie: x = a * (-b ); U narni m in u s m enja znak v rednosti. U narni plus je su p ro ta n u n a rn o m m in u su , iako nem a nikakav efekat sem to tipove byte, short i char p retv ara u int.
Broj 4 / se sm atrao arobnim brojem " na koledu koji sani pohaao, pa m i se to urezalo u seanje.

72

Misliti na Javi

Automatsko uveanje i umanjenje


Java im a p u n o skraenica, kao i C. Skraenice d o p rin o se lakem p isan ju k o d a i lakem , ili teem , itanju koda. Dve zgodne skraenice su o p e ra to ri uveanja i u m a n je n ja (n a engleskom se esto zovu auto-increm ent i auto-decrem ent). O p e ra to r u m a n je n ja je -- i oznaava sm anji za jedin icu . O p e ra to r uveanja je + + i o znaava poveaj za je d in ic u . A ko je a tip a int, na prim er, izraz ++a ekvivalentan je izrazu (a = a + 1). N e sam o da o p e ra to ri za uveanje i u m an jen je m en jaju v red n o st pro m en ljiv e, nego je i v raaju kao rezultat. Postoje dve verzije oba o p erato ra, koje se esto nazivaju prefiksne i sufiksne verzije. Prefiksno uveanjeje oblik kada se o p e ra to r + + pojavljuje p re prom enljive, a sufiksno uveanje kada se o p erato r + + pojavljuje n ak o n p rom enljive. Slino to m e, prefiksno um anjenje je kada se o p erato r pojavljuje p re p rom enljive, a sufiksno um anjenje kada se o p e ra to r -pojavljuje posle prom enljive. Pri p refik sn o m uveanju i p refik sn o m u m a n je n ju (tj. + + a ili a), najpre se izvodi operacija, a p o to m se vraa nova v red n o st. Pri sufiksnom uveanju i sufiksnom u m an jen ju (tj. a++ ili a), prvo se vraa stara v red n o st, a zatim se izvrava operacija. Kao prim er:
//: operatori/A utom atskiO peratori. java // Prikazuje operatore + + i --. import s t a t ic n e t,m in d view .u ti1. P r i n t .* ; p ublic class AutomatskiOperatori { public s t a t ic void m a in (S trin g [] args) { in t i = 1; p r in tC 'i : " + i ) ; print("+ + i : 1 1 + ++i); // Prefiksn o uveanje p rin t("i+ + : " + i++); // Sufiksno uveanje p r in t("i : 1 1 + i); p r in t(" i : 1 1 + - - i) ; // Prefiksn o umanjenje p r i n t ( " i : " + i ) ; // Sufiksno umanjenje p r in t("i : " + i );

)
} /* Is p is : i : 1 ++i : 2 i++: 2 i : 3 i : 2 i : 2

l : 1 * III--V idite da se vred n o st za prefiksni oblik vraa n ak o n izvravanja operacije, ali za sufiksni oblik dobijate vred n o st p ro m en ljiv e pre nego to se o p eracija izvri. O vo su jedini operato ri (osim o n ih koji u pliu i d o d elu ) koji im aju sp o re d n e efekte (o d n o sn o , koji ne koriste sam o v red n o st o p eran a d a , ve ih i m en jaju ).

Poglavlje 3: Operatori

73

Jedno o d objanjenja za p o rek lo im en a C + + , koje znai jed an k o rak dalje o d C -a, lei u o p e ra to ru uveanja. U vrem e n a sta n k a Jave, Bill Joy (jedan o d a u to ra Jave) rekao je da je Java = C + + (C p lus p lu s m in u s m in u s), nagovetavajui kako je Java jezik C + + iz kojeg su izbaeni n e p o tre b n i teki delovi, pa je zbog toga m n o g o jednostavnija. to b u dete vie napred ovali kroz ovu k njigu, videete da su m n o g i delovi jednostavniji, ali im a d ru g ih stvari zbog kojih Java ipak nije m n o g o jed n o stav n ija o d C + + -a .

Operatori poreenja
R ezultat o p e ra to ra p o re en ja je logika v re d n o st (boolean). O n i o cen ju ju relaciju izm ed u v re d n o sti dva o p era n d a. Izraz sa o p e ra to rim a p o re e n ja daje v re d n o st true ako je relacija tan a i false ako je relacija n eta n a. O p e ra to ri p o re en ja su m an je o d (< ), vee od (> ), m an je ili jed n ak o (< = ), vee ili jed n ak o (> = ), jed n a k o (= = ) i razliito (!=). Jednakost i razliitost se m ogu p rim e n iti n a sve proste tip o v e p o d atak a , ali se ostala p o re en ja ne m o g u p rim e n iti na tip boolean. O b jekti tip a boolean m o g u im ati sam o v red n o sti true ili false, p a ne bi im alo sm isla tra iti koji o d njih je vei, a koji m anji.

Ispitivanje jednakosti objekata


O p e ra to ri po re en ja = = i != m o g u se p rim e n iti i na sve objekte, ali n jihovo znaenje esto zb u n ju je p oetnike u Javi. Evo p rim era :
//: o p e ra to ri/Je d n ak o st.java p ublic c lass Jednakost { pub lic s t a t ic void m a in (S trin g [] args) In teg er nl = new In te g e r(4 7 ); In teger n2 = new In te g e r(4 7 ); S y s te m .o u t.p rin tln (n l = = n2); S y s te m .o u t.p rin tln (n l != n2);

}
} /* Is p is : fa l se true

* / / / =N aredba S y s te m .o u t.p rin tln ( n l = = n 2) ispisuje rezu ltat logikog p o re en ja koje se u n u ta r nje nalazi. Njegov rezultat bi sig u rn o treb alo da b u d e tru e, a o n o g sledeeg false, poto su v redno sti oba objekta klase In te g e r iste. Ali iako je sadrina o b jekata ista, reference su razliite, a o p e rato ri = = i != p o red e reference objekata, a ne n jihovu sadrinu. Stoga je rezultat prvog p ore en ja false, a d ru g o g tru e . N aravno, ovo isprva iznenaduje. ta ako elite da p o red ite ekvivalenciju stv arn ih sadraja objekata? U to m sluaju m orate da koristite poseb nu m eto d u eq u a ls() koja p o stoji u svim objek tim a (ta m eto d a se ne koristi za proste tipove, za koje ionako d o b ro fu n k cio n iu = = i !=). Evo kako se o n a koristi:

74

Misliti na Javi

//: o p erato ri/ M etodaEquals.java p ublic class MetodaEquals { public s t a t ic void m a in (S trin g [] args) In teger nl = new In te g e r(4 7 ); Integer n2 = new In te g e r(4 7 ); S y s te m .o u t.p rin tln (n l.e q u a ls (n 2 ));

}
} /* Is p is : true

* ///:R ezultat e sada biti kao to i oekujete. Ah, ali to ipak nije ba tako jed n o stav n o . Ako sam i n ap rav ite klasu, na p rim er:
//: o p erato ri/ MetodaEquals2.java // eq u als() Podrazumevano ne poredi sadrinu. c lass Vrednost { i nt i ;

}
public class MetodaEquals2 { p ublic s t a t ic void main (S t r in g [] args) { Vrednost v l = new V red n o st(); Vrednost v2 = new VrednostO ; v l . i = v2 .i = 100; System .out.pri n t l n ( v l . equ als(v 2 )) ;

}
} /* Is p is : fa l se

* ///:ponovo ete biti zbunjeni: rezultat je false, zato to je p o d ra z u m e v an o p o n aan je m etode equals() da poredi reference. Stoga n e e ted o b iti eljeno p o n aan je osim ako ne rcdefinieie (engl. override) m etodu equals() u vaoj novoj klasi. N aalost, o redefm isanju neete uiti sve do poglavlja Ponovno korienje klasa , a o p rav iln o m nainu definisanja m etode equals() do poglavlja Detaljno razm atranje kontejnera, ali u m e u v re m e n u sam o pazite na nain na koji se m etoda equals() p onaa, jer e vas to m oda sauvati od problem a. Veina klasa iz Javine biblioteke realizuje m eto d u equals() tako da o n a p oredi sadraj objekata u m esto njihovih referenci.

Veba 5: (2) N apravite klasu Pas koja sadri dve prom enljive tipa String: ime i kae. U m etod i main() n apravite dva objekta tip a Pas sa im en im a Fleki (koji k ae:R afi) i Buva
(koji kae: ,,Vafi ). Z atim p rik aite njihova im ena i ta kau.

Veba 6: (3) Posle vebe 5, napiite n o v u referencu objekta tip a Pas i d o d elite je Flekijevom objektu. Z atim obavite p o red en je koristei = = i equals() za sve reterence.

Poglavlje 3: Operatori

75

Logiki operatori
Logiki o p e ra to ri konju n k cija (&&), d isjunkcija (II) i negacija (!) daju v re d n o sti true ili false tipa boolean. U ovom p rim e ru k oriste se o p e ra to ri p o re e n ja i logiki o p erato ri:
//: o p e ra to ri/ L o g ick i. java // R elacion i operatori (poreenja) i lo g i k i o p e ra to ri. import j a v a . u t i 1 .*; import s t a t ic n e t.m in d v ie w .u til. P r in t . * ; p ublic c lass Logicki { public s t a t ic void m a in (S trin g [] args) { Random slucajan = new Random(47); in t i = s lu c a ja n .n e x tln t(1 0 0 ); in t j = s lu c a ja n .n e x tln t(1 0 0 ); p rin t("i = " + i ) ; p ri n t ( " j = " + j ) ; p r in t( i > j je " + (i > j ) ); p r i n t f 'i < j j e " + (i < j ) ) ; p r in t("i > = j je " + (i > = j )) ; p r in t("i < = j je " + (i < = j )); p r in t("i = = j je " + (i = = j)); p r in tC 'i != j j e " + ( i != j ) ) ; // Tip in t u Ja v i ne moe da se // k o ris ti kao lo g i k i tip //! p r i n t ( " i && j je " + ( i && j ) ) ; //! p r i n t ( " i //! p r i n t ( " ! i p r in t("(i + ( (i p r in t (" (i I I j je " + ( i | ( j ) ) ; je " + ! i ) ; < 10) && (j < 10) je " < 10) && (j < 10)) ) ; < 10) j | (j < 10) je " || ( j < 10)) ) ;

+ ( (i <10)

}
} i j i i i i i i (i /* Is p is : = 58 = 55 > j je true < j je fa ls e > = < = = = != < j je true j je fa ls e j je fa ls e j je true 10) && (j < 10) je fa ls e (j < 10) je fa ls e

(i < 10) ||

* ///:O p erato re konjunkcije, disjunkcije i negacije m oete p rim e n iti sam o na v red n o sti tip a b o o le an . Za logike uslove ne m oete da koristite ostale (nelogike) tipove, kao u jezicim a C i C + + . O vakve neuspele pokuaje m oete v ideti u k o m e n ta rim a sa o zn ak o m //!

76

Misliti na Javi

(takvo oznaavanje k o m e n ta ra om o g u av a n jihovo au to m atsk o uklanjanje, to olakava testiran je). N aredn i izrazi d aju v red n o sti tip a boolean tako to koriste pore en je, a zatim na te rezultate p rim e n e logike operacije. O b ratite p a n ju na to d a se v re d n o st tip a boolean au to m atsk i k o nvertuje u o d govarajui tek stualni oblik ako se k oristi ta m o gde se oekuje objek at klase String. D efiniciju za int u p re th o d n o m p ro g ra m u m oete zam en iti bilo kojim p ro stim tip o m p o d atak a osim boolean. Pazite na to da je p o re en je brojeva u fo rm a tu p o k re tn o g zareza veom a precizno. Broj koji se i p o p o sled n jo j d e m a li razlikuje od d ru g o g b ro ja i dalje je ,,razliit. Broj koji je na n ajm an jo j d ecim ali iznad nule i dalje je razliit o d n u le.

Veba 7: (3) N apiite p ro g ram koji sim u lira b acanje novia (pism o - glava ).

Nepotpuno izraunavanje
K ada rad ite s logikim o p e ra to rim a , naii ete na fen o m en p o d nazivom n e p o tp u n o izrau n av an je. To znai da e izraz b iti izrau n av an sam o do tren u tk a kada tan o st ili neta n o st celog izraza m oe n ed v o sm islen o da se odredi. Z ato p reostali delovi logikog izraza u opte nee b iti izrau n ati. Evo p rim era koji p o kazuje n ep o tp u n o izraunavanje:
//: operatori/N epotpunoIzracunavanje.java // Prik azuje pojavu nepotpunog izraunavanja pri radu s logikim operatorima. irnport s t a t ic net.mindvievv.u ti 1. P r in t . *; public c lass Nepotpunolzracunavanje { s t a t ic boolean t e s t l ( in t vrednost) { p r i n t ( " t e s t l ( " + vrednost + ) " ) ; p r in t ( " r e z u lt a t : " + (vrednost < 1 )); return vrednost < 1;

}
s t a t ic boolean te s t 2 (in t vrednost) { p r in t ( " t e s t 2 (" + vrednost + " ) " ) ; p r in t ( " r e z u l t a t : " + (vrednost < 2 )) ; return vrednost < 2;

}
s t a t ic boolean t e s t3 (in t vrednost) { p r in t ( te s t3 (" + vrednost + " ) " ) ; p r i n t ( " r e z u l t a t : " + (vrednost < 3 )) ; return vrednost < 3;

}
p ublic s t a t ic void m a in (S trin g [] args) { boolean b = te s tl(O ) && te s t2 (2 ) && te s t3 (2 ); p r in t (" iz r a z ima logiku vrednost" + b ) ;

}
} /* Is p is : t e s t l (0) r e z u lta t: true te s t2 (2 ) r e z u lta t: fa ls e izraz ima logiku vrednost fa ls e

* ///:-

Poglavlje 3: Operatori

77

Svaki test vri p o re en je sa a rg u m e n to m i vraa v re d n o sti true ili false. T akoe ispisuje i p o ru k u da je bio pozvan. Testovi se pozivaju preko sledeeg izraza:
t e s tl(O ) && te s t2 (2 ) && te s t3 (2 )

P riro d n o je da pom islite kako e sva tri testa biti izvrena, ali izlaz pokazuje drugaije: Prvi test daje rezultat tr u e pa se izraunavanje izraza nastavlja. M eutim , drug i test daje rezu ltat false. Poto to znai da e i rezultat celog izraza sigurno biti false, zato nastavljati izraunavanje izraza? To m oe da potraje. Ba zbog toga se koristi n e p o tp u n o izraunavanje: ako ne treba do kraja izraunavati sve delove logikog izraza, p ro g ram e m oda raditi bre.

Literali
Kada u p ro g ra m u doslov n o n avodite v re no sti, prevodilac o b in o tan o zna kojim tip o m da ih p redstavi. P o nek ad se m oe javiti n ed ou m ica. U to m sluaju m o rate da n avo dite p rev o d io ca d o d a tn im in fo rm ac ijam a u oblik u znakova p rid ru e n ih v red n o sti literala. Sledei p rim e r po k azu je te znakove:
// : o p e ra to ri/V red n o sti. java import s t a t ic n e t.m indview.u t i 1. P r i n t . *; p u b lic c lass Vrednosti { p u b lic s t a t ic void m a in (S trin g [] args) { in t i l = 0x2f; // Heksadecimalno zadavanje celog broja // (malim slovima) p r i n t ( " i l : " + In te g e r.to B in a ry S trin g ( i 1 )); in t i2 = 0X2F ; // Heksadecimalno zadavanje celog broja // (v e lik im s l ovima) p r in t ( " i2 : " + In t e g e r .t o B in a r y S tr in g (i2 )) ; in t i3 = 0177; // Oktalno zadavanje celog broja (vodea nula) p r i n t ( " i3 : " + In t e g e r .t o B in a r y S t r in g (i3 )); char c = 0 x f f f f ; // najvea vrednost za t ip char, heksadecimalno p r in t ( " c : " + In te g e r .t o B in a r y S tr in g (c )) ; byte b = 0x7f; // najvea vrednost za t ip byte, heksadecimalno p r in t (" b : " + In te g e r.t o B in a ry S tr in g (b )) ; short s = 0 x 7 fff; // najvea vrednost za tip sh o rt, heksadecimalno p r in t ( " s : " + In te g e r.to B in a r y S tr in g (s )) ; long nl = 200L; // su fik s za long long n2 = 2001; // su fik s za long (moe da zbuni j e r l i i na je d in ic u ) long n3 = 200; flo a t f l = 1; flo a t f2 = 1F; // su fik s za flo a t flo a t f3 = l f ; // sufik s za flo a t double dl = ld ; // su fik s za double double d2 = 1D; // su fik s za double // (Heksadecimalno i oktalno mogu se zadavati i b rojevi tip a long) ) /* Is p is : i 1: 101111 i 2: 101111 13: 1111111

78

Misliti na Javi

c : 1111111111111111 b: 1111111 s : 111111111111111 * /// Slovo iza literala zadaje njegov tip. Veliko ili m alo L oznaava tip long (m e u tim , m alo 1 zb un juje zato to lii n a jed in icu ). Veliko ili m alo F oznaava tip float. Veliko ili m alo D oznaava tip double. Sve celo b ro jn e tipove p o d atak a m ogue je zadavati u h ek sad ecim aln o m obliku (sa o sn o vom 16), n azn aav an jem v o d eim 0x ili 0X n ak o n ega slede sim boli 0 -9 i a -f, bilo m alim ili velikim slovim a. A ko p o k u ate da inicijalizujete p ro m en ljiv u v red n o u veom od one koju m oe d a uva (bez o b zira na n u m erik i oblik v red n o sti), prevodilac e prijaviti greku. U g o rn jem p rim e ru o b ra tite p an ju na m aksim alne m ogue heksadecim alne v red n o sti za tipove char, byte i short. Ako ih p rekoraite, prevodilac e ih auto m atsk i p retv o riti u int i jav iti kako treb a izvriti suavanje eksplicitnom konverzijom za d a tu d odelu. (E ksplicitne konverzije su d efin isan e u nastavku poglavlja.) Tada ete znati da ste prekoraili dozvo ljen u v red n o st. O ktalni ob lik (sa o sn o v o m 8) nazn aav a se v odeom n u lo m u zapisu broja, koju slede cifre 0 -7 . U C -u, C + + -u i Javi ne postoji prikaz b in arn ih brojeva kao literala. M eutim , pri rad u s heksadecim alnim i o k taln im zapisom rezultate je podesno prikazati u b in a rn o m obliku. To se lako postie m eto d am a static toBinaryString() iz klasa Integer i Long. Im ajte u vidu da se m anji tipovi autom atski k o nvertuju u int kada se proslede u Integer.toBinaryString().

Veba 8: (2) Pokaite da hek sad ecim aln i i o k taln i zapis rade s brojevim a tipa long. Za prikazivanje rezultata u p o treb ite Long.toBinaryString().

Eksponencijalni zapis
Za ekspon en cijaln i zapis realnog b ro ja k oristi se notacija koju sam o duvek sm atrao o b esh ra b ru ju o m .
// : o p erato ri/Ekspo n en ti. ja va // "e" znai "10 na stepen". public c lass Eksponenti { public s t a t ic void main( S t r i ng[] args) { // V eliko i malo 'e ' su jed n ak i: flo a t expFloat = 1. 39e-43f ; expFloat = 1.39E-43f; System .out.pri n tln (ex p Fl o a t ) ; double expDouble = 47e47d; // 'd ' je opciono double expDouble2 = 47e47; // Automatska konverzija u double System .out.pri ntln(expDoubl e ) ;

)
) /* Is p is : 1.39E-43 4.7E48

* ///:-

Poglavlje 3: Operatori

79

U nauci i inen jerstv u e o znaava o sno vu p riro d n o g logaritm a, koja otprilike iznosi 2,718. (Preciznija v re d n o st tip a d o u b le je d o stu p n a u Javi kao M ath.E .) O n a se k o risti u ek sp o n en cijaln im izrazim a kao to su 1,39 x e'43, to znai 1,39 x 2,71843. T vorci FORT R A N -a odluili su d a e e znaiti deset na step en , to je u dn o, je r se FO RTRAN u p o trebljava u nauci i inenjerstvu, i svako je m ogao pom isliti d a e tvorci o b ra titi p a n ju na takvu d vo sm islen ost.2 Bilo kako bilo, ovaj obiaj je nastavljen u C -u , C + + -u i sada u Javi. Stoga, ako ste se navikli na e kao na o sn ov u p riro d n o g logaritm a, o b ra tite p a n ju kad a u Javi v id ite izraz kao to je 1,39 e-43f; o n o znaava 1,39 x 10~43. P ratei znak n e m o ra te da k o ristite kada prevodilac m oe sam da o d red i odgovarajui tip. Kada napiete:
long n3 = 200;

nem a dvosm islenosti, pa je L iza bro ja 200 iziino. M e utim , kada napiete:
flo a t f4 = le-43f; // 10 na -43. stepen

prev od ilac o b in o tu m ai e k sp o n e n ja ln e brojeve kao brojeve d vo stru ke preciznosti (d o u b le ), pa bi v am bez p rateeg slova f prijav io greku, traei eksplicitnu konverziju iz d o u b le u float. Veba 9: (1 ) Prikaite najvei i n ajm an ji b ro j koji se m oe zapisati p o m o u eksponencijalno g zapisa tipova float i d o u b le .

Operatori nad bitovima


O p e ra to ri nad bitov im a o m o g u av aju da rad ite s p o jedin im bitov im a od kojih se sastoji neki prosti tip podataka. O p e ra to ri nad b ito vim a p rim e n ju ju logiku (B ulovu) algebru nad od go varaju irn bito v im a dva a rg u m e n ta da bi izraunali rezultat. O p e ra to ri nad b ito v im a vode p o rek lo od niskog nivoa jezika C; kada se d irek tn o radi s h ard v ero m i treba d irek tn o postavljati bitove hardverskih registara. Java je trebalo da b u d e u g ra e n a u a p arate za prikazivanje In tern eta na ek ran u TV-a, pa je ova orijentacija ka niskom nivou i im ala sm isla. M e u tim , o p era to re nad bitov im a verovatno neete previe koristiti. O p e ra to r konjunkcije nad bitovim a (&) daje jedinicu kao vrednost izlaznog bita ako su oba ulazna bita jednaka jedinici; inae, daje nulu. O p erato r disjunkcije nad bitovim a (I) daje jedinicu kao vrednost izlaznog bita ako je bilo koji od ulaznih bitova jed nak jedinici, a
John K irkham jc napisao: R aunarim a sam poeo da se bavim 1962. koristei FORTRAN II na m aini IBM 1620. U to vrem e, tokom ezdesetih i poetkom sedam desetih, FORTRAN je u p o tp u n o sti pisan velikim slovim a. To potic verovatno od toga to su stari ureaji za u no s koristili 5-bitni Baud o to v kod koji nije p o d r/av ao m ala slova. Slovo E je u eksponencijalnoj notaciji uvek pisano kao veliko i nikada nije bilo m eano sa osnovom p riro d n o g logaritm a, e, koja se uvek pie m alim slovom . H je oznaavalo eksponent, obino 10, za korieni brojni sistem. U to vrem e i oktalni form at je bio iroko rasp ro stranjen m edu p rogram erim a. Da sam naiao na oktalni broj u eksponencijalnoj notaciji, sm a trao bih da je osnova 8. Seam se da sam p rvi p ut video eksponencijalni zapis napisan s m alim e u kasnim sedam desetim i sm atrao sam ga zbunjujuim . Froblem se pojavio kada su m ala slova p ro d rla u FORTRAN, ne na saniom poetku. Mi sm o, u stvari, imali funkcije koje sm o koristili ako nam je zaista bila potrebna osnova p riro d n o g logaritm a, ali su sve bile pisane velikim slovim a.

80

Misliti na Javi

daje n u lu sam o ako su oba ulazna bita jednaka nuli. Iskljuiva disjunkcija nad bitovim a ili XOR ( A) daje jedinicu kao v red n o st izlaznog bita, ako je jed an ili d ru g i ulazni bit je d n a k je dinici, ali ne i oba. Negacija nad bitovim a (~ , koja se takoe naziva i operatorprvogkom plem enta ) jeste u n a rn i o p erato r; im a sam o jed an arg u m en t. (Svi ostali o p erato ri n ad bitovim a su b in a rn i o p erato ri - im aju dva o p eran d a.) Negacija n ad b ito v im a daje negaciju ulaznog bita - jed inicu ako je ulazni b it je d n ak nuli i n u lu ako je ulazni b it jed n ak jedinici. Za o p erato re n ad b ito v im a i logike o p e rato re k o riste se isti sim boli, p a e v am biti lake da se setite znaenja ako se d o setite sledeeg: p o to su bito v i ,,m ali, za o p erato re nad b ito v im a koristi se sam o p o jed an znak. O p e ra to ri n a d b ito v im a m o g u da se k o m b in u ju sa z n ak o m = i tim e u jed in e operaciju sa dodeljivanjem : dozvoljeni su &=, 1= i A=. (Poto je ~ u n a rn i o p erato r, o n ne m oe da se k o m b in u je sa zn ak o m =.) T ip boolean se tre tira kao je d n o b itn a v red n o st, pa je situacija neto d rugaija. N ad njim m oete da p rim e n ite k o n ju n k ciju , d isju n k ciju i iskljuivu d isju n k ciju n ad bitovim a, ali ne i negaciju nad b ito v im a (verovatno da bi se izbeglo m eanje s logikom negacijom ). Za tip boolean o p e rato ri n ad b ito v im a im aju isti efekat kao i logiki o p e ra to ri, osim to se n e javlja n e p o tp u n o izraunavanje. O p e ra to r iskljuive disju n k cije n ad b ito v im a nem a ekvivalentan logiki op erato r. Z ato ovaj o p e ra to r predstavlja jed in i nain da n a dve vrednosti tipa boolean p rim e n ite o p eraciju XOR. Na pro m en ljiv e tip a b o o le a n ne m oete p rim en jiv ati o p erato re p o m e ran ja koje em o u p rav o opisati.

Veba 10: (3) N apiite p ro g ram s dve b in a rn e k o n stan te koje im aju n aizm en in e jedinice
i nule, s tim to je prvoj na n ajm an je zn aajn o m m estu nula, a d ru g o j jed in ica. (U putstvo: to e vam biti najlake s hek sad ecim aln im k o n sta n tam a). Z ad ajte ta dva b ro ja kao arg u m en te svih o p e ra to ra n ad bito v im a na sve m o g u e naine, a rezu ltate prikaite m eto d o m

Integer.toBinaryString().

Operatori pomeranja
O p erato ri p o m e ran ja (engl. shift) takoe rade s bitovim a. O ni m o g u da se k oriste iskljuivo s p ro stim , celobrojnim tip o v im a. O p e ra to r p o m era n ja ulevo (< < ) kao rezultat daje o p e ra n d s leve stra n e o p e rato ra, p o m e ren ulevo za broj bitova naveden n.ikon o p erato ra (nii bitovi p o p u n jav aju se n u lan ia). O p e ra to r o zn aen o g p o m e ran ja u d esn o (> > ) kao rezultat daje o p e ra n d s Ieve stra n e o p e ra to ra , p o m eren u d esn o za bro j bitova naveden nakon o p erato ra . O zn aen o p o m eran je ud esn o > > k oristi produavanje uz oiivanje znaka (engl. sign extension)\ ako je v red n o st p ozitivna, vii bitovi se p o p u n jav aju nu lo m ; ako je v red n o st negativna, vii bitovi se p o p u n jav aju jedinicom . U Javu je takoe d o d a to i neoznaen o p o m e ran je u d esn o > koje k oristi produavanje nz dodavanje nula (engl. zero extension): bez o bzira na znak, vii bitovi se p o p u n jav aju n u lo m . Ovaj o p e ra to r ne postoji u C -u ili C++-U. Ako p o m e ra te v red n osti tip a char, byte ili short, on e e biti p ro iren e na int pre nego to se izvri p o m eran je i rezu ltat e biti tip a int. Koristi se sam o p et niih b itova vrednosti s desne stra n e o p erato ra. O vo vas spreava da izvrite p o m eran je za vie m esta nego to ima bitova u ru ita r p ro m en ljiv e tip a int. Ako o periete n ad v red n o u tipa long, dobiete

Poglavlje 3: Operatori

81

rezu ltat tipa Iong. Bie k orieno sam o est niih b ito v a v re d n o sti s desne stra n e o p erato ra, pa ne m oete da izvrite p o m e ra n je za vie m esta nego to im a b itova u n u ta r p ro m enljive tip a long. P om eranje m oe da se k o m b in u je sa z n ak o m jed n ak o sti ( = ili = ili > = ) . L vrednost se zam enjuje v red n o u lv red no st p o m e re n o m za d v re d n o st m esta. Kada se n eozn aen o p o m eran je u d esn o k o m b in u je sa d o d e lo m , po sto ji p ro blem . A ko ovu o p eraciju p rim e n ite na vredn o sti tip a b y te ili short, neete d o b iti p rav iln e rezultate. U m esto toga, on e se p ro iru ju na int, p o m e ra ju ud esn o, ali se sk rau ju p riiik o m d odele, pa u tim sluajevim a kao rezultat d obijate 1. To pok azuje n a red n i p rim er:
//: operatori/NeoznacenoPomeranjeUdesno.java // Test za neoznaeno pomeranje udesno. import s t a t ic n e t.m in d v ie w .u til. P r in t . * ; p ublic class NeoznacenoPomeranjeUdesno { p ublic s t a t ic void m a in (S trin g [] args) { in t i = -1; p r i n t ( In t e g e r .t o B in a r y S t r in g (i)) ; i >= 10; pri n t(In te g e r.to B i n a ry S tri n g ( i) ) ; long 1 = -1; pri n t(Lo n g .toBi n a ry S trin g ( 1 ) ) ;

1 > = 10;
p r in t(L o n g .to B in a r y S trin g (l) ) ; short s = -1; pri n t (In te g e r .toBi n a ry S tri n g (s )); s >= 10; pri n t (In te g e r .toBi n a ry S tri n g (s )) ; byte b = -1; pri n t (In t e g e r .toBi n a ry S tri n g (b )) ; b >= 10; p rin t(In te g e r.to B i n a ry S tri n g (b )) ; b = -1; pri n t (In te g e r.to B i n a ry S tri n g (b )) ; pri nt (In te g e r . toBi n a ry S tri n g (b > 1 0 )) ;

)
} /* Is p is :

11111111111111111111111111111111
1111111111111111111111

1111111111111111111111111111111111111111111111111111111111111111
111111111111111111111111111111111111111111111111111111 11111111111111111111111111111111 11111111111111111111111111111111 11111111111111111111111111111111

11111111111111111111111111111111
11111111111111111111111111111111 1111111111111111111111

* ///:-

82

Misliti na Javi

U posled njem p o m e ra n ju , rezultu ju a v red n o st nije d o d eljena prom enljivoj b, ve je d irek tn o ispisana, p a se d o b ija isp ravn a v rednost. Evo p rim e ra koji p o kazuje korienje svih o p e ra to ra koji se o d n o se n a bitove:
//: operatori/RadSaBitovim a.java // Korienje operatora nad bitovim a. import ja v a .u t il import s t a t ic n e t.m in d view .u ti1. P r in t . * ; p ublic class RadSaBitovima { public s t a t ic void main (S t r in g [] args) { Random slucajan = new Random(47); in t i = s lu c a ja n .n e x tln t (); in t j = s lu c a ja n .n e x t ln t (); p r in t B in a r y In t ( " - l" , -1); p r in tB in a r y In t("+ l", +1); in t maxpos = 2147483647; pri n tB in a r y In t("n a jv e i" , maxpos); in t maxneg = -2147483648; pri ntBi narylnt("najm anj i " , maxneg); p r i n t B in a r y In t ( " i", i ) ; p r in t B in a r y In t ( " ~ i" , - i ) ; p r in t B in a r y ln t ( " - i" , - i ) ; p r in t B in a r y In t ( " j " , j ) ; p r in t B in a r y In t (" i & j " , i & j ) ; p r in t B in a r y In t (" i | j " , i | j ) ; p r in t B in a r y In t (" i " j " , i ~ j ) ; p r in t B in a r y In t (" i 5 ", i 5 ); p r in t B in a r y In t (" i 5 , i 5 ) ; p r in t B in a r y In t ( " ( - i) 5 ", ( i ) 5 ); p r in t B in a r y In t (" i > 5 ", i > 5 ); p r in tB in a r y In t( " ( - i ) > 5 ", (~ i) > 5); long 1 = s lu c a ja n . nextlong( ) ; long m = s lu c a ja n . nex tLo n g(); p rin tB in a ry L o n g ("- lL ", -1L); prin tB in aryLo n g (" +1L , +1L); long 1 1 = 9223372036854775807L; pri ntBi naryLong( " n a jv e i", 11); long 11n = -9223372036854775808L; p rin tB i naryLong("najmanj i " , 1 1n ); pri ntBi naryLong ( " 1 " , 1 ); p rin tB in aryLo n g ("~ 1 " , -1); p rin tBin aryLong( " - 1 " , -1); prin tB in aryLon g ("m ", m); p rin tB in a ryLo n g ("l & m", 1 & m); p rin tB in a ryLo n g ("l | m", 1 | m); p rin tB in aryLo n g ("l " m", 1 ^ m); p r intBinaryLong("1 5 ", 1 5 );

Poglavlje 3: Operatori

83

prin tB in aryLo n g (" 1 p rintBin aryLo ng (" (-1) p ri ntBi naryLong(" 1 > p rin tB in aryLo n g (" (-1)

5", 1 5 ); 5" , (-1) 5 ); 5", 1 > 5 ); > 5" , (-1) > 5)

) J s t a t ic void p rin tB i n a ry ln t(S t ring s, in t i ) p r in t(s + ", in t : " + i + " , binarno:\n In t e g e r .t o B in a r y S t r in g (i));

}
s t a t ic void p rin tB in aryLo n g (String s, long 1) { p r in t(s + " , long: ".+ 1 + " , binarno:\n " + L o n g .to B in a r y S tr in g (l));

}
} /* Is p is : -1, in t : -1, binarno:

11111111111111111111111111111111
+1, in t : 1, binarno:

1
n a jv e i, in t : 2147483647, binarno:

1111111111111111111111111111111
najm anji, in t : -2147483648, binarno:
10000000000000000000000000000000

i,

in t : -1172028779, binarno:

10111010001001000100001010010101
- i , in t : 1172028778, binarno: 1000101110110111011110101101010 - i, in t : 1172028779, binarno: 1000101110110111011110101101011 j , in t : 1717241110, binarno: 1100110010110110000010100010110 i & j , in t : 570425364, binarno: 100010000000000000000000010100 i | j , in t : -2 5 2 1 3 0 3 3 , binarno: 11111110011111110100011110010111 i A j , in t : -5 9 5 6 3 8 3 9 7 , binarno: 11011100011111110100011110000011 i 5 , in t : 1149784736, binarno: 1000100100010000101001010100000 i 5 , in t : -3 6 6 2 5 9 0 0 , binarno: 11111101110100010010001000010100 ( i ) 5 , in t : 3 6625899, binarno: 10001011101101110111101011 i > 5 , in t : 97591 8 28, binarno: 101110100010010001000010100 ( i ) > 5 , in t : 36625899, binarno: 10001011101101110111101011

* // /= -

84

Misliti na Javi

Dve m eto de na k raju , printBinaryInt() i printBinaryLong(), ispisuju v red n o st tipa int ili long u b in a rn o m o b lik u zajed n o s tek stu a ln im o p iso m . O sim to p o kazuje dejstvo svih o p e ra to ra n ad b ito v im a n a v re d n o sti tip a int i long, ovaj p rim e r pokazuje i m in im aln u i m aksim alnu v red n o st kao i v re d n o sti +1 i -1 za tipove int i long, pa m oete videti kako o n e izgledaju. O b ra tite p an ju n a to da najvii b it p redstavlja znak: 0 oznaava p o zitivan broj, a 1 negativan. P rik azan je rezu ltat p ro g ram a za deo koji se tie tip a int. O vakav b in arn i zapis celih b rojeva naziva se drugi kom plem ent.

Veba 11: (3) K renite o d b ro ja s je d n o m b in a rn o m jed in ico m na n ajznaajnijem m estu.


(U putstvo: u p o treb ite h ek sad ecim a ln u k o n sta n tu ). P o m erajte tu jed in ic u o p e ra to ro m oznaenog p o m era n ja u d esn o p o svim m o g u im b in a rn im polo ajim a i sve ih prikaite m e to d o m Integer.toBinaryString().

Veba 12: (3) K renite o d b ro ja sa svim b in a rn im jed in icam a. P o m erite ih za je d n o m esto


ulevo, a zatim ih o p e ra to ro m n eo zn aen o g p o m e ra n ja p o m e ra jte u d esn o p o svim m o guim b in a rn im p o lo ajim a i sve ih p rik aite m e to d o m Integer.toBinaryString().

Veba 13: (1) N apiite m e to d u k oja p rik azu je char v red n o sti u b in a rn o m obliku. Rezultate rada m eto d e prikaite n a n ek o lik o razliitih znakova.

Ternarni operator uslovljavanja


Ternarni o p e ra to r uslovljavanja n eo b ian je jer im a tri o p era n d a. O vo je pravi op erato r, jer kao rezultat daje v red n o st, za razliku o d uobiajene n ared b e if-else koju ete videti u sledeem o deljku ovog poglavlja. O p e ra to r se k oristi u obliku: lo g i k i- iz r a z ? vrednostO : v red n o stl Ako je v red n o st logikog-izraza tr u e , izraunava se vrednostO i taj rezultat postaje vrednost operacije. Ako je v red n o st logikog-izraza false, izraunava se vrcdnostl i njen rezultat postaje v redn o st cele o p eracije. N aravno, m oete da k o ristite i u o b iajen u k o n stru k c iju if-else (o p isan u kasnije), ali je te rn arn i o p e ra to r m n o g o saetiji. Iako je jed n a od n ajistak n u tijih osobina jezika C (iz koga ovaj o p e ra to r potie) m o g u n o st pisanja sa.etih izraza i iako je tern arn i o p e ra to r delim ino uveden zbog efikasnosti, treb a biti o p rezan pri njegovom sv akodnevnom korienju jer lako m oe da pro izv ed e neitljiv kod. O p e ra to r uslovljavanja se razlikuje od k o n stru k cije if-else p o to m e to izraunava vrednost. Evo p rim era u kojem ete videti tu razliku: / / : o p e ra to ri/T e rn a ry IfE ls e .jav a import s t a t i c n e t.m in d v ie w .u til.P r i n t.* ; public c la s s T ern ary IfE lse { s t a t i c in t t e r n a r n i f in t i) { re tu rn i < 10 ? i * 100 : i * 10; I s t a t i c in t sta n d a rd n iIfE ls e ( in t i) { i f (i < 10)

Poglavlje 3: Operatori

85

return i * 100; el se return i * 10;

}
p ub lic s t a t ic void m a in (S trin g [] args) p r in t ( t e r n a r n i( 9 ) ) ; p r in t ( t e r n a r n i(1 0 )); p r in t ( s t a n d a r d n iIf E ls e ( 9 ) ) ; p r in t ( s t a n d a r d n iIf E ls e ( lO ) ) ; {

}
} /* Is p is : 900

100
900

100

* ///:V idite d a je k o d m eto d e ternarni() saetiji od o noga to biste m o rali pisati d a te rn a rnog o p e ra to ra nem a, kao u m eto d i standardniIfElse(). M e u tim , standardniIfElse() se lake ita i bre pie. Stoga d o b ro razm islite p re nego to u p o treb ite te rn a rn i o p e ra to r po pravilu, to je u m esn o kada pro m en ljiv o j dodelju jete je d n u od dve v rednosti.

Operatori + i += za znakovne nizove


Postoje o p e ra to ri koji u favi im aju specijalnu n am en u : o p era to ri + i + = m o g u se koristiti za nadovezivanje znako v n ih nizova, kao to ste ve videli. ini se da je to razu m ljiv nain korienja tih o p e rato ra, iako se ne u k lap a u trad icio n aln i nain njihovog korienja. O va m og u n ost se inila kao d o b ra ideja pro jek tan tim a C + + -a, pa su u jezik dodali prcklapnnje operatora (engl. operatoroverloading) koje je om oguilo dodavanje znaenja skoro svakom o p e ra to ru . N aalost, preklapanje o p erato ra je, u kom binaciji s nekim d ru g im o g ranienjim a C + + -a , ispalo p rilino kom plikovano za p rog ram ere koji su tu m o g u n o st hteli da u grade u svoje klase. Iako bi se preklapanje o p erato ra m nogo jednostavnije realizovalo na Javi nego na C ++-U (to je pokazano u jeziku C# koji inui jedn ostav n o preklapanje o p era to ra ), ova m o gunost je i dalje ocenjena kao previe sloena, pa p ro g ram eri na Javi ne m ogu da preklope o p erato re, kao to m ogu p ro g ram eri na C #-u i C++-U . Pri korien ju o p era to ra na znak o v n im nizovim a, javlja se zan im ljiv efek at. A k o je p rv i o p eran d izraza tipa String, tada svi o p e ra n d i koji slede tako e m o raju biti tog tip a (setite se da e prevodilac au to m atsk i p retv o riti niz znakova pod navodnicim a u String):
//: o p e ra to ri/S trin g O p e rato rs.ja va import s t a t ic n et.m in d view .u ti1. P r in t . * ; p u b lic c lass String O p eratori { p ub lic s t a t ic void main( S t r i ng[] args) { in t x = 0, y = 1, z = 2; S trin g s = "x, y , z pri n t(s + x + y + z ) ; p rin t(x + " " + s ) ; // Pretvara x u Strin g

86

Misliti na Javi

s + = (sabrano) = // Operator nadovezivanja p rin t(s + (x + y + z ) ) ; p r i n t ( " + x ); // Skraeni zapis umesto In te g e r.to S trin g O

}
) /* Is p is : x, y , z 012 0 x, y , z x, y , z (sabrano) = 3

0 * ///:O b ratite pan ju na to da je p rv a n a red b a p rin t ispisala 012, a ne 3, to b ism o dobili da su ti celi brojevi sab ran i. O vo se esilo zato to e Java prevodilac p retv o riti x, y i z u znakovne ekvivalente i nadovezati ih, u m esto da ih p rv o sabere. D ruga n a re d b a p rin t pretvorila je vodeu p ro m en ljiv u u Stri.ng, dakle ta konverzija ne zavisi o d redosleda argum enata. N ajzad, v idite kako je o p e ra to r + = u p o tre b lje n da se prom enljivoj s doda znakovni n iz i kako je z ag rad o m o d re e n red o sle izrau n av an ja izraza, da bi celi brojevi u njoj bili sabrani p re prikazivanja. Im ajte u vid u posled n ji p rim e r u m e to d i main(): p razan znak o v n i niz iza kojeg sledi + i neki p ro st tip. Java e au to m atsk i p retv o riti taj tip u String, a da n ism o m orali da pozivam o g lom azniju izriitu m eto d u , u ovom sluaju, Integer.toString().

este greke prilikom korienja operatora


N ainiete je d n u od estih greaka ako p ri ra d u sa o p e ra to rim a izostavite zagrade a niste sasvim sig u rn i kako e tei izrau n av an je izraza. O vo vai i u Javi. Veoma esta greka u C -u i C + + -u je:
while (x = y) {

/ / } P ro gram er je o igledno eleo da ispita jed n ak o st (= = ), a ne da vri dodelu. U C -u i C + + -u rezultat ove d o d ele bi uvek b io true, ako je y razliito od nule, i verovatno biste dobili b eskrajn u petlju. R ezultat ovog izraza u Javi nije tipa boolean, ali prevodilac oekuje tip boolean i nee izvriti konverziju iz tipa int, nego e p ri prevoenju prijaviti greku. Tako e p ro b lem biti otk riv en p re nego to p o k u ate da pok ren ete program . Stoga se ovaj tip zam ke u Javi ne javlja. (G reku p ri p rev o d en ju jed in o neete dobiti kada su x i y tipa boolean; tada je x = y ispravan izraz to bi u p re th o d n o m p rim eru verovatno predstavljalo greku u k u can ju .) Slian p ro blem u C -u i C + + -u jeste korienje k o nju n k cije i isjunkcije nad bitovim a um esto logikih varijanata. K onjunkcije i disju n k cije nad b ito v im a se piu s jed n im sim bolom (& ili I), dok se logika k o n ju n k cija i d isjunkcija pi.u s dva (&& i II). Isto kao i sa = i = = , lako je grekom upisati sam o jed an znak um esto dva. Javin prevodilac to ponovo spreava, jer nee dozvoliti da iz n eh ata iskoristite pogrean tip u izrazu.

Poglavlje 3: Operatori

87

Eksplicitna konverzija tipova


Kada je p o treb n o , Java au to m atsk i p retv ara jed an tip po d atak a u drugi. Na prim er, ako dodelite celobrojnu v red n o st prom enljivoj u fo rm a tu p o k retn o g zareza, prevodilac e a u to m atski konvertovati tip int u tip float. P ro g ram er m oe i da zahteva eksplicitnu konverziju (engl. casting ) ili da je sprovede u sluajevim a u kojim a se n o rm a ln o ne bi dogodila. D a biste izvrili eksplicitnu konverziju, n avedite eljeni tip u n u ta r zagrada, s leve strane v red n o sti ko ju tre b a k o nvertovati. Na p rim er:
// : o p e r a to ri/ iz ric ite K o n v e rz ije .ja v a public c lass iz r ic ite K o n v e r z ije { p ublic s t a t ic void m a in (S trin g [] args) { in t i = 200; long 1ng = (lo n g )i ; lng = i ; // "P ro iru ju a k o n v e rz ija ", pa e k s p lic itn a konverzija // zapravo n ije potrebna long lng2 = (long)200; 1ng2 = 200; // "Suavajua k o n ve rz ija ": i = ( in t ) lng2; // Ovde j e e k s p lic itn a konverzija neophodna

} III-Kao to vidite, eksplicitnu konverziju je m ogue izvriti na nu m erik o j vred n o sti, kao i na p rom enljivoj. Im ajte u vidu da je ozvoljena i izlina eksplicitna konverzija. Na p rim er, prevodilac, kad god treb a, a u to m atsk i prevodi v red n o st tip a int u tip long; ipak, suvina konverzija se dozvoljava, je r tim e m oete d a ie naglasite ili da kod u inite jasnijim . U d ru g im situacijam a, eksplicitna konverzija m oe b iti n e o p h o d n a da bi kod uop te m o gao biti preveden. U C -u i C++-U , a u to m atsk a konverzija m oe izazvati m alo glavobolie. U Javi je a u to m atska konverzija b ezb ed n a osim kada vrite takozvanu snavajuu konverziju (tj. kada prelazite sa tipa p o d atak a koji m oe da uva vie inform acija na onaj koji uva m anje), pri em u rizikujete da izgubite inform acije. U to m siuaju prevoilac vas prisiljava da vrite eksplicitn u konverziju, obavetavajui vas na taj nain da ,,to m oe biti op asn o - a ako ipak to elite, m oraete izriito da traite konverziju. Proiirujuu konverzijn ne treba izriito zahtevati jer novi tip m oe da uva z n a tn o vie in fo rm acija nego stari, pa se inform acije nikad ne gube. Java dozvoljava da izvrite e k sp li tn u konverziju iz bilo kog pro sto g tipa u bilo koji dru gi prost tip, izuzev tipa boolean koji u o p te ne m oete k o nvertovati. Klase takoe ne dozvoljavaju eksplicitnu konverziju. Da biste konvertovali jed n u klasu u d ru g u , za to m o ra da postoji posebna m etoda. (Kasnije u knjizi videete i da se m oe vriti eksplicitna konverzija objekata u o k v iru porodice tipova; Hrast m oe biti eksplicitno k o nvertovan u Drvo i o b rn u to , ali ne i u stran i tip kao to je Stena.)

88

Misliti na Javi

Odsecanje i zaokruivanje
K ada obavljate suavajuu konverziju, m o ra te p aziti na odsecan je i zaokru iv an je. P rim era rad i, ako realan bro j tip a float izriito p retv o rite u ceo bro j tip a int, ta u ra d i Java? Na p rim er, broj 29.7 p retv arate u int - hoete li d o b iti 30 ili 29? O d g o v o r n a to p ita n je naved e n je u sledeem p rim e ru :
//: o p e ra to ri/ Iz ric ita K o n v e rz ija B ro je v a .ja v a // ta se deava kada broj tip a flo a t // i l i double i z r i i t o p re tv o rite u ceo b ro j? import s t a t ic n e t.m in d v ie w .u til. P r in t . * ; pu b lic class Iz ric ita K o n v e rz ija B ro je v a { public s t a t ic void m a in (S trin g [] args) { double iznad = 0.7, ispod = 0.4; flo a t fiznad = 0 .7 f, fispod = 0 .4 f; p r in t ( " (in t ) iz n a d : " + ( in t)iz n a d ); p r i n t ( (in t )is p o d : " + (in t )is p o d ); p r in t ( " (in t ) f iz n a d : " + ( in t ) f i z n a d ) ; p r in t ( " (in t)fis p o d : " + ( in t ) f i s p o d ) ;

}
} /* Is p is : (in t)iz n a d : (in t)is p o d : (in t)fiz n a d : (in t)fis p o d : 0 0 0 0

* ///:Dakle, odgovor je da se p rilik o m eksplicitne konverzije tipa float ili double u ceo broj (tip int), decim ale uvek odsecaju. U koliko h oete da rezultat b u d e z ao k ru en , u p o tre b ite m e to d u round() iz b iblioteke java.lang.Math:
//: o p erato ri/Z aok ru zivan jeB rojeva.java // Zaokruzivanje brojeva tip a flo a t i double. import s t a t ic n e t.m in d v ie w .u til. P r in t .* ; p ublic class ZaokruzivanjeBrojeva { p ublic s t a t ic void m a in (S trin g [] args) { double iznad = 0.7, ispod = 0.4; flo a t fiznad = 0 .7 f, fispod = 0 .4 f; p r in t("M a th .roun d(iznad): " + Math.round(iznad)) ; p r in t ( "M ath.round( i spod): " + Math. round(ispod)) ; p r in t ("M a th .rou n d (fi znad): " + M ath.round (fiznad)) ; p rin t("M a th .ro u n d (fis p o d ): 1 1 + Math. ro u n d (fisp o d )) ;

}
} /* Is p is : M ath.round(iznad): 1 M ath.round(ispod): 0 M ath .ro und (fiznad): 1 Math. ro u n d (fisp o d ): 0

* ///:-

Poglav[je 3: Operatori

89

Poto round() p rip ad a biblioteci java.Iang, nije p o tre b n a p o seb n a n ared b a iinport za n jen o uvoenje i korienje.

Unapreenje tipova
Ako vrite bilo koju m atem atik u operaciju i operaciju nad b itov im a s p ro stim tipo vim a p o datak a, koji su m anji o d int (tj., char, byte ili short), videete d a e te v red n o sti b iti u n ap re en e u tip int pre nego to se operacije izvre, a rezultat e tak o e biti tipa int. Stoga, ako tu v red n o st elite da p onovo dodelite m an jem tip u , rn orate da koristite eksplicitnu konverziju. (A poto vrite d o d elu m an jem tip u , m o gu se izgubiti inform acije.) U prin cip u , najvei tip p o datak a u izrazu o d re u je veliinu rezu ltata tog izraza; ako p o m n o ite float i double, rezultat e b iti tipa double; ako saberete int i long, rezultat e biti tip a long.

Java nema operator za odreivanje veliine


U C -u i C ++-U o p e ra to r sizeof() zadovoljava specifinu p o treb u : o n daje bro j bajtova koje podaci zauzim aju. Prenosivost je n ajb itn iji razlog za korienje o p e ra to ra sizeof() u C -u i C ++-U . Razliiti tip o v i p o d atak a m o gu im ati razliite veliine n a razliitim rau n arim a. Stoga p ro g ra m e r m o ra da im a in fo rm aciju o veliini tip o v a koje koristi, p re nego to izvri neku o peraciju za koju je b itn a veliina p ro m en ljivih. Na p rim er, jed a n ra u n ar m oe da uva celobrojne veliine sa 32 bita, d o k d ru g i to m oe d a rad i sa 16 bitova. Program i na p rv o m ra u n a ru mogli bi da uvaju vee v red n o sti u c elob ro jn im p ro m en ljiv am a. Kao to m oete da zam islite, prenosivost zadaje velike glavobolje p ro g ra m e rim a na C -u i C++-U . lavi ne treba o p e ra to r za o d re iv an je veliine jer su svi tipovi p o d ata k a uvek iste veliine, na svim ra u n arim a. Na ovom nivou ne razm iljajte o prenosivosti - o n a je u g ra ena u jezik.

Saet pregled operatora


Sledei p rim e r pokazuje koji prosti tipovi m ogu da koriste o d re e n e o p erato re . U osnovi, to je isti p rim e r koji se stalno ponavlja, sam o se u p o treb ljavaju d ru g i p ro sti tipovi. Ova d ato tek a se m oc prevesti bez greaka, jer su redovi koji m og u da izazovu greku p retv oreni u k o m e n ta r p o m o u //!.
//: o p e ra to ri/ S v iO p e ra to ri.java // Prik az uje sve operatore nad svim prostim tipovima podataka // da bi pokazao koje su o p era cije dozvoljene. p ub lic c lass SviO peratori ( // Da bi se p r ih v a t ili re z u lta ti logikog te s ta : void f(boolean b) {} void boolTest(boolean x, boolean y) {

90

Misliti na Javi

// A ritm e ti k i o p e ra to ri: // X = X * y; // X = x / y ; // X = x % y; // X = x + y; // X = x - y ; + // x+ // X-// X = +y; // X = -y; // Poreenje i f(x > y ) ; // f (X > = y ); // f(x < y ) ; // f(x < = y ); // f(x = = y ); f(x != y ) ; f(!y ); x = x && y; x = x || y; // Operatori nad bitovim a: //! x = x = x = x x x x = & | ~ ~y; y; y; y;

//! x = x
//! x = x

1;
1;

/ / ! x = x > 1;
// Sloena dodela: = y; // x + // x -= y; // x *= y; // x /= y; // x %= y; // x = 1; // x = 1; // X >= 1 x &= y; x A= y; x |= y ; // E k s p lic itn a kon verzija: //! char c = (ch ar)x ; //! byte b = (b yte)x ; //! short s = (s h o rt)x ; //! in t i = ( in t ) x ; //! long 1 = (lon g)x ; //! flo a t f = (f lo a t ) x ; //! double d = (double)x;

1
void charTest(char x, char y) {

Poglavlje 3: Operatori

91

// A ritm e ti k i x = (c h a r)(x * x = (c h a r)(x / x = (c h a r)(x % x = (c h a r)(x + x = (c h a r)(x x++;

o p e ra to ri: y ); y ); y ); y ); y );

x--; x = (char)+ y; x = (ch ar)- y; // Poreenje i lo g i k i: f(x > y ) ; f(x > = y ); f(x < y ) ; f(x < = y ); f(x = = y ); f(x ! = y ) ; //! f ( ! x ) ; //! f(x && y ) ; //! f(x || y ) ; // Operatori nad bitovima: x= (ch ar)~ y; x = (c h a r)(x & y ) ; x = (c h a r)(x | y ) ; x = (c h a r)(x ~ y ) ; x = (c h a r)(x 1); x = (c h a r)(x 1); x = (char) (x > 1); // Sloena dodela: x + = y; x -= y; x *= y; x /= y; x %= y ;

X = 1;
X X = > = 1 ; 1;

x &= y; x "= y ; x |= y; // E k s p lic itn a konverzija: //! boolean bl = (boolean)x; byte b = (b yte )x ; short s = (s h o rt)x ; in t i = ( in t ) x ; long 1 = (lon g )x ; flo a t f = ( f lo a t ) x ; double d = (double)x;

)
void byteTest(byte x, byte y) {

92

Misliti na Javi

// A ritm etik i o p e ra to ri: x = (b y te )(x * y ) ; x = (b y te )(x / y ) ; x = (b y te )(x % y ) ; x = (b y te )(x + y ) ; x = (b y te )(x - y ) ; x++;

x ;
x = (byte)+ y; x = (b yte)- y; // Poreenje i lo g i k i: f(x > y ) ; f(x > = y ); f (x < y ) ; f(x < = y ); f(x = = y ); f(x != y ) ; //! f ( ! x ) ; //! f(x && y ) ; //! f(x || y ) ; // Operatori nad bitovim a: x = (b yte )- y; x = (b y te )(x & y ) ; x = (b y te )(x | y ) ; x = (b y te )(x ~ y ) ; x = (byte) (x 1); x = (b y te )(x 1); x = (b yte) (x > 1 ); // Sloena dodela: x x x x + = -= *= /= y; y; y; y;

x %= y;

x = 1; x = 1; x > = 1;
x &= y ; x A= y; x |= y ; // E k s p lic itn a ko n verz ija: //! boolean bl = (boolean)x; char c = (ch ar)x ; short s = (s h o rt)x ; in t i = ( in t ) x ; long 1 = (lo n g )x ; f lo a t f = ( f lo a t ) x ; double d = (double)x;

}
void shortTest(short x, short y) {

Poglavlje 3: Operatori

93

// A ritm e ti k i o p e ra to ri: X = (s h o rt)(x * y ) ; x = ( s h o r t ) (x / y ) ; x = (s h o rt)(x % y ) ; x = (s h o rt)(x + y ) ; x = (s h o rt)(x - y ) ; x++; x--; x = (sh ort)+ y; x = (s h o rt)- y ; // Poreenje i lo g i k i: f(x > y ) ; f(x > = y ); f (x < y ) ; f(x < = y ); f(x = = y ); f (x ! = y ) ;

//! f(!x );
//! f(x && y ) ; //! f(x || y ) ; // Operatori nad bitovim a: x = (sh o rt)~ y ; x = (s h o rt)(x & y ) ; x = (sh o rt) (x | y ) ; x = (s h o rt)(x ~ y ) ; x = (s h o rt)(x 1 ); x = (s h o rt)(x 1 ); x = (sh o rt) (x > 1 ); // Sloena dodela: x + = y; x -= y; x *= y; x /= y; x %= y;

x = 1; X = 1; X > = 1;
x & = y;

x "= y ;
x 1 = y; // E k s p lic itn a ko n verz ija: //! boolean bl = (boolean)x; char c = (ch a r)x ; byte b = (b yte )x ; in t i = ( in t ) x ; long 1 = (lo n g )x ; flo a t f = ( f 1o a t )x ; double d = (double)x;

)
void in t T e s t(in t x, in t y) {

94

Misliti na Javi

// A ritm etik i o p e ra to ri: x = x * y; x = X / y; x = x % y; x = x + y; x = x - y; x++; x--; x = +y; x = -y; // Poreenje i lo g i k i: f(x > y ) ; f ( x >= y ) ; f(x < y ) ; f ( x <= y ) ; f ( x == y) f ( x ! = y) //! f(!x); / / ! f(x & & y); / / ! f(x || y); // Operatori nad bitovim a: x = -y; x = x & y; x = x | y; x = x A y; x = x 1; X = x 1; x = x > 1; // Sloena dodela: x += y; x -= y ; x *= y; x x x X x /= y; %= y; = 1; = 1; >= 1;

x &= y; x "= y; x |= y; // E k s p lic itn a ko nverzija: //! boolean bl = (boolean)x; char c = (ch ar)x ; byte b = (b yte)x ; short s = (sh o rt)x ; long 1 = (long)x ; f lo a t f = (f lo a t ) x ; double d = (double)x;

}
void longTest(long x, long y) {

Poglavlje 3: Operatori

95

// A ritm etik i o p e ra to ri:

x = x x = x x = x x = x x = x x++;
x--;

* / % + -

y; y; y; y; y;

x = +y; x = -y;
// Poreenje i lo g i k i:

f(x f(x f(x f(x f(x f(x //! //! //! x x x x x x x = = = = = = =

> y); >= y); < y); <= y); == y); != y); f(!x); f(x && y); f(x M y);
-y ;

// Operatori nad bitovim a:

x x x x x x

& y; | y; A y; 1; 1; > 1;

// Sloena dodela: x + = y; x -= y; x *= y; x /= y ; x %= y ;

x = 1; x = 1;
x >= 1; x & = y; x "= y ;

x1 = y;
// E k s p lic itn a konverzija: //! boolean b = (boolean)x; char c = (ch ar)x ; byte b = (b yte )x ; short s = (sh o rt)x ; in t i = (in t ) x ; flo a t f = (f lo a t ) x ; double d = (double)x;

I
void floatTest(float x, float y)

96

Misliti na Javi

// A ritm etik i o p e ra to ri: x = x * y; x =x x =x x =x x =x x++; / % + y; y; y; y;

x--;
x = +y; x = -y; // Poreenje i lo g i k i:

f(x > y); f ( x >= y ) ; f(x < y ) ;


f(x < = y ); f(x = = y );

f ( x != y ) ;
//! f ( ! x ) ; //! f(x && y ) ; //! f(x M y ) ; /'/ Operatori nad bitovim a: //! x = -y; //! x = x & y; //! x = x | y; //! x = x ~ y;

//! X = X //! X = X

1; 1;

//! X = X > 1; // Sloena dodela: x + = y; x -= y ; x *= y; x /= y ; x %= y;

/ / ! x = 1; / / ! x = 1; / / ! x > = 1;
//! x &= y; //! x -= y; //! x |= y; // E k s p lic itn a kon verzija: //! boolean bl = (boolean)x; char c = (ch ar)x ; byte b = (b yte)x ; short s = (sh o rt)x ; in t i = (in t ) x ; long 1 = ( 1ong) x ; double d = (double)x;

I
void doubleTest(double x, double y)

Poglavjje 3: Operatori

97

// A ritm etik i o p e ra to ri: x = x * y; x = x / y; x = x % y; x = x + y; x = x - y; x++; x--; x = +y; x = -y; // Poreenje i lo g i k i: f(x > y ) ; f (x > = y ); f (x < y ) ; f(x < = y ); f(x = = y) f(x != y) //! f ( ! x ) ; //! f(x && y ) ; //! f(x || y ) ; // Operatori nad bitovim a: //! //! //! //! x x x x = = = = -y; x & y; x | y; x " y;

//! x = x

1;

//! x = x 1; //! x = x > 1; // Sloena dodela: x + = y; x -= y; x *= y; x /= y;

x % = y; X = 1; x = 1;
x >= 1; x & = y; x A= y;

x |= y ;
// E k s p lic itn a konverzija: //! boolean bl = (boolean)x; char c = (ch ar)x ; byte b = (b y te )x ; short s = (s h o rt)x ; in t i = ( in t ) x ; long 1 = (lon g)x ; flo a t f = (f lo a t ) x ;

98

Misliti na Javi

O b ra tite p an ju n a to da je tip boolean p rilin o o granien. M oete m u o d eliti vredn o sti true i false i ispitivati v re d n o st, ali ne m oete da vrite sab iran je ili bilo koju d ru g u operaciju. Kod tip o v a char, byte i short m oete v id eti efekte u n ap re en ja tip a (pro iriv an ja) koje se javlja p ri u p o tre b i aritm e tik ih o p erato ra . Svaka aritm etik a o p eracija p rim e n je n a na ove tipove daje rezultat tip a int, koji m o ra ek sp licitn o m konverzijom da b u d e vraen u p rv o b itn i tip (to je suavajua konverzija p ri kojoj se m ogu izgubiti in form acije). Eksplicitn a konverzija nije p o tre b n a kada k o ristite v red n o sti tip a int, p o to je sve ve tip a int. Ipak, n e m o jte se o p u stiti i m isliti kako je sve sigurno. Ako p o m n o ite dve v red n o sti tipa int koje su dov o ljn o velike, d oi e d o p rek o raenja. To pokazuje n a re d n i p rim er:
//: o p e ra to ri/P re k o racen je.ja va // Iznenaenje! Ja va dozvoljava prekoraenje. p ub lic c lass Prekoracenje { p ublic s t a t ic void m a in (S trin g [] args) { in t v e lik i = Integer.MAX_VALUE; System.out .p ri ntl n ("v e l i ki = 1 1 + v e lik i); i nt veci = v e li ki * 4; S y s te m .o u t.p rin tln ("jo s veci = " + v e c i);

I
} /* Is p is : v e lik i = 2147483647 jo s veci = -4

* III-Ne dobijate niti p o ru k u o greci niti u p o zo ren je od prevodioca, niti se javlja izuzetak pri izvravanju. Java je d o b ra , ali nije tohko d o b ra. Sloena dodeljivanja nc zahtevaju eksp licitn u konverziju za tipove char, byte ili short, iako vre p ro iren ja koja im aju isti rezu ltat kao i d irek tn e aritm etike operacije. S d ru g e stran e, izostavljanjem eksplicitne konverzije zasigurno se uproava kod. M oete p rim e titi kako svi prosti tip o v i, osim tipa boolean, m ogu biti eksplicitno konvertovani u bilo koji d ru g i p ro st tip. P onovo n ap o m in jem , vodite rau n a o efektu suavanja kaa vrite e k sp li tn u konverziju u m anji tip, jer m oete n ehotice da izgubite inform acije tok o m konverzije.

Veba 14: (3) N apiite m e to d u koja p rim a dva znakovna niza kao arg u m e n te i p o red i ili p o m o u svih boolean p o re en ja. Ispiite rezultate. 7.a = = i != obavite i test equals(). Pozovite svoju m e to d u iz main() s nekoliko razliitih String objekata.

Saetak
Ako im ate iskustva s bilo kojim jezik o m ija je sintaksa slina C -ovoj, videli ste kako su Javini o p e rato ri toliko slini o n im a iz vam a zn a n ih jezika da ih gotovo ni ne treb a uiti. U koliko vam je poglavlje bilo tesko, p ogledajte m u ltim ed ijsk u prezentaciju T hinking in C, koja je d o stu p n a na adresi www.M iin1Vicw.iici. Reenja odabranih vebi data su u elektronskom dokumentu Thinking in lava Annotatcd Solution Guide, koji se moe kupiti na lokaciji tvww.MindView.rwt.

Kontrolisanje izvravanja
Kao i svesno bie, i program tnora da upravlja svojim okruenjem i da bira ta e da radi. U lavi birate pom ou naredaba za kontrolisanje toka programa.
JAVA KORISTI SVE NAREDBE JEZIKA C ZA KONTROLU IZVRAVANJA, STOGA E VAM, AKO STE

p ro g ram ira li na C -u ili C ++-U , vei deo ovoga to sledi biti p o zn at. M n ogi p ro ced u raln i p ro g ram sk i jezici im aju neku v rstu n ared b e za k o n tro lu i m e u jezicim a esto posto je slinosti. M e u rezervisane rei za k o n tro lu izvravanja u Javi sp ad aju if-else, while, dowhile, for, return, break i n are d b u izbora switch. Java, m e u tim , ne p o d rava p rilin o o p a sn u n a re d b u goto (koja i dalje m oe b iti najefikasniji nain za reavanje o d re e n e vrste p ro b lem a ). I dalje m oete da n ap rav ite skok koji je nalik na goto, ali je m n o g o vie o g ran ien o d tip in e nared b e goto.

Logike vrednosti
Sve uslovne n ared b e koriste tan o st i n etan o st uslovnih izraza da bi odredile to k izvravanja. P rim er uslovnog izraza je a = = b. Tu se k o risti o p e ra to r p o re en ja = = d a se utvrdi da li je v red n o st a jed n ak a v red n osti b. Ovaj izraz vraa true ili false. I bilo koji o p e ra to r p o re en ja iz p re th o d n o g poglavlja m oe se ko ristiti za fo rm iran je uslovnog izraza. O b ratite p an ju na to da Java ne dozvoljava da kao logiki tip (boolean) koristite broj, iako je to dozvoljeno u C -u i C ++-U (gde je tan o sve razliito o d nule, a n eta n o je nu ia). Ako elite da k o ristite nelogiki tip u logikom testu, na p rim e r if(a), prvo ga m o rate pretvoriti u v re d n o st tipa boolean p o m o u usiovnog izraza, na p rim e r if(a != 0).

Naredba if-else
N aredba if-else je najosnovniji nain na koji se k o ntrolie to k p ro g ram a. D eo else nije obavezan, pa stoga if m oete da koristite u dva oblika:
i f (1ogi k i- i zraz) naredba

i f (1ogi k i- i zraz) naredba el se naredba

Logiki izraz m o ra da vraa rezultat tipa boolean. Naredba je jed n o stav n a nared b a koja se zavrava z nakom taka i zarez, ili sloena n ared b a, o d n o sn o g ru p a naredaba u n u ta r vitiastih zagrada. Svaki p u t kada se koristi term in naredba, p o d razu m ev a se da ona m oe biti jednostavna ili sloena.

100

Misliti na Javi

Kao p rim e r za if-else, sledi m eto d a test() koja saoptava da li je p retp o stav ljen a vredn ost vea, m an ja ili je d n ak a ciljnoj v red n o sti:
//: c o n tr o l/ If E ls e .ja v a import s t a t ic n e t.m in d view .u ti1 .P r in t p ub lic c lass If E ls e { s t a t ic in t re z u lta t = 0; s t a t ic void t e s t ( in t vrednost, in t c i l j ) if(v re d n o s t > c i l j ) re z u lta t = +1; e lse if(v re d n o s t < c i l j ) re z u lta t = -1; el se re z u lta t = 0; // Po k lo p ile su se

}
p ublic s t a t ic void m a in (S trin g [] args) { te s t(1 0 , 5 ); p ri n t ( r e z u l t a t ) ; t e s t(5 , 10); p rin t(re z u lta t); t e s t(5 , 5 ); pri n t ( r e z u l t a t ) ;

}
} /* Is p is :

1 -1 0 * ///= Negde na sredini koda m eto d e test() v idite kom bin aciju else if, to nije nova rezervisana re, nego jed an else iza kojega je nova n ared b a if. Iako je Java jezik slo b o d n o g o blika, kao C i C + + , uvlaenje tela n ared b e za k o n tro lu izvravanja je korisn o kako bi italac m ogao Iake da o dredi gde o n o poinje i gde se zavrava.

Petlje
Petlje se k o n tro liu rezervisanim reim a w h ile, d o -w h ile i for, koje se pon ek ad nazivaju i iteracione narcdbe. Naredba se ponav lja sve d o k logiki-izraz za k o n tro lu ne dobije vredn o st false. O blik petlje w h ile je sledei:
w hile (1o g i k i-izraz) naredba

Logiki-izraz se izraunava je d n o m na p o etk u petlje i p o n o v o pre svakog koraka izvravanja naredbe.

Poglavlje 4: Kontrolisanje izvravanja

101

Sledi je d n o stav an p rim e r koji generie sluajne brojeve sve d o k o d red en i uslov ne b u d e ispunjen.
// : control/Prim erZaW hi1e .ja va // Prik az u je p e tlju w h ile. p u b lic c lass PrimerZaWhile { s t a t ic boolean u s lo v () { boolean re z u lta t = Math.random() < 0.99; S y s te m .o u t.p rin tln (re z u lta t + " , " ) ; return r e z u lta t;

}
p ublic s t a t ic void m a in (S trin g [] args) { w h ile (u s lo v () ) Syste m .o u t.p rin tln (''U n u ta r p e t lje ' wh i 1e 1 ) ; S y s te m .o u t.p rin tln ("Iz a a o iz p e t lje ' whi1e ' " ) ;

}
} /* ( Iz v r i t e da b is te v id e li iz la z ) */ // :-

M eto da uslov() koristi sta tin u m eto d u random() iz b iblioteke Math, koja generie v re d n o st tip a double izm e u 0 i 1 (u k lju u ju i 0, ali ne i 1). Prom enljivoj rezultat vredno st daje o p e ra to r p o re en ja <, iji je rezu ltat tip a boolean. U koliko o d ta m p ate vredn o st tip a boolean, au to m atsk i ete d o b iti odgo v araju i znakovni niz tru e ili false. Uslovni izraz za while glasi: Ponavljaj n ared b e u telu petlje sve d o k m e to d a uslov() vraa true.

Petlja do-while
O blik petlje do-w hile je:
do naredba w h ile ( lo g i k i- iz r a z ) ;

ledina razlika izm eu while i do-while jeste ta da se n ared b a u petlji do-while uvek izvri b arem je d n o m , ak i ako izraz im a v red n o st false i prvi p u t. Ako je uslovni izraz netaan pre ulaska u petlju vvhile, n ared b a se nee izvriti n ijed n o m . U praksi, do-while se rede javlja od vvhile.

Petlja for
Petlja for je verovatno najei oblik ponavljanja (ciklusa). O na vri inicijalizaciju pre prvog izvravanja ciklusa. Z atim isp itu je uslov ( logiki-izraz ) i ako je o n isp u n jen , izvrava narcdbu, a p o to m na kraju svakog ciklusa izvrava korak i p o n o v o ispituje logiki-izraz. O b lik petlje for je:
fo r ( i n i c i j a l iz a c ij a ; 1o g i k i- iz ra z ; korak) naredba

102

Misliti na Javi

Bilo koji o d izraza inicijalizacija, logiki-izraz ili korak m oe b iti prazan . Logiki izraz se prov erava p re svakog izvravanja n aredbe. im izraz d obije v red n o st false, petlja se p rek ida i izvravanje se nastavlja p rv o m n a red b o m n ak o n petlje for. Na kraju svakog ciklusa (posle svakog izvravanja n a red b e), izvrava se korak. Petlje for se o b in o k oriste za nabrajanje:
//: co n tro l/:L ista Z n a k o v a .ja v a // Prik az u je p e tlju fo r tako to // is p is u je sva mala ASC II slova. p ub lic c lass ListaZnakova { p ub lic s t a t ic void m a in (S trin g [] args) { f o r ( char c = 0; c < 128; c++) i f (Character.isLow erCase ( c ) ) S y ste m .o u t.p rin tln ("v re d n o st: " + ( in t ) c + " znak: " + c ) ;

}
} /* Is p is : vrednost: 97 znak: a vrednost: 98 znak: b vrednost: 99 znak: c vrednost: 100 znak:: d vrednost: 101 znak: : e vrednost: 102 znak: f vrednost: 103 znak: 9 vrednost: 104 znak: h vrednost: 105 znak: i vrednost: 106 znak: j * '/ / / :-

O b ratite p an ju na to da je p ro m en ljiv a c definisana na m estu gde se koristi, u n u ta r k o n tro ln o g izraza petlje for, u m esto na p oetku m eto d e m a in (). O blast vaenja p ro m e n ljive c je sam o n a red b a koju k o n tro lie petlja for. U p ro g ra m u se koristi i o m o ta k a klasa ja v a .la n g .C h a ra c te r, koju o m o tav a pro st tip c h a r u objekat, a p ru a i d ru g e usluge njena sta tic m eto d a isL ow erC ase() otkriva da li tre n u tn i zn ak p rip a d a m alim slovim a. T radicionalni p ro c ed u raln i jezici kao to je C, zahtevaju da sve prom enljive b u d u definisane na p o etk u bloka, tako da prevodilac, kada pravi blok, m oe da rezervie p ro sto r za njih. U Javi i C + + -u definicije p ro m en ljiv ih m oete da napiete bilo gde u bloku, tam o gde vam treb aju . Z bog toga je stil pisanja p riro d n iji i k o d razum ljiviji.

Veba 1: ( I) N apiite p ro g ra m koji ispisuje brojeve o d 1 do 100. Veba 2: (2) N apiite p ro g ra m koji generie 25 sluajnih v rednosti tip a int. Za svaku v red n o st u p o tre b ite n a re d b u if-else da biste o dredili da li je vea, m anja ili jed n ak a drugoj
sluajno generisan o j v red n o sti.

Veba 3: (1) Izm en ite vebu 2 tako da n jen kod u klopite u b eskonanu petlju vvliile. Program e tad a rad iti sve d o k ga ne p rek in ete s ta statu re (o b in o pritisk o m na C o n tro l-C ).

Poglavlje 4: Kontrolisanje izvravanja

103

Veba 4: (3) N apiite p ro g ram koji p o m o u dve u gnedene petlje for i o p e ra to ra m o d u lo


deljenja (% ) p ro n ala zi i ispisuje p ro ste brojeve (cele brojeve koji su deljivi sam o sa 1 i sam i sa so b o m ).

Veba 5: (4) P onovite vebu 10 iz p re th o d n o g poglavlja, ali tako da za prikazivanje jed inica i n u la u p o tre b ite te rn a rn i o p erato r, te ispitivanje bitova u m esto m e to d e Integer.toBinaryString().

Operator zarez
R anije u ovom poglavlju naveo sam da se operator zarez (n e razdelnik zarez koji slui d a odvoji definicije, kao i arg u m e n te m eto d e) k oristi sam o u k o n tro ln im izrazim a petlje for. Kako p rilik o m inicijalizacije, tako i p ri izvravanju koraka k o n tro ln o g izraza, m oete navesti vie n a red ab a razdvojenih zarezim a i o n e e biti izvrene sekvencijalno. P o m o u o p e ra to ra zarez m oete definisati vie prom enljiv ih u n u ta r n ared b e for, ali o n e m o ra ju b iti istog tipa:
// : c o n tro l/ O peratorZarez.java p u b lic c la s s OperatorZarez { public s t a t ic void m a in (S trin g [] args) { f o r ( i n t i = 1, j = i + 10; i < 5; i++, j = i * 2 ) { S y s te m .o u t.p rin tln ("i = " + i + " j = " + j ) ;

) )
} /* Is p is : i= 1 j= 11 i= 2 j= 4 i= 3 j= 6 i= 4 j= 8 * ///:-

D efinicija c elobrojn ih p ro m en ljiv ih u n aredbi for obuh v ata i i i j. Inicijalizacioni deo m oe im ati vie definicija istog tipa. iM ogunost da se prom en ljiv e definiu u n u ta r kontro ln o g izraza o g ran ien a je sam o na petlje for. O vaj p ristu p ne m oete da koristite ni sa je d n o m d ru g o m n a re d b o m izbora ili petlje. U oite da su, i u delu za inicijalizaciju kao i u d elu koraka, n ared b e izvravane u sekvencijalnom p o retk u .

Foreach sintaksa
Java SE5 uvodi novu i saetiju sintaksu naredbe for koja se u p o trebljava za nizove i kontejnere (o kojim a ete vie saznati u poglavljim a N izovi i Detaljno razm atranje kontejnera). N ju esto nazivaju foreach sintaksa, jer u njoj ne m o rate sam i da pravite celobrojnu p rom enljivu za bro jan je prolaza kroz sekvencu stavki - foreach au to m atsk i pravi sve stavke u m esto vas.

104

Misliti na Javi

Na p rim er, p retp o stav im o da im ate n iz b ro jev a tip a float i d a ho ete da izaberete svaki elem ent to g niza:
//: c o n tro l/Fo rEa ch Flo at.ja va import j a v a . u t i l .* ; public c lass ForEachFloat { public s t a t ic void m a in (S trin g [] args) Random slu cajan = new Random(47); flo a t f [ ] = new f 1o a t[10]; f o r ( in t i = 0; i < 10; i++) f [ i ] = s lu c a ja n .n e x tF 1 o a t(); f o r (f lo a t x : f ) S y s te m .o u t.p rin tln (x );

}
} /* Is p is : 0.72711575 0.39982635 0.5309454 0.0534122 0.16020656 0.57799757 0.18847865 0.4170137 0.51660204 0.73734957

* ///:Niz je n a p u n jen staro m for petljo m , je r m u se m o ra p ristu p a ti p o m o u indeksa. Foreach sintaksu vidite u redu:
fo r (f lo a t x : f ) {

N jim e se definie p ro m en ljiv a x tip a float kojoj se se k v e n ja ln o d o d elju je svaki elem ent niza f. Za korienje foreach sintakse k a n d id at je svaka m e to d a koja vraa niz. P rim era radi, klasa String im a m e to d u toCharArray() koja vraa niz tip a char, pa je lako izdvojiti svaki znak znakovnog niza:
//: co n tro l/Fo rE a ch S trin g .java public c lass ForEachString { public s t a t ic void m a in (S trin g [] args) { fo r(c h a r znak : "A fri k a 1a s t a v ic a " . toCharArray() System .ou t.prin t(zn ak + " " ) ;

}
} /* Is p is : A f r i k a l a s t a v i c a

* ///:-

Poglavlje 4: Kontrolisanje izvravanja

105

Kao to ete videti u poglavlju iivanje objekata, foreach sintaksa ra d i sa svakim obje k to m za koji se zn a p o re d a k n jegovih lan o v a (koji p rip a d a klasi Iterable). M noge for n ared b e o b u h v ataju p ro lazak k ro z sekvencu celo b ro jn ih v re d n o sti, recim o ovako:
f o r ( i n t i = 0; i < 100; i++)

U ovo m sluaju foreach sintaksa n e b i rad ila, sem ukoliko u n a p re d n e n a p ra v ite n iz int brojeva. D a bih to pojen o stav io , u b ib lio tek u net.mindview.util.Range stavio sam m eto d u range() koja au to m atsk i g enerie o d g o v araju i niz. N a m era m i je bila d a se range() ko risti kao static uvoz:
// : co n tro l/Fo rE a ch ln t.ja v a import s t a t ic n e t.m in d v ie w .u til.Range.*; import s t a t ic n e t.m in d v ie w .u til. P r i n t . * ; public c lass ForEachlnt { p u b lic s t a t ic void m a in (S trin g [ ] args) { f o r ( in t i : ra n g e(lO )) // 0 ..9 p rin tn b (i + " ) ; pri n t ( ) ; f o r ( in t i : range(5, 10)) // 5 . . 9 p rin tn b (i + " " ) ; p rin t(); f o r ( in t i : range(5, 20, 3 )) // 5..20 uz korak 3 pri ntnb( i + " " ) ; p rin t();

I
} /* Is p is : 0 1 2 3 4 5 6 7 8 9 5 6 7 8 9 5 8 11 14 17

* ///:M etoda range() bila je preklopljena, to znai da se uz isto im e m eto d e m o g u zadavati razliite liste a rg u m e n a ta (o p rek lap an ju e u skoro biti rei). Prvi prek lo p ljen i oblik m etode range() poinje o d nule i daje brojeve d o v rh a opsega (engl. range), iskljuujui sam v rh . D rugi oblik poinje od prve zadate v red n o sti i daje b ro je v e d o iznosa za jed a n m anjeg od d ru g o g a rg u m en ta, a trei ob lik p rim a k o rak poveanja u svom treem a rg u m e n tu . range() je veom a p ro sta verzija generatora, kao to ete v ideti u nastavku knjige. Im ajte u vidu da range() ee o m oguava korienje foreach sintakse i stoga m oda poveava itljivost koda, ali don ek le sm an ju je efikasnost. Z ato, ako pokuavate da poveate efikasnost svog prog ram a, izm erite njegove p erfo rm an se profajlerom (engl. profiler). M oda ste p rim etili da je u p re th o d n o m p rim e ru , sem m eto d e print(), u p o treb ljen a i printnbf). O n a ne zadaje prelazak u novi red, pa slui za v iek ratn o p isanje u istom redu.

106

Misliti na Javi

Foreach sintaksa tedi neto v rem en a prilik o m pisan ja koda, ali je vanije to to je takav k od m nogo itljiviji - kazuje ta hoete (d o b iti svaki elem en t n iza), a ne kako to postiete (Pravim ovaj indeks d a b ih m ogao izabrati svaki elem en t niza )- U ovoj knjizi e se foreach sintaksa upotrebljavati gde god je to m ogue.

Rezervisana re return
Za bezuslovno skakattje slui nekoliko rezervisanih rei: return, break i continue, a p o sto ji i nain skakanja n a n a re d b u sa o zn ak o m (engl. label), slino n ared b i goto u d ru g im jezicim a. R ezervisana re return im a dve n am ene: o n a o d re u je v re d n o s t k o ju e m eto d a v ratiti (ako uop te vraa v red n o st, tj. ako p o v ratn a v red n o st nije tip a void) i p ro u zro k u je tre n u tan izlazak iz m etode. P re th o d n u m e to d u test() m o em o p o n o v o n ap isati tako d a iskoristim o tu pogo d n o st:
//: c o n tro l/ IfE ls e 2 .ja v a import s t a t ic n e t.m in d v ie w .u til. P r in t . * ; public c lass IfE1se2 { s t a t ic in t t e s t ( in t vrednost, in t c i l j ) if(v re d n o s t > c i l j ) return +1; e ls e if(v re d n o s t < c i l j ) return -1; el se return 0; // Po k lo p ile su se

}
p ublic s t a t ic void m a in (S trin g [] args) { S y s te m .o u t.p rin tln (te s t(1 0 , 5 )) ; S y s te m .o u t.p rin tln (te s t(5 , 1 0 )); S y s te m .o u t.p rin tln (te s t(5 , 5 )) ;

}
} /* Is p is :

1 -1 0 * ///:Sada else nije p o treb n a je r return zavrava rad m etode. U koliko u m eto d i koja vraa void nem ate n are d b u return, zavretak m etode ini im plicitan return, pa n a re d b u return ne m o rate pisati. M e u tim , ako m eto d a vraa bilo koji dru gi tip sem void, va je po sao d a ob ezb ed ite da za sve m o g u e p u ta n je kroz njen kod postoji neka p ov ratn a v red n o st.

Veba 6: (2) Prepravite m eto d e test() iz oba p re th o d n a p ro g ra m a tak o da p rim a ju jo dva a rg u m en ta, poetak i kraj. Za svaku vrednost treb a pro v eriti da li je njen iznos izm eu poetka i kraja (ukijuivo).

Poglav|je 4: Kontrolisanje izvravanja

107

Rezervisane rei break i continue


Tok petlje tako e m oete da k o n tro liete p o m o u rezervisanih rei break i continue u n u ta r tela bilo koje petlje. N aredba break izlazi iz petlje, b ez izvravanja o stalih n a red ab a u n u ta r petlje. N aredb a continue zaustavlja izvravanje teku eg ciklusa i v raa se n a p oetak petlje kako b i p oeo n o v korak. Sledei p ro g ra m pokazuje kako se koriste break i continue u n u ta r petlji for i while:
/ / : co ntrol/BreaklC on tin ue.java // Pokazuje rezervisane rei break i continue. import s t a t ic net.m in dview .uti1 .Range.*; p ublic c lass BreaklContinue { p ub lic s t a t ic void m a in (S trin g [] args) { f o r ( in t i = 0; i < 100; i++) { if (i = = 74) break; // Izai iz p e tlje i f ( i % 9 != 0) continue; // Sledei c ik lu s S y ste m .o u t.p rin t(i + 1 1 ");

)
S y s te m .o u t.p rin tln (); // Upotreba foreach sin tak se: fo r ( in t i : range(lOO)) { if (i = = 74) break; // Izai iz p e tlje i f ( i % 9 != 0) continue; // Sledei c ik lu s S y ste m .o u t.p rin t(i + " " ) ;

}
S y s te m .o u t.p rin tln (); in t i = 0; // "beskonana" p e tlja : w h ile (tru e ) {

i++;
in t j = i * 27; if (j = = 1269) break; // Izai iz p e tlje i f ( i % 10 != 0) continue; // Povratak na vrh p e tlje S y ste m .o u t.p rin t(i + " " ) ;

} }
} /* Is p is : 0 9 18 27 36 45 54 63 72 0 9 18 27 36 45 54 63 72 10 20 30 40

* ///:U petlji fo r v red n o st i nikada ne d oe d o 100 jer n a red b a b re a k p rek in e p etlju kada i p o sta n e 74. N aredba b re a k se na ovaj nain o b in o ko risti satno kada nije u n a p re d p o znato kada e se stvoriti uslov za izlazak. N aredba c o n tin u e nastavlja izvravanje o d v rh a petlje (stoga uveava i) kad god i nije deljivo sa 9. Kada jeste, ta v re d n o st se ispisuje. U dru g o j petlji fo r p rik azan o je kako se u p o treb ljav a foreach sin tak sa i vidi se da o n a daje jed n ak e rezultate.

108

Misliti na Javi

Poslenji d eo prik azu je b e sk o n an u p etlju while, koja b i se, teorijski, izvravala n ep rekidno. M e u tim , u n u ta r p etlje se nalazi n are d b a break p o m o u koje se izlazi iz petlje. Pored toga, p rim etie te d a n a re d b a continue v raa izvravanje na v rh petlje i d a ne izvrava o sta ta k tela petlje n a k o n sebe. (Stoga se ispisivanje u dru go j petlji javlja sam o k ad a je v re d n o st i deljiva sa 10.) N a izlazu se ispisuje 0 je r je v red n o st izraza 0 % 9 jed n ak a 0. D rugi oblik besk o n an e p etlje je for(;;). P revodilac tu m a i while(true) i for(;;) n a isti nain, p a je izb o r iskljuivo p ita n je p ro g ram e rsk o g ukusa.

Veba 7; (1) P repravite vebu 1 tak o d a p ro g ra m zavrava ra d p o m o u rezervisane rei break, k ad a v re d n o st d o e d o 99. Pokuajte d a u m esto toga k oristite return.

uveni goto
R ezervisana re goto p o sto ji u p ro g ram sk im jezicim a o d sam og poetka. Z aista, goto je nastao kao posledica k o n tro le p ro g ra m a u m ain sk o m jeziku: ,A ko je isp u n jen uslov A, skoi tam o , u su p ro tn o m , skoi o v am o . K ad p ro itate asem blerski ko d koji na k raju generie p rak tin o bilo koji p revodilac, p rim etie te d a se k o n tro la p ro g ram a sastoji o d vie skokova. (P revodilac Jave p rav i sop stveni asem blerski k o d , ali njega izvrava v irtueln a m aina Jave (JV M ), a n e hard verski procesor.) N aredba g o to je skok n a niv o u izvornog koda i to ju je iznelo na lo glas. Ako e p ro g ram stalno skakati s je d n e take n a d ru g u , zar ne po stoji nain da se ko d reorganizuje tako da to k k o n tro le n e b u d e toliko p u n skokova? N aredba g o to je pala u p rav u nem ilost n ako n objavljivanja uv en o g lanka E dsgera D ijkstre G oto se sm atra tetnim " i od tada su n ap ad i na nju postali p o p u la ra n sp o rt. Kao to je takvoj situaciji svojstveno, srednji p u t je najbolji. Problem nije u korienju n ared b e g o to, ve u n jen o m preesto m ko rien ju - u retkim situ acijam a g o to je najbolji nain za s tru k tu rira n je to k a izvravanja. Iako je re g o to rezervisana u Javi, o n a se u jeziku ne koristi; Java nem a n ared b u goto. Ipak, o na im a neto to lii na skok, pov ezan o sa rezervisanim reim a b re a k i c o n tin u e . To nije skok, ve vie nain da se izae iz petlje. O n se esto povezuje s n a red b o m g o to zato to koristi isti m eh an izam : o zn ak u (engl. label). O znaka je id en tifik ato r iza koga sledi dvotaka, na p rim er:
oznakal:

U Javi se oznaka k o risti satno n e p o sred n o p re iteracione naredbe. I to znai ba nep osred n o - ne valja stavljati bilo koje d ru g e n ared b e izm eu oznake i petlje. O zn aku treba staviti p re petlje sam o ako u n u ta r te petlje elite da ugnezdite jo jed n u petlju ili n ared bu izbora (sw itch ), o kojoj ete u skoro saznati ta treba. R ezervisane rei b re a k i c o n tin u e o b in o prek id aju sam o tek u u p etlju , ali kada se k oriste sa oznak om , o n e e p rek in u ti sve petlje d o nivoa gde se nalazi oznaka:
oznakal: sp o ljn a - p e tlja { u n u tra n ja - p etlja {

/ /

Poglavlje 4: Kontrolisanje izvravanja

109

break; // (1)

/ /
continue; // (2)

/ /
continue oznakal; // (3)

/ /
break oznakal; // (4)

} } U (1), break p rek id a u n u tra n ju p etlju a vi nastavljate sa spoljanjom . U (2), continue se vraa n a p o eta k u n u tra n je petlje. Ali u (3), continue oznakal prek id a i u n u tra n ju i sp oljanju petlju, sve d o oznakel. Z atim se p etlja nastavlja, ali poev o d sp oljnog ciklusa. U (4), break oznakal tak o e vri p rek id sve d o oznakel, ali n e ulazi p o n o v o u p etlju. U stvari, o n a zaista p rek id a obe petlje. Evo p rim e ra s p e tljo m for:
//: co n tro l/O znacenaPetljaFor.java // P e t lja fo r sa "oznaenom naredbom break" i "oznaenom naredbom contin ue". import s t a t ic n e t.m in d v ie w .u til. P r in t . * ; p u b lic c lass OznacenaPetljaFor { p u b lic s t a t ic void m a in (S trin g [] args) { in t i = 0; s p o lja s n ja : // Ovde ne mogu da sto je naredbe f o r ( ; true ; ) { // beskonana p e tlja unutrasnja: // Ovde ne mogu da sto je naredbe f o r ( ; i < 10; i++) { p r in t("i = " + i ); if(i = 2) { p r i n t ( " n a s t a v i" ) ; conti nue;

}
if (i = = 3) { p ri n t("p re k i n i " ) ; i++; // Inae se i nikad // ne uveava. break;

}
i f (i = = 1) { p r in t("n a s ta v i s p o lja n ju "); i++; // Inae se i nikad // ne uveava. continue sp o lja sn ja ;

}
if (1 - 8) { p r in t("p r e k in i s p o lja n ju "); break sp o lja s n ja ;

110

Misliti na Javi

f o r ( i n t k = 0; k < 5; k++) { if (k = = 3) { p r in t("n a s ta v i u n u tra n ju "); continue u nutrasnja;

} } } }
// Ovde ne moete da pozovete // oznaene naredbe break i continue

}
}/ * Is p is : i =0 n astavi unutranju i = 1 nastavi unutranju i =2 nastavi i =3 preki ni i =4 nastavi unutranju i = 5 nastavi unutranju i = 6 nastavi unutranju i = 7 nastavi spoljanju i =8 p rekini spoljanju

* ///:O b ratite pan ju na to da n are d b a break p rek id a p etlju for i da se izraz za uveavanje ne izvrava sve d o kraja prolaza k ro z p etlju for. Poto break preskae izraz za uveavanje, d o uveanja dolazi sam o kada je i = = 3. N ared b a continue spoljasnja u sluaju kada je i = = 7 takoe skae na v rh petlje i preskae uveavanje, stoga se i o n o vri direktno. Da nem a n ared b e breakspoljasnja, ne bi p o sto jao nain da se n ap u sti spoljanja petlja iz u n u tra n je petlje, p o to sam a n a re d b a break m oe da p rek in e sam o tre n u tn u petlju. (Isto vai i za continue.) Kada n a p u tan je petlje tak o e zn ai izlazak iz m eto d e, m oete koristiti return. Sledi p rim e r o zn aen ih n ared ab a break i continue s p etljam a while:
//: control/O znacenW hile.java // P e t lje vvhile sa "oznaenom naredbom break" i "oznaenom naredbom continue". import s t a t ic n e t.mindvievv.uti 1. P r i n t .* ; public c lass OznacenWhile { public s t a t ic void m a in (S trin g [] args) { in t i = 0;

Poglavlje 4: Kontrolisanje izvravanja

111

spoljasnja: while(true) {

p r i n t ( " S p o lj a n j a p e t l j a w h ile " ) ; w h ile (tr u e ) {

i++;
print("i = 1 1 + i); if(i 1) { printC'nastavi"); continue;

)
i f (1 == 3) { print("nastavi spolja n ju "); continue spoljasnja;

}
if(i == 5) { pri nt("preki n i " ) ; break;

}
if(i ==

7)

p rin t("p re k in i s p o lja n ju "); break s p o lja s n ja ;

} } } }
} / * I s p is : Spoljanja p e t l j a while i = 1 nastavi i = 2 i = 3 nastavi spo lja nju Spoljanja p e t l j a while i = 4 i = 5 preki ni Spoljanja p e t l j a while i = 6 i = 7 p re k in i spoljanju

* ///:Ista prav ila vae i za vvhile:

1 . O b in a n ared b a continue vraa k o n tro lu n a v rh tekue p etlje i nastavlja. 2. O znaen a nared b a continue vraa se na o zn ak u i p o n o v o ulazi u p etlju koja se nalazi o d m a h iza oznake.

3. break izlazi iz petlje. 4. O znaen a nared b a break izlazi iz petlje obeleene zad a to m oznakom .

112

Misliti na Javi

V ano je da zap am tite kako se o zn ak a u Javi jed in o k o risti kad im ate u g n e en e petlje i elite da n ap rav ite break ili continue u vie nivoa u gneivanja. U svom lan k u G o to se sm atra tetn im , D ijkstra je p o se b n o im ao zam e rk i na u p o tre b u oznaka, a ne sam e nared b e goto. P rim etio je da broj b u b ica raste s b ro jem oznaka u p ro g ram u i da oznake i goto oteavaju analizu p ro g ram a. O b ra tite p an ju n a to d a oznake u Javi ne pate o d to g p ro b lem a, p o to je m esto n a k o m e m o g u da stoje o g ran ien o i ne m o g u da se koriste za o p ti p ren o s k ontrole. Treba p rim e titi i d a je ovo sluaj kada neka osobin a jezikai postaje korisnija kada se ogranie njene m o g u n o sti.

Naredba switch
N ared ba sw itc h se n ek ad naziva i naredba izbora. N ared b a sw itc h b ira iz m e u nekoliko delova koda u zavisnosti o d v re d n o sti d ato g izraza. N jen o p ti ob lik je:
switch case case case case case

(izraz) { vrednostl vrednost2 vrednost3 vrednost4 vrednost5

: : : : :

naredba; naredba; naredba; naredba; naredba;

break; break; break; break; break;

/ /
default: naredba;

} Izraz m o ra im ati c elo b ro jn u ili logiku v red n o st. N ared b a switch p o red i rezultat izraza i svake vrednosti. Ako p ro n a e ekv ivalen tnu v red n o st, o d g o v araju a naredba (jed n a ili vie njih, vitiaste zagrade nisu p o tre b n e) bie izvrena. A ko ne p ro n a e ekvivalentnu v red n o st, bie izvrena naredba oznaena kao default. P rim etiete u p reth o d n o j definiciji da se svaki blok case zavrava sa break, im e se to k izvravanja p rem eta na kraj tela nared b e switch. O vo je uob iajen nain fo rm iran ja naredbe switch, ali je break o pcioni. Ako nedostaje, izvravae se sledee case n aredbe sve d o k se ne naie na break. Iako o b in o ne elite da se to deava, isk u sn o m p ro g ram e ru ovaj p ristu p m oe biti koristan . O b ratite panju na to da p o sled n ja nared b a, posle default, nem a break jer se izvravanje nastavlja ba tam o gde bi se io n ak o odvijalo nakon break. M oete, bez ikakvih posledica, da stavite break na kraj bloka default ako sm atra te da je to vano zbog stila. N aredba switch o m oguuje istu realizaciju izbora izm eu vie razliitih puteva izvravanja, ali je za nju p o treb an izraz koji im a v rednost tipova kao to su int ili char. Sa n aredb o m switch ne m o etek ao izrazza izbor koristiti, na p rim er,z n ak o v n i niz ili broj u fo rm atu po k retnog zareza. Za tipove koji nisu celobrojni ili logiki m o ra te da koristite niz naredaba if. Na kraju sledeeg poglavlja, videete da to ogranienje olakava enum, nova rezervisana re koja se u Javi koristi od verzije SE5, jer enum lepo radi sa n a re d b o m switch.

Poglavlje 4 : Kontrolisanje izvravanja

1 13

U n a re d n o m p rim e ru o d re u jem o da li su n a su m i n o o d a b ra n a slova sam oglasnici ili suglasnici:


/ / : c o n tro l/S a m o g la s n ic ilS u g la s n ic i. java / / Prikaz naredbe svvitch. import j a v a . u t i l import s t a t i c . n e t . m i n d v i e w . u t i l . P r i n t . * ; p u b lic class SamoglasnicilSuglasnici { p u b lic s t a t i c void m a in (S trin g [] args) { Random slucajan = new Random(47); f o r ( i n t i = 0; i < 100; i++) { i n t c = s lu c a ja n .n e x tln t(2 6 ) + ' a ' ) ; p r i n t n b (( c h a r )c + " , 1 1+ c + "); switch(c) { case ' a ' : case ' e ' : case ' i 1: case ' o ' : case ' u ' : p rin t (" s a m o g la s n ik " ) ; break; d e f a u lt: p rin t("s u g la s n ik ");

) } }
}/* Ispis: y, 121: suglasnik n, 110: suglasnik z, 122: suglasnik

b, 98:

suglasnik

r , 114: suglasnik
n, 110: suglasnik y, 121: suglasnik g, 103: suglasnik c, 99: suglasnik f, 102: suglasnik o, 111: samoglasnik w, 119: suglasnik z, 122: suglasnik

'/ / / = -

Random.nextInt(26) generie broj izm eu 0 i 26 (26 je r toliko im a slova u engleskoj abecedi), kojem treba d o d ati p o m eraj slova 'a' da bi se d o b ila m ala slova. Z nakovi u p olu n av o d n icim a u nared b am a case tak o e se p retv araju u celo b ro jn e veliine koje se koriste pri po re en ju .

114

Misliti na Javi

O b ra tite pan ju na to d a n are d b e case m o g u biti naslagane je d n e izn ad d ru g ih tak o da se isti deo k oda izvrava u vie sluajeva. V odite rau n a da je n e o p h o d n o d a stavite n a re d b u break na kraju o d re en o g sluaja, inae e p ro g ra m proi dalje i p o eti da izvrava n ared b e pred v i en e za n a re d n i sluaj. U izrazu:
int c = slucajan.nextlnt(26) + 'a';

m eto d a Random.nextInt() daje sluajan ceo bro j izm e u 0 i 25, koji se d o d aje n u m e ri koj (ASCII) vred n o sti slova 'a'. To znai d a se 'a' a u to m atsk i p retv ara u tip int da b i se izvrilo sabiranje. D a bi se pro m en ljiv a c tip a int tam p ala k ao znak, m o ra b iti vraen a u tip char eksplic itn o m konverzijom ; inae b i se ispisivali brojevi.

Veba 8: (2) N apiite n a red b u switch k oja ispisuje p o ru k u za svaki o d sluajeva (case), a switch stavite u n u ta r petlje for koja isprobava svaki sluaj. Stavite n are d b u break n akon svakog sluaja i testirajte p ro g ram , a zatim izbacite nared b e break i v idite ta se deava. Veba9: (2) Fibonaijev n iz c ine b rojevi 1 , 1 ,2, 3, 5, 8 ,1 3 , 21, 34 itd., gde je svaki bro j (o d
treeg nadalje) je d n ak zb iru p re th o d n a dva. N apiite m eto d u koja p rim a ceo bro j k ao arg u m e n t i prikazuje toliko F ibonaijevih brojeva poev o d prvog, n pr. ako p o k ren ete java Fibonacci 5 (gde je Fibonacci im e klase) izlaz e biti: 1, 1, 2, 3, 5.

Veba 10: (5) Vampirski broj im a p a ran broj cifara i dobija se m n o e n je m p ara brojeva
koji im aju p o pola o rig in aln ih cifara proizvoda. Cifre se uzim aju iz p o lazn o g broja p ro izvoljnim redosledom . N isu dozvoljeni parovi zavrnih nula. P rim eri: 1260 = 21 * 60 1827 = 21 * 87 2187 = 27 * 81 N apiite p ro g ra m koji pronalazi sve etvorocifrene v am pirske brojeve. (P redloio D an F orhan.)

Saetak
O vim poglavljem se zavrava prouavanje osnovnih osobina koje se javljaju u veini program skih jezika: raunanje, p rio ritet operatora, eksplicitna konverzija tipova, uslovi i petlje. Sada ste sprem ni za korake koji e vas pribliiti svetu objektno orijentisanog pro g ram iran ja. N aredno poglavlje e ob rad iti vane tem e: inicijalizaciju i ienje objekata te veom a b itn o sakrivanje realizacije.
R eenja n ek ih vebi d ata su u e le k tro n sk o m d o k u m e n tu Thc Thinking in Java Annotalecl Solution

Guide, a m o g u se k u p iti na lo k a ji www.MindVicw.com.

Inicijalizacija i ienje
S napretkom raunarske revolucije, ,,nesigurno program iranje je postalo jeda n od glavnih razloga za visoku cenu programiranja.
D V E BITNE OBLASTI SIGURNOSTI SU INICIJALIZACIJA I IENJE. M N O G E GRESKE U C - U

pojavljuju se kada p ro g ra m e r zaboravi d a inicijalizuje prom enljivu. To n aro ito vai za bibltoteke, kada korisnici ne znaju kako da inicijalizuju k o m p o n e n tu iz biblioteke, ak ne znaju ni d a je to p o treb n o . ienje je poseban p ro b lem je r je lako zaboraviti n a elem ent kada s n jim zavrite - vie vas se ne tie. Z bog toga se resursi koje taj elem ent ko risti zadravaju i lako m oete doi u situaciju d a vam p o n e stan u resursi, i to naroito m em o rija. Jezik C + + je uveo k o n cep t konstruktora, p o seb n e m e to d e koja se a u to m a tsk i p oziv a p ri p rav ljen ju objekta. Java je tak o e usvojila k o n stru k to re , a p o re d toga im a i sakuplja sm ea koji au to m atsk i oslobaa m em o rijsk e resurse kada se n e koriste. U ovom poglavlju ra z m a tra se inicijalizacija i ienje i nain n a koji su p o d r a n i u Javi.

Garantovana inicijalizacija pomou konstruktora


Z am islite da za svaku klasu koju piete, n aprav ite m eto d u p o d im en o m inicijalizacija(). Sam o im e nagovetava da bi ona trebalo da b u d e pozvana pre korienja objekta. N aalost, to znai da k orisnik m o ra da se seti da pozove tu m etod u. P ro jektant klase u Javi m oe da obezbedi garan to v an u inicijalizaciju svakog objekta ako napravi konstruktor (engl. constructor). Ako klasa im a k onstruktor, Java ga au to m atski poziva p ri stvaranju objekta, pre nego to korisnik uopte m oe da m u p ristu p i. Stoga je inicijalizacija zagarantovana. Sledei izazov je kako nazvati tu m eto d u . Tu se javljaju dva p ro b lem a. Prvo: bilo koje im e m oe se p o k lo p iti s nekim d ru g im im en o m , koje elite d a iskoristite za n e k u lanicu klase. D rugo, p o to je prevodilac o d g o v o ran za poziv k o n stru k to ra , o n uvek m o ra da zna koju m e to d u d a pozove. Reenje iz C + + -a deluje najlake i najloginije, p a se tak o e koristi i u Javi: im e k o n stru k to ra jc isto kao i im e ldase. I.ogino je da e takva m eto d a a u to m atski biti pozvana to k o m inicijalizacije. Sledi jed n o stav n a klasa sa k o n stru k to ro m :
/ / : in ic ija liz a c ija / J e d n o s ta v a n K o n s t r u k to r .ja v a / / P rik a ziv an je jednostavnog konstru ktora. class Kamen { Kamen() { / / Ovo j e konstruktor System.out.print("Kamen");

p u b lic class JednostavanKonstruktor { p u b lic s t a t i c void m a in(S trin g [] args) { f o r ( i n t i = 0; i < 10; i++) new Kamen();

116

Misliti na Javi

} /* Ispis:

Kamen Kamen Kamen Kamen Kamen Kamen

Kamen Kamen Kamen Kamen

* ///:Kada se prav i objekat:


new Kamen();

zauzim a se m em o rija i poziva k o n stru k to r. G ara n tu je se da e objek at b iti p rav iln o inicijalizovan p re nego to b u d e u p o treb ljen . O b ra tite p an ju n a to d a se stil p isan ja p o k o m e se prv o slovo im en a svih m eto d a pie m alim slovom ne o d n o si n a k o n stru k to re , je r im e k o n stru k to ra m o ra da se tano pok lapa sa im en o m klase. K o n stru k to r koji ne p rim a a rg u m e n te naziva se po d ra zu m eva n i konstruktor. U veem d elu literature o Javi koju je izdao Sun, n jih nazivaju k o n stru k to ri bez arg u m en ata" (engl. no-arg constructors). T erm in p o d razu m ev an i k o n stru k to r upotrebljava se dugi niz godin a, p a u ga i ja koristiti. Ali kao i svaka m eto d a , k o n stru k to r m oe da im a arg u m e n te koji om o g uav aju d a o d red ite kako e o b jek at biti naprav ljen . P reth o d n i p rim e r lako m oe da se prep rav i tako da k o n stru k to r im a jed an arg u m en t:
/ / : in ic ija liz a c ija / J e d n o s ta v a n K o n s t r u k to r 2 . ja v a / / Konstruktori mogu da imaju argumente. class Kamen2 { Kamen2(int i ) { System.out.print(''Kamen " + i + " ) ;

} }
p u b lic class JednostavanKonstruktor2 { p u blic s t a t i c void m a in ( S tr in g [] args) { f o r ( i n t i = 0 ; i < 8 ; i++) new Kamen2(i);

}
} / * I s p is : Kamen 0 Kamen 1 Kamen 2 Kamen 3 Kamen 4 Kamen 5 Kamen 6 Kamen 7

* ///:A rg u m en ti k o n stru k to ra o m o g u av aju da ob ezb ed ite p aram etre za inicijalizaciju objekta. Na p rim er, ako klasa Drvo im a k o n stru k to r s je d n im celo b ro jn im a rg u m en to m koji o d re u je visinu drveta, o b jek at Drvo n ap rav iete na sledei nain:
Drvo d = new Drvo(12); / / drvo od 12 metara

Ako je Drvo(int) jed in i k o n stru k to r, prevodilac nee dozvoliti da n ap rav ite objekat Drvo ni na koji d ru g i nain.

Poglavlje 5: Inicijalizacija i ienje

117

K on struk tori elim iniu veliku g ru p u p ro b lem a i ine p ro g ram e razum ljivijim . U preth o d n o m p rim e ru ne postoji eksplicitan poziv nekoj m eto d i inicijalizacija() koja je k o n ceptu alno odvojena o d pravljenja objekta. U Javi, pravljenje objekta i njegova inicijalizacija objed injen i su kon cepti - je d n o bez d ru g o g ne ide. K o n stru k to r je n eo b i n a vrsta m eto d e je r n e m a p o v ra tn u v red n o st. O vo je p o tp u n o razliito o d p o v ra tn e v re d n o sti tip a void, kad a m eto d a ne vraa n ita, ali i dalje im ate m o g u n o st d a je n ap ra v ite tako da v raa neto. K o n stru k to ri n em aju p o v ra tn u v re d n o st i d ru g a m o g u n o st ne p o sto ji (izraz n e w v raa referencu n o v onapravljenog objekta, ali sam k o n stru k to r ne vraa n ita). Ako bi p o sto jala p o v ra tn a v red n o st i ako b iste m o g li da je b irate , prevo d ilac bi nekako m o ra o da zna ta d a rad i s to m p o v ra tn o m v red n o u .

Veba 1: (1) N apravite klasu koja sadri n e in i ja liz o v a n u String referencu. Pokaite da Java tu referencu inicijalizuje v red n o u null. Veba 2: (2) N aprav ite klasu s je d n im String p o ljem koje se inicijalizuje n a m estu definisanja i d ru g im koje inicijalizuje k o n stru k to r. Po em u se razlik u ju ta dva pristu p a?

Preklapanje metoda
Jedno od vanih p itan ja u svakom p ro g ram sk o m jeziku jeste u p o tre b a im ena. Pravei objekat, vi zaajete im e oblasti u m em o riji. M etoda je im e neke akcije. Svim o b jek tim a i m eto d am a obraate se prek o im ena. D o b ro o d a b ra n a im en a ine sistem koji je lake razu m eti i m enjati. To veo m a lii na p isanje p ro ze - cilj je da b u d e te u vezi sa itaocim a. P rob lem nastaje kada nijanse ljudskog jezika p rim en ju jete n a p ro g ram sk i jezik. esto ista re im a vie razliitih znaenja - o n a je preklopljena (engl. overloaded). To je korisno, naro ito ako se radi o je d n o stav n im razlikam a. Vi ete rei operi koulju, operi kola i operi psa. Bilo bi b u d alasto ako biste m o rali da g ovorite operiV eM ainom k oulju, operiC rev o m kola i operiK upanjem psa sam o da bi slualac m ogao da n ap rav i razliku izm eu tih akcija. Veina Ijudskih jezika je re d u n d a n tn a , pa i dalje m oete da od red ite znaenje ak i ako ispu stite nekoliko rei. Jedinstveni id en titik a to ri n am n isu p o tre b n i - znaenje m o e m o shvatiti iz konteksta. U veini p ro g ram sk ih jezika (p o seb n o C -u ) svaka m eto d a (koje se u tim jezicim a obino nazivaju funkcije) m o ra im ati jed in stv en identifikator. Tako ne m oete da im ate jed n u funkciju p o d im en o m p rin t() koja bi ispisivala cele brojeve i d ru g u istog im en a za ispisivanje brojeva u fo rm a tu p o k retn o g zareza - svaka funkcija m o ra da im a jed in stv en o im e. U Javi (i C + + -u ) p o sto ji jo je d a n inilac koji n am ee prek lap an je im ena m etoda: konstruk to r. Poto je im e k o n stru k to ra u n a p re d o d re e n o im e n o m klase, k o n stru k to r m oe im ati sa m o to je d n o im e. Ali ta ako elite da objek at p rav ite na vie naina? N a p rim er, p re tp o sta v im o da p rav ite klasu koja m oe da se inicijalizuje na sta n d a rd a n nain ili itanjem info rm ac ija iz dato tek e. P otreb n a su vam dva k o n stru k to ra , jed an bez arg u m en ata (p o ra zu m ev a n i k o n stru k to r) i d ru g i, iji je arg u m e n t tipa String u kom e je im e datoteke iz koje treb a inicijalizovati objekat. O b a su k o n stru k to ri, pa m o raju im ati isto im e - im e te klase. Stoga je prcklapanje m etoda sutinski zn aajn o u Javi, je r o m o g u uje da se isto im e m eto d e koristi s dru g aijim tip o v im a arg u m e n ata . Iako je prek lap an je m eto d a neoph o d n o za k o n stru k to re , to je opta p o g o d n o st d o stu p n a za bilo koju m etodu.

118

Misliti na Javi

N ared n i p rim e r po k azu je p rek lap an je k o n stru k to ra i p rek lap an je m etoda:


/ / : in ic ija liz a c ija /P re k la p a n je .ja v a / / Prikaz preklapanja kon struktora / / i preklapanja obinih metoda. import s t a t i c n e t . m in d v i e w . u t il. P r i n t . * ; class Drvo { i n t v is in a ; Drvo() { print("S a en je m la dic e "); v is in a = 0;

}
D rvo (in t pocetnaVisina) { v is in a = pocetnaVisina; print("P ravim o novo Drvo koje j e visoko " + v is in a + " m");

)
void in f o ( ) { p r in t ( " D r v o j e visoko " + v is in a + " m");

)
void in f o ( S t r i n g s) { p r i n t ( s + ": Drvo j e visoko " + v is in a + " m");

pu b lic class Preklapanje { p u b lic s t a t i c void m a in ( S trin g [] args) { f o r ( i n t i = 0; i < 5; i++) { Drvo d = new D rv o ( i) ; d .in fo (); d . in f o f 'p r e k l o p l j e n a metoda");

}
/ / Prekloptjen k on s tru ktor: new D rv o ();

}
} / * Is p is : Pravimo novo Drvo koje j e Drvo j e visoko 0 m pre klo pljena metoda: Drvo Pravimo novo Drvo koje j e Drvo j e visoko 1 m pre klo pljena metoda: Drvo Pravimo novo Drvo koje j e Drvo j e visoko 2 m pre klo pljena metoda: Drvo Pravimo novo Drvo koje j e visoko 0 m j e visoko 0 m visoko 1 m j e visoko 1 m visoko 2 m j e visoko 2 m visoko 3 m

Poglavlje 5: Inicijalizacija i ienje

119

Drvo je visoko 3 m preklopljena metoda: Drvo je visoko 3 m Pravimo novo Drvo koje je visoko 4 m Drvo je visoko 4 m preklopljena metoda: Drvo je visoko 4 m Saenje mladice

* ///:O bjekat klase Drvo m o e d a b u d e nap rav ljen ili kao m laica, b ez arg um enata, ili kao biljka odgajena u rasad n ik u s d a to m visin om . D a bi se to o m oguilo, postoje dva k o n stru k to ra ; je d an bez arg u m e n a ta i d ru g i, iji je a rg u m e n t postojea visina. M oe vam zatreb ati d a pozovete m e to d u info() n a vie naina. N a p rim er, ako elite da ispiete jo n e k u d o d a tn u p o ru k u , o n d a n a re d ite a rg u m e n t tip a String, a ako ne elite nita vie d a kaete, o n d a v am n e tre b a a rg u m e n a t. Bilo bi n eo b i n o da date dva razliita im ena neem u to oigled n o im a isti kon cep t. N a sreu, p rek lap an je m eto d a om oguava da isto im e koristite za obe stvari.

Razlikovanje preklopljenih metoda


Ako m e to d e im aju isto im e, kako Java zna na k o ju m e to d u mislite? Postoji jed no stavn o pravilo: svaka preklop ljena m e to d a m o ra da im a jed in stv en u listu tipo va argu m en ata. A ko razm islite na tre n u ta k , to im a sm isla: kako b i drugaije p ro g ra m e r m ogao da razlikuje dve m eto d e koje im aju isto im e, ako ne p o tip o v im a n jih o v ih arguinenata? ak je i razlika u p o re tk u a rg u m e n a ta d ovoljna da se dve m eto d e razlikuju (ipak nc treba se oslanjati na ovaj p ristu p je r se oteava odravanje):
/ / : in ic ija liz a c ija / P r e k la p a n je P o r e t k o m . ja v a / / Preklapanje zasnovano na poretku argumenata. import s t a t i c n e t,m in d v ie w .u ti1. P r i n t . * ; pu blic class PreklapanjePoretkom { s t a t i c void f ( S t r i n g s, i n t i ) { p rin t("S trin g : " + s + ", in t: " + i ) ;

}
s t a t i c void f ( i n t i , S tr in g s) { p r i n t ( i n t : " + i + " , S tr in g : " + s );

}
pu b lic s t a t i c void main( S t r i ng[] args) { f("P rv o S t r i ng ", 11); f (99, "Prvo I n t " ) ;

}
} / * I s p is : S tr in g : Prvo S t r in g , i n t : 11 i n t : 99, S trin g : Prvo I n t

} ///= Dve m eto d e f() im aju id en tin e a rg u m e n te ,a li je njihov p o re d a k d ru g a iji i p o to m e se razlikuju.

1 20

Misliti na Javi

Preklapanje i prosti tipovi


Prost tip m oe au to m atsk i da se p ro iri iz m an jeg u vei, to u k o m b in aciji s prek lap an jem m oe d a izazove m alu za b u n u . N ared n i p rim e r p o k azu je ta se deava kada se p ro s t tip prosleuje m etodi:
//: inicijalizacija/PreklapanjeProstihTipova.java // Proirivanje prostih tipova i preklapanje. import static net.mind vi ew .u ti l.Print.*; public class PreklapanjeProstihTipova { void fl(char x) { pr intnb("fl(char)"); } void fl(byte x) { printnb("fl(byte)"); void fl(int x) { pr in tnb("fl(int)"); } void fl(long x) { prin tn b(fl(lon g) "); } void fl(float x) { pr in tnb("fl(float)); } void fl(double x) { printnb("fl(double)"); } } void fl(short x) { printnb("fl(short)"); }

void void void void void void void void void void void

f2 (b y te x) { p r i n t n b ( " f 2 ( b y t e ) " ) ; } f 2 ( s h o r t x) { p r i n t n b ( " f 2 ( s h o r t ) " ) ; } f 2 ( i n t x) { p r i n t n b ( " f 2 ( i n t ) " ) ; } f2(lo ng x) { p r i n t n b ( " f 2 ( l o n g ) " ) ; } f 2 ( f l o a t x) { p r i n t n b ( f 2 ( f l o a t ) " ) ; } f2(double x) { p r in t n b ( " f 2 ( d o u b le ) " ) ; } f3 ( s h o r t x) { p r i n t n b ( " f 3 ( s h o r t ) " ) ; } f 3 ( i n t x) { p r i n t n b ( " f 3 ( i n t ) " ) ; } f 3 ( 1ong x) { p r i n t n b ( " f 3 ( l o n g ) " ) ; } f 3 ( f 1oat x) { p r i n t n b ( " f 3 ( f l o a t ) " ) ; } f3(double x) { p r in t n b ( " f 3 ( d o u b le ) " ) ; }

{ pr in tnb("f4(int) " ) ; } { pr intnb("f4(long) " ) ; } void f4(float x) { printnb("f4(float) " ) ; } void f4(double x) { printnb("f4(double) " ) ; }
void f4(long x)

void f4(int x)

void f 5(1ong x) { p r i n t n b ( " f 5(1on g)" ) ; } void f 5 ( f l o a t x) { p r i n t n b ( " f 5 ( f l o a t ) " ) ; } void f5(double x) { p r i n t n b ( " f 5 ( d o u b le ) " ) ; } void f 6 ( f l o a t x) { p r i n t n b ( " f 6 ( f l o a t ) ) ; } void f6(double x) { p r i n t n b ( " f 6 ( d o u b le ) " ) ; } void f 7 (double x) { p r i n t n b ( " f 7 ( d o u b le ) " ) ; } void testKonstantom() { printnb ("5: " ) ; f 1 ( 5 ) ; f 2 ( 5 ) ; f 3 ( 5 ) ; f 4 ( 5 ) ; f 5 ( 5 ) ; f 6 ( 5 ) ; f 7 (5 ); p r i n t ( ) ;

}
void testChar() { char x = ' x ' ;

Poglavlje 5: Inicijalizacija i ienje

121

printnb("char:

");

fl(x);f2(x);f3 (x);f4(x);f5 (x);f6(x);f7 (x); p r i n t ( ) ;

}
void testByte() byte x = 0; printnb("byte: "); f l ( x ) ;f2 (x);f3(x);f4(x);f5(x);f6(x);f7 (x); p r i n t ( ) ; {

}
void testShort() short x = 0; printnb("short: "); fl (x);f2(x);f3 (x);f4(x);f5 (x);f6(x);f7 (x); pri n t (); {

}
void t e s t l n t O int x = 0; printnbC'int: "); {

f l ( x ) ; f 2 ( x ) ; f 3 ( x ) ; f 4 ( x ) ; f 5 ( x ) ; f 6( x ) ; f 7( x ) ; p r i n t ( ) ;

}
void testLong() { long x = 0; p r in t n b ( " io n g : " ) ; fl(x);f2 (x);f3(x);f4(x);f5 (x);f6(x);f7 (x); p r i n t ( ) ;

}
void t e s t F l o a t( ) { f l o a t x = 0; p rin tn b ("flo a t: "); f l ( x ) ; f 2 ( x ) ; f 3 ( x ) ; f 4 ( x ) ; f 5 ( x ) ; f 6 ( x ) ; f 7( x ) ; p r i n t ( ) ;

}
void testDouble() { double x = 0; printn b ("d o u b le : " ) ; f l ( x ) ; f 2 ( x ) ; f 3 ( x ) ; f 4 ( x ) ; f 5( x ) ; f 6 ( x ) ; f 7( x ) ; p r i n t ( ) ;

}
pu blic s t a t i c void m a in(S trin g [] args) { PreklapanjeProstihTipova p = new PreklapanjeProstihTip ova(); p.testKonstantom (); p .te s t C h a r O ; p.te stB yte (); p.te s tS h o rtO ; p .te s tln t(); p .t e s t L o n g ( ) ; p .te s tF lo a tO ; p.testDoubleO ;

}
} / * Is p is : 5: f l ( i n t ) f 2( i n t ) f 3( i nt ) f 4 ( i n t ) f5(lo ng ) f 6 ( f 1oat) f7(double) char: f l ( c h a r ) f 2 ( i nt ) f 3 ( i n t ) f 4 ( i n t ) f5(lo n g ) f 6 ( f l o a t ) f 7 (double) byte: f 1(byte) f2(byte ) f 3( s h o r t ) f 4 ( i n t ) f 5 ( 1ong) f 6 ( f l o a t ) f7(double) sho rt: f l ( s h o r t ) f2 (s h o rt) f 3 ( s h o r t ) f 4 ( i n t ) f 5 (lo n g ) f 6 ( f l o a t ) f7(double)

122

Misliti na Javi

i n t : f l ( i n t ) f 2 ( i n t ) f 3 ( i n t ) f 4 ( i n t ) f 5 (lo n g ) f 6 ( f 1oat) f7(double) long: f l ( l o n g ) f 2 (lo n g ) f3 (lo n g ) f4 (lo n g ) f 5(1 ong) f 6 ( f l o a t ) f7(double) f l o a t : f l ( f l o a t ) f 2 ( f l o a t ) f 3 ( f l o a t ) f 4 ( f l o a t ) f 5 ( f l o a t ) f 6 ( f 1oat) f7(double) double: fl( d o u b le ) f2(double) f3(double) f4(double) f5(double) f6(double) f7(double)

* ///:P rim etiete d a se k o n sta n ta 5 tre tira kao v red n o st tip a int, pa se, ako postoji, poziva prek lopljen a m eto d a iji je a rg u m e n t tip a int. U svim d ru g im sluajevim a tip p o d a tk a se p ro iru je ako je m an ji o d tip a a rg u m en ta u m eto d i. K od tip a char javlja se neto drugaiji efekat - ako se n e n a e ta n o odgo v araju i p a rn ja k tip a char, o n se p ro iru je n a int. ta se deava ako je a rg u m e n t vei o d a rg u m e n ta koji se oekuje u p reklopljenoj m etodi? O dgo v o r daje izm en jen i p re th o d n i p rim er:
/ / : i n i c i j a l iz a c ija /D e g r a d a c ija .ja v a / / Degradacija p r o s tih tip o va i preklapanje. import s t a t i c n e t . m in d v i e w . u t il. P r i n t . * ; p u b lic class Degradacija {

void void void void void void void void void void void void void void void void void void void void void void

f l( c h a r x) { p r i n t ( " f l ( c h a r ) " ); } fl( b y te x) { p r i n t ( " f 1(b y te )" ); } f l ( s h o r t x) { p r i n t ( " f l ( s h o r t ) " ); } f l ( i n t x) { p r i n t ( " f 1( i n t ) " ) ; } fl(lo n g x) { p r i n t ( " f 1(1ong)" ) ; } f l ( f l o a t x) { p r i n t ( " f l ( f l o a t ) " ) ; } fl(d o u b le x) { p r i n t( " f l ( d o u b le ) " ) ; } f2 (ch ar x) { p r i n t (" f2 (c h a r)" ) ; } f2 (b y te x) { p r in t( " f 2 ( b y te ) " ); } f2 (sh o rt x) { p r i n t ( " f 2 ( s h o r t) " ) ; } f 2 ( in t x) { p r i n t ( " f 2 ( i n t ) " ) ; } f2(long x) { p r in t( " f 2 ( lo n g ) " ) ; } f 2 ( f lo a t x) { p r i n t (" f 2 (f 1o a t) " ) ; } f3 (ch ar x) { p r in t( " f 3 ( c h a r ) " ) ; } f3 (b y te x) { p r i n t ( "f 3 (b y te )" ) ; } f3 (sh o rt x) { p r i n t ( " f 3 ( s h o r t) " ); } f 3 ( in t x) { p r i n t ( " f 3 ( i n t ) "); } f 3 (1ong x) { p r i n t ( "f 3 (1ong)" ); } f4 (ch ar x) { p r i n t ( " f4 (c h a r)" ); } f4 (b y te x) { p r i n t( " f 4 ( b y te ) " ); } f4 (sh o rt x) { p r i n t ( " f 4 ( s h o r t) " ) ; } f 4 ( in t x) { p r i n t ( " f 4 ( i n t ) " ) ; }

void f5 (ch ar x) { p r i n t ( "f 5 (c h a r)" ) ; } void f5(b y te x) { p r i n t ( " f 5 (b y te )" ); } void f5 (sh o rt x) { p r i n t ( " f 5 ( s h o r t) " ) ; }

Poglavlje 5: Inicijalizacija i ienje

12 3

void f6(cha r x) { p r i n t ( " f 6 ( c h a r ) ) ; ) void f6 (b y te x) { p r i n t ( f 6 ( b y t e ) ) ; } void f7(cha r x) { p r i n t ( '' f 7 ( c h a r ) ) ; } void testDouble() { double x = 0; print("argum ent t ip a d o u b le ;"); fl(x );f2 ((flo a t)x );f3 ((lo n g )x );f4 ((in t)x ); f5 ((s h o rt)x );f6 ((b y te )x );f7 ((c h a r)x );

}
pu b lic s t a t i c void m a in (S trin g [] args) { Degradacija p = new Degradacija ( ) ; p.tes tD o ubleO ;

}
} / * I s p is : argument t ip a double: fl( d o u b le ) f2 (flo a t) f 3(1ong) f4 (in t) f5 ( s h o r t ) f 6 ( byte) f7(c ha r)

* ///:U o vom sluaju m etode u zim aju suene v red n o sti p ro sto g tipa. A ko je va a rg u m e n t iri, m o ra te ga eksplicitno k o nv erto vati u p o treb an tip. U koliko to ne u rad ite, prevodilac e prijaviti greku.

Prekiapanje povratnih vrednosti


C esto se uje pitanje: Z ato sam o im ena ldasa i liste arg u m en ata m etoda? Z ato ne b ism o pravili razliku izm edu m etoda na osn ov u n jihovih p o v ra tn ih v rednosti? Na p rim er, ove dve m eto d e, koje im aju isto im e i a rg u m en te, jasno se razlikuju:
void f () { } i n t f ( ) { return 1; }

O vo d o b ro radi kada prevodilac nedvosm isleno m oe d a razlui znaenje iz konteksta, kao u in t x = f(). M etod u , m e d u tim , m oete da pozovete i da ignoriete p o v ra tn u vrednost; to se esto naziva pozivanje m etode zbog njenog sporednog efekta, p o to vam njena p o v ra tn a v red n o st nije b itn a, ve vam treb aju d ru g i efekti poziva m etode. Z ato, ako m eto d u pozovete na sledei nain:
f0;

kako izvrno o k ru en je m oe da o d red i koju m eto d u f() treb a da pozove? I kako bi neko ko ita p ro g ram to m ogao da zna? Z bog ove vrste p ro b lem a, tip p o v ra tn e v red n o sti ne m oe da se koristi za razlikovanje m etoda.

124

Misliti na Javi

Podrazumevani konstruktori
Kao to je ranije p o m e n u to , p o d ra z u m e v a n i k o n stru k to r (tj. n o -a rg k o n stru k to r) n em a arg u m en te i k oristi se za p rav ljen je p o d ra z u m e v a n ih o b jekata. A ko n ap rav ite klasu koja nem a k o n stru k to re, p rev o d ilac e u m esto vas a u to m a tsk i n a p ra v iti p o d ra z u m ev a n i k o n struk to r. Na p rim er:
/ / : in ic ija liz a c ija /P o d r a z u m e v a n iK o n s tr u k to r .ja v a class Ptica {} p u blic class PodrazumevaniKonstruktor { pu b lic s t a t i c void m a in (S tr in g [] args) { Ptica p = new P t i c a ( ) ; / / Podrazumevani!

} } ///:Izraz
new Ptic a()

pravi nov objekat i poziva p o d raz u m ev an i k o n stru k to r, iako o n ranije nije izriito naveden. Bez njega ne bi p ostojala n ijed n a m eto d a koju b ism o m ogli da p o zovem o kako bism o napravili objekat. M e u tim , ako definiete m ak ar jed a n k o n stru k to r te klase (sa arg u m en tim a ili bez n jih), prevodilac nee um esto vas n ap rav iti p o d razu m ev an i k o n stru k to r:
class Ptica2 { Ptica2 ( i n t i ) { } Ptica2 (double d) { }

}
p u blic class NemaSinteze { pu blic s t a t i c void m a in (S tr in g [] args) { / / ! Ptica2 p = new P t ic a 2 ( ) ; / / Nema podrazumevanog konstruktora Ptica2 p2 = new P t i c a 2 ( l ) ; Ptica2 p3 = new P tic a 2 ( 1 . 0 );

1 III-Ako sada napiete: new P t ic a 2 ( ) ; prevodilac e se aliti da ne m o e da n a e od g o v araju i k o n stru k to r. To je kao da niste napravili nijedan k o n stru k to r, p a prev o d ilac kae: M o rate da im ate ncki k o n stru k to r, stoga p ustite m en e da vam n a p ra v im jed an . Ali, ako n ap rav ite bilo kakav k o n stru k to r, prevodilac kae: N apisali ste k o n stru k to r, znai zn ate ta radite; ako niste stavili p o d ra z u m e van, n am ern o ste hteli da ga izostavite." V eba 3: (1) N apravite klasu s p o d ra z u m e v a n im k o n stru k to ro m (o n im koji ne p rim a arg u m en te) koji ispisuje n ek u p o ru k u . N apravite objekat te klase.

Poglavjje 5: Inicijalizacija i ienje

1 25

Veba 4: ( 1) P re th o d n o j vebi d o d a jte p rek lo p ljen i k o n stru k to r koji p rim a je d a n String


a rg u m e n t i ispisuje ga uz vau p o ru k u .

Veba 5: (2) N ap rav ite klasu Pas s p re k lo p ljen o m m e to d o m laje(). M eto d a treb a da je
p rek lo pljena na o snov u razn ih p ro stih tip o v a p o d atak a i d a ispisuje razliite vrste lajanja, zavijanja itd. u zavisnosti o d toga koja je p rek lo p ljen a verzija pozvana. N apiite m e to d u main() koja poziva sve te verzije.

Veba 6: ( 1) P repravite p re th o d n i p ro g ra m tako d a dve o d prek lo p ljen ih m eto d a p rim a ju


po dva ista a rg u m e n ta (razliitih tip o v a), ali o b rn u tim redosledom . U verite se da to radi.

Veba 7: (1) N ap rav ite klasu b ez k o n stru k to ra i zatim u m eto d i main() n ap rav ite objekat
te klase, kako biste se uverili da se p o d ra z u m e v a n i k o n stru k to r a u to m atsk i sintetizuje.

Rezervisana re this
A ko im ate dva objekta istog tipa, koji se zovu a i b, m o d a se p itate kako m oete da p ozovete m e to d u oljusti() za o b a objekta:
//: in ic ija liz a c ija / B a n a n u O l j u s t i . java class Banana { void o l j u s t i ( i n t i ) { / * . . . * / } pu b lic class BananuOljusti { p u b lic s t a t i c void m a in (S trin g [] args) { Banana a = new Banana(), b = new BananaO; a . o l j u s t i (1 ); b . o l j u s t i (2 );

} } ///:Ako postoji sam o je d n a m eto d a p o d im e n o m oIjusti(), kako e o n a zn ati da li je p o zvana za objek at a ili b? Da b iste p ro g ram napisali u p rak ti n o j o b je k tn o o rijen tisan o j sintaksi, p ri em u ,,aljete p o ru k u objektu", prevodilac obavlja izvestan p rik riv en i posao. Postoji skriveni prvi arg u m en t koji se p ro sle uje m eto d i o lju sti() i taj a rg u m en t je referenca n a objek at kojim se rukuje. Dva p re th o d n a poziva m e to d e p o staju otprilike:
B a n a n a . o lju s t i( a , l ) ; B a n a n a . o lju s t i( b , 2 ) ;

O vo su saino in tern i oblici i ako ih napiete, p revodilac ih ne prih v ata, ali vam daje ideju o to m e ta se deava. P retp ostavim o da se nalazite u n u ta r m eto d e i da elite da koristite referencu na tekui objekat. Poto je tu referencu prevodilac skriveno prosledio, za nju ne postoji identifikator. Za tu n a m en u , m e u tim , postoji rezervisana re: this. R ezervisana re this - koja m oe da se koristi sam o u n u ta r ne-static m eto d e - vraa referencu na objekat za koji je m etoda pozvana. Tu referencu m oete da koristite kao i svaku d ru g u referencu na objekat. Vodite

126

Misliti na Javi

rau n a o sledeem: ako pozivate m e to d u svoje klase iz neke d ru g e m e to d e te iste klase, ne treba da koristite this; sam o pozovite m eto d u . Tekua referenca this a u to m atsk i se koristi i za d ru g u m eto d u . Znai, m oete da napiete:
//: i n i c i j a l i z a c i j a / K a j s i ja . j a v a pu blic class K a js ija { void o t r e b i ( ) { / * . . . * / } void k o s tic a () { o t r e b i ( ) ; / * . . . * / }

} ///:U n u tar m eto d e kostica() tnogli biste d a n ap iete this.otrebi(), ali to nije p o tre b n o .1 Prevodilac to au to m atsk i radi u m e sto vas. R ezervisana re this k o risti se sam o kada treb a da izriito k o ristite referencu n a tek u i objekat. Na p rim e r, o n a se esto k o risti uz n a red b e return kada treba da v ratite referencu n a tekui objekat:
/ / : in ic ija liz a c ija /L is ta .ja v a / / Jednostavna upotreba rezervisane re i " t h i s " . public class L is ta { i n t i = 0; Lista p r e l i s t a j O { i++; return t h i s ;

}
void i s p i s ( ) { S y s te m . o u t. p r in t ln (" i = " + i ) ;

}
pu blic s t a t i c void m a in (S trin g [] args) { L ista x = new L i s t a ( ) ; x . p r e l i s t a j ( ) . p r e l i s t a j ( ) . p r e l i s t a j ( ) . i spi s ( ) ;

}
} / * Is p is : i = 3

* ///:Poto m etoda p re lista j() vraa referencu n a tekui o b jek at preko rezervisane rei th is, lako se m oe izvriti vie o p eracija n ad istim o b jek to m . th is se koristi i za prosleivanje tekueg objekta d ru g o j m etodi:
//: in ic ija liz a c ija / P r o s le d iP o m o c u T h is . java

class Osoba { pu blic void pojedi(Jabuka jabuka) {

Ima Ijudi koji kao opsednuti piu this ispred svakog poziva m eto d e i svake reference polja, obrazlaui da je takav ko d jasniji i eksplicitniji. Ne inite to. Z na se zbog ega koristim o jezike visokog nivoa: oni m nogo toga rade um esto nas. Ako budete pisali this kada nije n eo p b o d n o , zbuniete i naljutiti sve koji e itati va program , poto u svem ostalom kodu koji su proitali this nije bio posejan posvuda. Ljudi oekuju da se this upotrebljava sam o o n d a kada je n eo p h o d an . Pridravajui se doslednog i jednostavnog stila program iranje tedite vrem e i novac.

Poglavlje 5: Inicijalizacija i ienje

127

Jabuka o lju s te na = j a b u k a . o l j u s t i S e ( ) ; S y s t e m . o u t . p r i n t ln ( " M lja c " ) ;

} }
class lj u s t a c { s t a t i c Jabuka 1ju s ti(J a b u k a jabuka) { / / . . . ukloni Ijusku re tu rn jabuka; / / Oljustena

} }
class Jabuka { Jabuka o l j u s t i S e ( ) { return 1j u s t a c . l j u s t i ( t h i s ) ; }

}
pu b lic class ProslediPomouThis { p u b lic s t a t i c void m a in (S trin g [] args) { new Osoba() .pojedi (new JabukaO);

}
} / * I s p is : M 1ja c

* ///:-

Jabuka zove Ijustac.ljusti(), sp o ljn u m e to d u koja obavlja o p eraciju koja, iz nekog razloga, m o ra biti spo ljna za objekat Jabuka (m o d a se sp o ljn a m eto d a m oe p rim e n iti na vie razliitih klasa, a vi ne elite da p o navljate n jen ko d ). D a bi se p rosledila spoljnoj m etodi, o n a m o ra da u p o treb i this. Veba 8: (1) N apravite Idasu s dve m eto d e. U n u ta r prve m eto d e, d v ap u t pozovite d ru g u : prvi p u t bez this, a d ru g i p u t p o m o u this - tek da biste videli kako radi; tc ne bi treb alo
da rad ite u praksi.

Pozivanje konstruktora iz konstruktora


Kaa za klasu napiete nekoliko k o n stru k to ra, m o ete pozvati je d a n k o n stru k to r iz d ru gog da biste izbegli p isanje istih n ared ab a vie p u ta . Takav poziv m oete obaviti p o m o u rezervisane rei this. Kada napiete this, o b in o m islite na ovaj o b jek at" o d n o sn o na tekui o b jek at, to sam o po sebi daje referencu na tekui objekat. A ko je u p o tre b ite s listom arg u m en a ta u n u ta r k o n stru k to ra , rezervisana re this im a dru g aije znaenje: o n a eksplicitno poziva p reklopljeni k o n stru k to r sa o d g o v araju o m listo m arg u m en ata. Stoga d ru g e k o n stru k to re m oete sm esta da pozivate:
/ / : in ic ija liz a c ija /C v e t.ja v a / / Pozivanje konstruktora pomou rezervisane rei " t h i s " . import s t a t i c n e t.m in d v ie w .u ti1. P r i n t . * ;

12 8

Misliti na Javi

p u b lic class Cvet { i n t b r o jL a t ic a = 0; S trin g s = "poetni b r o j " ; C v e t( in t l a t i c e ) { b r o jL a t ic a = l a t i c e ; p r i n t ( " K o n s t r u k t o r samo sa argumentom t i p a i n t , bro jL atic a= " + b ro jL a tic a );

)
Cve t(Strin g ss) { p r i n t ( " K o n s t r u k t o r samo sa argumentom t ip a S t r in g , s = " + s s ) ; s = ss;

}
C vet(Strin g s, i n t l a t i c e ) { th is (la tic e ); //! t h i s ( s ) ; / / Ne moete da pozovete dva konstruktora! t h i s . s = s; / / Jo jedna upotreba rezervisane rei " t h i s " print("Argum enti t ip a S trin g i i n t " ) ;

}
Cvet() { t h is ( " z d r a v o " , 47); print("podrazumevani kon stru ktor (bez argumenata)" ) ;

}
void is p is B r o ja L a t ic a ( ) { //! t h i s ( l l ) ; / / Ne moe izvan konstruktora! p r i n t ( " b r o j L a t i c a = " + b r o jL a tic a + " s = "+ s ) ;

}
p u b lic s t a t i c void main ( S t r in g [] args) { Cvet x = new C v e t ( ) ; x .is p is B ro ja L a tic a ();

}
) / * I s p is : Konstruktor samo sa argumentom t i p a i n t , b r o jla t ic a = 47 Argumenti t ip a S tr in g i i n t podrazumevani kon stru ktor (bez argumenata) b r o jL a t ic a = 47 s = zdravo

* ///:K o n stru k to r Cvet(String s, int latice) p o kazuje da p o m o u rezervisane rei this m o ete pozv ati jed an k o n stru k to r ali ne i dva. Takoe, k o n stru k to r m o rate da pozovete pre svih operacija - u su p ro tn o m , bie p rijavljena greka p ri prevoenju. P re th o d n i p rim e r tak o e pokazuje jo jed a n nain u p o tre b e rezervisane rei this. Poto su im en a a rg u m e n ta s i p o d atk a lana s ista, to je dvosm isleno. Reenje je da se p o d a tku la n u o b raate sa this.s. Takav oblik ete esto sretati u p ro g ram im a, kao i na b ro jn im m estim a u ovoj knjizi. U m e to d i ispisBrojaLatica() m o ete videti da prevodilac nee dozvoliti da pozovete k o n stru k to r iz bilo koje d ru g e m eto d e osim iz dru g o g k o n stru k to ra .

Veba 9: (1) N apravite klasu s dva (p reklopljena) k o n stru k to ra . Iz prvog k o n stru k to ra p o m o u this pozovite drugi.

Poglavlje 5: Inicijalizaclja i ienje

129

Znaenje rezervisane rei static


Poto ste u p o z n a li rezervisanu re this, m oete p o tp u n ije da shvatite ta znai k ad a n ek u m e to d u pro glasite statin o m - za tu m e to d u n e po sto ji referenca this. Iz sta ti n ih m e to d a ne m oete da pozovete n e statin e2 m e to d e (iako je o b ra tn o m ogue). S tatin u m e to d u takoe m o ete d a pozovete za sam u klasu, bez objekta. To i jeste o sn o v n a n a m e n a statin e m eto d e. To je kao da p rav ite ekvivalent globalne m eto d e. M e u tim , globaln e m e to d e nisu dozvoljene u Javi. Kada u klasu stavite statin u m eto d u , o n a m oe da p ristu p a d ru g im stati n im m e to d a m a i stati n im poljim a. P ostoji m iljenje da statin e m eto d e nisu o b jek tn o o rijen tisan e p o to im aju se m a n tik u globalne m eto de; u stati n im m eto d a m a vi n e aljete p o ru k u o b jek tu , je r n e p o sto ji referenca this. To jeste ra z u m a n a rg u m e n t i ako p rim e tite da mnogo k o ristite statin e m eto d e, vero v atn o bi treb alo da p o n o v o razm o trite svoj projekat. Ipak, statin e m e to d e i polja su p rag m atin i i p o n e k ad zaista p o tre b n i, p a ra zm atran je jesu li ili n isu isp rav n o o b jek tn o o rije n tisa n i treb a p rep u stiti teoretiarim a.

ienje: finalizacija i sakupljanje smea


P rogram eri su svesni znaaja inicijalizacije, ali esto zaboravljaju na vanost ienja. N aposletku, kom e treba da isti m em o riju koju zauzim a p rom enljiva tipa int? Ali u bibliotekam a, jed n o stav n o ,,naputanje objekta kada s n jim zavrite nije uvek bezbedno. N aravno, Java im a sakuplja sm ea koji oslobaa m em o riju objekata koji se vie ne koriste. R azm otrim o je d a n neobian sluaj. Pretpostavim o da je va objekat zauzeo ,,posebnu m e m o riju bez u p o tre b e op e ra to ra new. Sakuplja sm ea zna sam o kako da oslobodi m e m o riju d o d eljenu pomou nevv i nee znati kako da oslobodi ,,posebnu m em o riju tog objekta. Da bi se reio taj sluaj, u Javi je obezbeena m etoda finalize() koju m oete da definiete u svojoj klasi. Evo kako bi o na trcbalo da radi. Kada je sakuplja sm ea sp rem an da oslobodi p ro sto r koji zauzim a va objekat, prvo e pozvati m eto d u finalize() i tek u d ru g o m prolasku sakupljanja sm ea osloboie i m em o riju objekta. Z nai, ako koristite m e to d u finalize(), o n a vam o m o g u u je da neto vano oistite u trenutku sakupljanja smea. O v de se krije p o ten cijalan izvor greaka, jer neki p ro g ra m e ri, p o seb n o o n i vini C + + u, u p o etk u m og u da p o m eaju m eto d u finalize() s destruktorom u C ++-U , to je fu n k cija koja se uvek poziva kada se objekat unitava. O vde je vano d a n ap rav ite razliku izm e u C + + -a i lave, jer se u C ++-U objekti uvck unitavaju (u p ro g ra m u bez greaka), d o k u Javi sakuplja sm ea ne poisti uvek objekte. D ru g im reim a: 1. M oe se desiti da sakuplja sm ea ne poisti va objekat. 2. Saku pljanje sm ea nije unitavanje. Ako to zap am tite, neete im ati nevolje. To znai sledee: ukoliko im ate neto da u ra dite pre nego to odbacite objekat, to m o rate u ra d iti ru n o . Java nem a d estru k to re niti bilo ta slino, pa m o rate d a n ap rav ite uob iajen u m e to d u koja e o b av iti ienje. Na
To je m ogue sam o kada toj statinoj m etodi prosledite referencu na objekat (i statina m etoda m oe praviti sopstvene objekte). Tada preko te reference (koja je u to m sluaju igra ulogu refe ren ce th is) m oete da pozivate nestatine m etode i da pristupate nestatinim poljim a. Ipak, kada elite neto tako da radite, napravite sam o obinu nestatinn m etodu.

130

Misliti na Javi

p rim er, p retp o stav im o da se to k o m stv aran ja va objekat iscrta n a ekranu. Ako eksplicitn o ne ob riete njegovu sliku sa ek ran a, m o d a o n a n ikad nee biti u klonjena. U koliko m e to d u fin alize() p ro g ra m ira te za n ek u v rs tu b risan ja, a o b jek at do sp e p o d dejstvo sakupljaa sm ea i fin alize() b u d e p o zv an a (a niko ne ja m i d a e se to desiti), u prvoj fazi ienja slika e b iti u k lo n jen a sa ek ran a. U s u p ro tn o m , ako sakuplja sm ea ne rei da poisti va objekat, slika o staje na ekranu. M oe se d o g o d iti d a p ro s to r koji zau zim a va objekat n ik ad ne b u d e o slo b o en , jer vaem p ro g ra m u n ik a d a nee p o n e stati m em o rije. A ko se p ro g ra m zavri, a sakuplja sm ea nije im ao p rilik u d a o slo b o d i p ro s to r koji je bilo koji va objekat zauzim ao, celok u p an p ro sto r e b iti odjednom v raen o p e ra tiv n o m sistem u na izlasku iz p ro g ram a. Tako se izbegava d o d a tn a u p o tre b a rau n a rsk ih resursa - ako ienje nije p o tre b n o p a se p o boljavaju p erfo rm an se p ro g ram a.

emu slui metoda finalize()?


D akle, ako m e to d u finalize() ne treb a d a k o ristite kao m e to d u ienja opte n am ene, em u , o n d a, o n a slui? Trea stv ar koju tre b a d a zap am tite glasi: 3. Sakupljanje sm ea se bavi sam o m em o rijo m . D ru gim reim a, sakuplja sm ea postoji sam o da bi oslobaao m em o riju koju va program vie ne koristi. Z bog toga svaka aktivnost koja je povezana sa sakupljanjem sm ea, p osebno m etod a finalize(), m o ra takoe da se bavi sam o m em o rijo m i n jenim oslobaanjem . D a li to znai da m eto d a finalize() treba eksplicitno da oslobaa objekte ukoliko ih sadri va objekat? Ne, je r o oslo b a an ju m em orije objekata vodi rauna sakuplja sm ea, i to bez o bzira na to kako su objekti napravljeni. M etoda finalize() je korisna sam o u posebnim sluajevim a, kada va objekat zauzim a m em o riju na neki dru g i nain, ne pravljenjem objekata. Ali, kao to znate, u Javi je sve objekat, pa kako je, onda, taj sluaj mogu? Ispostavlja se da m eto d a finalize() p ostoji zbog m o g u n o sti da m em o riju zauzm ete koristei neki m eh an izam slian C -ovom , um esto na u o biajen nain. To se prvenstveno m oe desiti p ri p ozivu lokalnih m etoda (engl. native m ethods) koje slue da iz Jave pozovete neki d ru g i lokalni kod. (L okalne m eto d e se razm atra ju u d o d atk u B u elektronskom 2. izdanju ove knjige, d o stu p n o m na Iokaciji w w w .M indV iew .net.) O d jezika za pisanje lokalnih m e to d a p o d r a n i su sam o C i C + + , ali p o to u njim a m oete da pozovete p o tp ro g ram na n eko m d ru g o m jeziku, zapravo m oete da pozovete bilo ta. U n u tar lokalnog koda, m e m o riju m o ete zauzeti n ek o m funkcijom iz C -ove p o ro d ice funkcija malloc(). Ako ne pozovete i o d g o v araju u funkciju free(), p ro sto r nee biti o sloboden i javie se curenje m em o rije" (engl. m em o ry leak). N aravno, free() je funkcija jezika C i C + + , pa m o rate da je pozovete p rek o neke lokalne m eto d e iz vae m eto d e finalize(). Poto ste sve ovo proitali, v ero v atn o vam je jasno d a neete esto koristiti m eto d u finalize()3. To je zaista tako: o n a nije p rik lad n o m esto na k o m e se radi uobiajeno ienje. A gde bi, o n d a , o n o treb alo d a se radi?
Jo sh u a B loch ide jo dalje u odeljku pod naslovom Izbegavajte korienje zavrnih m etoda": Finalizatori su nepredvidljivi, esto o p a s n i, i po pravilu nepotrebni." Efiknsno programiranje na lavi, s. 18

(M ikro knjiga, 2004).

Poglavlje 5: Inicijalizacija i ienje

131

Morate da istite sami


D a bi po istio objekat, k o risn ik m o ra da pozove m e to d u za ienje u p o g o d n o m tre n u tku . To zvui p rilin o jasno, ali se p o m alo su d ara s k o n cep to m d e stru k to ra u C + + -u . U C + + - U svi objek ti se unitav aju , to jest, svi objekti b i trebalo da b u d u u n iten i. U koliko je o b jek at u C + + -u n ap rav ljen lo k alno (n a p rim e r na steku to nije m o gu e u Javi), u n itav a se k ada se n a i e n a zatv o ren u v itiastu zag radu koja zavrava oblast vaenja objekta. Ako je o b jek at n ap rav ljen p o m o u o p e ra to ra new (kao u Javi), d e stru k to r se p o ziva kad a p ro g ra m e r pozove o p e ra to r delete (koji ne postoji u Javi). Ako p ro g ra m e r na C + + -U zab orav i d a pozove delete, d e stru k to r nee biti n ik ad a pozv an i nastae curenje m e m o rije a p o red toga ni d ru g i delovi o b jekta nee biti poieni. O va v rsta greaka o tk riv a se vrlo teko i je d a n je o d ub ed ljiv ih razloga za p relazak sa C + + -a n a Javu. S u p ro tn o o d toga, Java n e dozvoljava da p rav ite lokalne objekte - uvek m o ra te da koristite o p e ra to r new. M e u tim , u Javi ne p o sto ji o p e ra to r delete koji pozivate d a biste oslo b o d ili objekat, je r sakuplja sm ea to rad i u m esto vas. P ojednostavljeno gledano, m o ete rei d a Java n e m a d e stru k to re zato to im a sakupljae sm ea. Kako ova knjiga o d m ie, videete d a p o stojan je sakupljaa sm ea ne elim inie p o tre b u za d estru k to rim a i n jih o v im korienjem . (A nikada n e treb a d irek tn o da pozivate finalize(), p a to, dakle, nije reenje p ro b lem a.) Ako p o red o slob a an ja m em o rije elite i neko d ru g o ienje, i dalje m o ra te e k sp li tn o da pozovete odgo v araju u m e to d u u Javi koja je ekvivalent des tru k to ra u C + + - U . Z a p a m tite d a se ne g ara n tu je ni sakupljanje sm ea n iti ienje. A ko JV M -u ne p o n estane m em o rije, on (m u d ro ) nee gub iti v rem e na o slo b a anje m em o rije sakupljanjem sm ea.

Stanje okonanja
U glavnom ne m oete da se oslonite na to da e finalize() biti pozvana, ve m o ra te da nap rav ite zasebne funkcije za ienje i da ih eksplicitno pozovete. Z ato se ini da je finalize() je d in o korisna za o slo b a an je m em o rije rezervisane n a neu ob iajen nain, koju veina p ro g ra m e ra nikada nee koristiti. Postoji, m e u tim , je d n a vrlo k o risna p rim e n a m eto d e finalizef) koja se ne oslanja na to da e ona biti svaki p u t pozvana. To je provera p o sto jan ja stanja okonanja' (engl. ten n in a tio n condition ) nekog objekta. O d tre n u tk a kada vas objekat vie ne zan im a - kada je sp rem an da b u d e poien - taj o bjekat treba da b u d e u stan ju u kom e njegova m em o rija m oe b ezb ed no da b u de oslob o en a. Na p rim er, ako objekat predstavlja o tv o ren u d a to tek u , pro g ra m e r treb a da zatvori tu d a to tek u pre nego to sakuplja sm ea poisti taj objekat. A ko bilo koji deo objekta nije p ra v iln o poien, im aete greku u p ro g ram u koju je vrlo teko o tk riti. M etoda fin a lize() je znaajna zato to m oe da se iskoristi za otk riv an je tak vih sluajeva i ako se ne poziva uvek. Ako neka finalizacija o tk rije greku, otkrili ste p ro b lem , to vas, uostalom , jedino i brin e.
' Izraz koji je skovao Bill V enners tokom naeg sem inara.

(w w w.artima.com)

132

Misliti na Javi

Evo jed nostav n o g p rim e ra kako d a to iskoristite:


/ / : in ic ij a li z a c ija / S t a n je O k o n c a n ja . ja v a / / O tkrivanje objekta k o ji n i j e ispravno poien / / pomou metode f i n a l i z e ( ) . class Knjiga { boolean pozajmljena = f a ls e ; Knjiga(boolean p2) { pozajmljena = p2;

}
void v r a t i ( ) { pozajmljena = f a ls e ;

}
protected void f i n a l i z e ( ) { i f (pozajml jena) System .out.println (''Greka: k njig a n i j e vraena"); / / Normalno b is te u r a d i l i i sledee: / / s u p e r . f i n a l i z e O ; / / Poziv v e r z i j e osnovne klase

} }
pu b lic class StanjeOkoncanja { p u b lic s t a t i c void m a in (S trin g [] args) { Knjiga roman = new K n j ig a ( t r u e ) ; / / Pravilno i enje : ro m a n .v ra ti( ) ; / / Ispu sti referencu, zaboravi da p o is t i new K n j ig a ( t r u e ) ; / / Zahtevaj sakuplja nje smea i f i n a l i z a c i j u System.gc();

}
} / * Is p is : Greka: Knjiga n i j e vraena

* ///:Sve pozajm ljene knjige u stan ju o k o n an ja treb alo bi da b u d u v raen e pre nego to ili pok u pi sakuplja sm ea, ali se do g o d i da su m eto d i m a in () p ro g ra m e r grekom ne vrati jednu o d knjiga. Bez m eto d e fin alize() koja proverava stanje o k o n an ja, to bi predstavljalo greku koju je p o ten cijaln o teko o tkriti. O b ra tite p an ju na to da je za zahtevanje ienja korien p oziv S ystem .gc(). ak i da nije, vrlo je verovatno da bi zalutala K n jiga je d n o m bila o tk riv en a u p o n o v ljen im izvrav an jim a p ro g ram a (p o d p retp o stav k o m da p ro g ra m zau zim a d o v o ljn o m em o rije kako bi bio p o k re n u t sakuplja sm ea). Po prav ilu , treb alo bi da p retp o stav ite kako i verzija fin alize() u osnovnoj klasi irna vanog posla i da je pozovete p o m o u rezervisane rei su p e r, kao to vidite u K njiga.finalize(). U ovom sluaju, taj red je pretvoren u k o m e n ta r zato to zahteva o b ra d u izuzetaka, a m i ih jo n ism o razm o trili.

Poglavlje 5: Inicijalizacija i ienje

1 33

Veba 10: (2) N apravite klasu s m e to d o m finalize() koja ispisuje n eku p o ru k u . U m etod i main() n apravite objekat te klase. O b jasn ite p o n aa n je svog p ro g ra m a. Veba 11: (4) Prepravite p re th o d n u vebu tak o d a se vaa m e to d a finalize() uvek poziva. Veba 12: (4) N aprav ite klasu Rezervoar koja se m o e p u n iti i p raz n iti, i koja im a takvo stanje okonanja da m o ra b iti p ra z n a p rilik o m ienja objekta. N apiite m e to d u finalize() koja proverava ispun jen je tog uslova o k o n an ja . U m eto d i main() ispitajte scenarije koji se m o g u o d ig rati p rilik o m u p o tre b e klase Rezervoar.

Kako radi sakuplja smea?


Ako ste preli s p ro g ram sk o g jezika u k o m e je za pravljenje o b jek ta u din am ik o j m em o riji n e o p h o d n o m n o g o procesorske snage, m o ete p retp o stav iti d a je Javin p rin c ip p ravljenja svega (osim p ro stih tipova) u d in am ik o j m em o riji ta k o e zahtevan. M e u tim , ispostavlja se da sakupljanje sm ea m oe znaajn o u ticati n a poveanje b rzin e p ravljenja objekata. To u po etk u zvui p o m alo u d n o - o slo b a an je p ro s to ra u tie n a dodeljivanje p ro sto ra - ali na taj nain rad e neke v irtu eln e m a in e za Javu i to znai da dodeljivanje p ro sto ra u dinam ik o j m em o riji u Javi m o e d a b u d e gotovo je d n ak o b rzo kao odvajanje p ro sto ra na steku u d ru g im p ro g ra m sk im jezicim a. Na prim er, m oete zam isliti da je d in a m i k a m em o rija u C + + -u trav n jak n a kom se svaki objekat pojavljuje kao m ali bu sen. O vaj p o sed m oe kasnije d a b u d e n a p u ten i zatim p o n o v o korien. U n ek im Javinim v irtu e ln im m ain am a, d in am i k a m em o rija m oe da b u de p o tp u n o drugaija; vie kao p o k re tn a trak a koja se p o m e ra u n a p re d svaki p u t kada n aprav ite n o v objekat. To znai d a je d o d eljivanje p ro s to ra veo m a brzo. D inam iki pokaziva" se p o m eri u n a p re d u n e d irn u tu te rito riju , to o d go vara d odeli p ro sto ra sa steka u C + + -u . (N aravno, im a m alo d o d a tn ih tro k o v a za knjigovodstvo, ali to nije ni nalik traen ju m esta za ostavu.) O b ra tite pan ju na to da d in am ik a m em o rija nije p o k re tn a trak a i ako je na taj nain tretirate, ub rzo ete doi u situ aciju da vam se p ro g ra m rairi n a vie m em o rijsk ih stranica - koje se snim aju na isk i vraaju s njega u b rzu m e m o riju , pa e izgleati da im ate vie m em o rije nego to je zapravo sluaj. P ro m en a stran ice zn aajno sm anjuje p erform anse. Na kraju, n akon to n ap rav ite dovoljan broj o b jekata, p o nestae vam m em o rije. Tu na scenu stu p a sakuplja sm ea - d o k radi, o n isto v rem en o sabija sve objekte u dinam ikoj m em o riji im e se dinam iki pokaziva p o m e ra blie p o etk u p o k re tn e trak e i dalje o d kraja stranice. Sakuplja sm ea reo rg an izu je stvari i om og uav a da se za d odeljivanje p ro sto ra koristi m odel brze d in am ik e m e m o rije b esk on an e duin e. Da biste shvatili kako to radi, treba da m alo bolje razu m ete kako rad e sakupljai sm ea (engl. garbage collector, gc). Pri njihovom rad u koristi se jednostavna ali spora tehnika - brojanje referenci. To znai da svaki objekat im a broja referenci i d a se taj broja uvea za jedan svaki p u t kada se objektu prid ru i neka referenca. Kad god referenca izae iz oblasti vaenja ili bude postavljena na vredn o st n u ll, broja referenci se um anji za jedan. P rem a tom e, voenje rauna o brojau referenci je m ali, ali stalan reijski troak koji se obavlja za vrem e trajanja vaeg program a. Sakuplja sm ea proiazi kroz celo k u pn u listu ob jekata i oslobaa m em o riju kada naie na objekat iji je broja referenci nula. (M e utim , u realizacijam a

13 4

Misliti na Javi

brojaa referenci objekat se esto oslobaa im broja p ad n e n a nu lu .) Jena o d m an a ovog p ristu p a je sledea: ako su objekti uzajam n o povezani referencam a, njihovi brojai referenci m o g u biti razliiti od nule, ak i ako ti objekti predstavljaju sm ee. O tkrivanje takvih sam oreferencirajuih grupa znaajan je d o d atn i posao za sakuplja sm ea. Brojanje referenci se esto koristi da objasni jed n u vrstu sakupljanja sm ea, ali se n e p rim en ju je gotovo n i u jednoj stvarnoj virtuelnoj m aini. U b rim realizacijam a, sakupljanje sm ea se n e zasniva n a b ro jan ju referenci. U m esto toga, o n o se zasniva na ideji d a za svaki ivi objek at m oem o n a k raju p ro n ai n e k u referencu koja se nalazi bilo na steku ili u statin o m skladitu. Lanac veza m oe ii kroz nekoliko nivoa objekata. Znai, ako krenete o d steka i statin o g skladita, i p ro ete kroz sve reference koje se tam o nalaze, p ro n ai ete sve ive objekte. Za svaku referencu koju p ro n a ete, m o rate p ristu p iti objektu n a koji o n a ukazuje i zatim analizirati sve reference u totn objektu, dalje ka dru g im o bjektim a itd. P o stu p ak se zavrava kada pro ete kroz celu M reu koja je zapoela to m referencom n a steku ili statin o m skladitu. Svaki objekat kroz koji p ro ete sigurno je iv. O b ratite p an ju n a to da n e m a p ro b lem a sa izdvojenim sam oreferencirajuim grupam a, jer njih na ovaj nain ne nalazite i stoga au tom atski postaju smee. U gore o p isa n o m p ristu p u , JVM k o risti prilagodljiv (engl. adaptive) n ain sakupljanja sm ea. ta posle rad i sa p ro n a e n im ivim o b jek tim a, zavisi o d v arijan te koja se tre n u tn o koristi. Jedna o d tih varijan ata je stani-i-kopiraj (engl. stop--and-copy). To znai da se - iz razloga koji e ubrzo p o stati jasn i - p ro g ra m p rv o zaustavi (ovo nije sakupljanje u pozad in i). Z atim se svaki ivi objekat k o p ira iz jed n o g d in am ik o g m em o rijsk o g p ro sto ra u d ru g i, p ri em u u staro m ostaje sve sm ee. Pored toga, o bjekti se pri k o p ira n ju u n o v i dinam ik i m em orijski p ro sto r slau je d a n za d ru g im , im e se o n sabija (i o m o g u av a da se novi skladini p ro sto r sam o n astavi na kraj, kao to je ran ije o p isan o ). Kada se objekat p o m era s je d n o g m esta na d ru g o , raz u m e se da sve reference koje na njega ukazuju m o raju da b u d u izm enjene. Referenca koja p o tie iz d in am ik o g m e m o rijskog p ro sto ra ili statikog skladita m oe b iti o d m a h p ro m en je n a, ali m o g u p o sto jati i d ru g e reference koje ukazuju na taj objekat n a koje em o naii kasnije. O n e se sre u ju kako se na n jih nailazi (zam islite tabelu koja p revodi stare adrese u nove). Postoje dva inioca koja ine neefikasnim ove takozvane sakupljae s k o p iran jem (engl. copy collectors). Prvi je da im ate dva d in am i k a m em o rijsk a p ro sto ra i da p reb acu jete m e m o riju izm edu ta dva o dvojena p ro sto ra , troei d v o stru k o vie m em o rije nego to vam zaista treba. Neke v irtu e ln e m aine ovo reavaju tako to zau zim aju d in am ik i m em o rijsk i p ro sto r u k o m ad iim a, p o p o treb i, i k o p iraju iz je d n o g k o m ad ia u drugi. D rugo pitanje je sam proces k o piranja. Kada va p ro g ram p o stan e stabilan, pravie m alo ili n im alo sm ea. U prkos to m e, sakuplja s k o p iran jem e i dalje kopirati m em o riju s jed n o g m esta na dru g o , to je gubljenje vrem ena. Da bi to spreile, neke v irtu eln e m aine otkrivaju kada se vie ne pravi sm ee i prelaze na drugaije ienje (ovo je ,,prilagodljiv deo). D rugi p ristu p se naziva oznai-i-poisti (engl. rnark a n d sweep) i njega su starije virtuelne m aine kom panije Sun koristile sve vrem e. Za o p tu u p o tre b u , teh n ik a ozn ai-i-p o isti je prilin o spora, ali kada zn ate da im ate m alo ili nim alo d u b reta, o n a je brza. Pri tehnici oznai-i-poisti k oristi se ista logika polaenja o d steka i statikog skladita i p raen ja svih referenci da bi se p ronali ivi objekti. Svaki p u t kada se naie na ivi objekat, o n se oznai in d ik ato ro m , ali se objekat jo uvek ne sakuplja. ienje se obavlja tek

Poglavlje 5: Inicijalizacija i ienje

1 35

kada se zavri p o stu p ak oznaavanja. T okom ienja o slo b aa se m em o rija koju su zauzim ali m rtv i objekti. M e u tim , n ita se ne k o p ira, p a ako sakuplja odlui d a sabije fragm e n tira n i d in am iki m em orijski prostor, o n to rad i tako to p o m e ra objekte. Tehnika stani-i-kop iraj poiva na ideji da ovaj tip saku pljan ja sm ea ne rad i u p o zad ini, ve se p ro g ram zaustavlja d o k se obavlja sak u p ljan je sm ea. U literatu ri koj u je objavio Sun p ro n ai ete vie referenci na sakuplja sm ea kao p o zad in sk i proces niskog p rio riteta, ali se ispostavilo da u ran ijim verzijam a S unove JVM saku pljanje sm ea nije realizovano n a taj nain. U m esto toga, S unov sakuplja sm ea je zaustavljao p ro g ra m k ad a p o n estan e m em o rije. I teh n ik a o zn ai-ip o isti zahteva zaustavljanje p ro g ram a. Kao to je ranije p o m e n u to , o p isan a v irtu eln a m ain a zau zim a m e m o riju u velikim b lokovim a. A ko napravite veliki objekat, o n d o b ija so pstveni blok. S trik tn a teh n ik a stanii-kopiraj zahteva kop iran je svih ivih objek ata sa izvorinog d in am ik o g m em orijsk og p ro sto ra u novi p re nego to se stari oslobodi, za ta je n e o p h o d n o p u n o m em orije. Sakuplja sm ea to k o m sakupljanja o bino m oe d a k o p ira objekte u m rtve blokove. Svaki blo k im a brojaproizvodnje koji vodi evidenciju d a li je b lo k iv. O b i n o se sabijanje vri sam o n ad b lokovim a koji su n apravljeni posle posled n jeg sak up ljan ja sm ea; svim ostalim blok ov im a b roja p roizvodnje se uveava ako su o d n e k u d referencirani. N a ovaj n ain se b rin e o uobiajenim sluajevim a p riv re m e n ih o b jekata k ratk o g ivotnog veka. P erio din o se radi p o tp u n o ienje - veliki o bjekti se i dalje ne k o p iraju (ve im se sam o uvea broja proizvodnje), a blokovi koji sadre m ale o b jek te k o p iraju se i slau. JVM p rati efikasnost sakupljaa sm ea i ako se za njegovo korienje n e o p h o d n o troi vrem e jer su svi objekti dugog ivotnog veka, preb acuje ga u reim o zn ai-i-p oisti. Slino tom e, JVM vodi rau n a o u spenosti tehnike o zn ai-i-po isti, i ako d in am i k i m em o rijsk i p ro sto r p o stan e fragm en tiran , prebacuje se nazad u reim stan i-i-k o p iraj. P reth o d n a m eto d a prebacivanja predstavlja p o m e n u ti prilagodljivi deo, tj. p o sto ji teh n ik a prilagodljiva stani-i-k o p iraj oznai-i-poisti. Postoji vie d o d atn ih m o g u n o sti za ub rzav an je v irtu e ln e m aine. Posebno vana m o gunost o n o si se na rad p ro g ram a za uitavanje (engl. loader) i o n og a to se naziva p revodilac b a-k ad a-treb a (engl. Just-In-Time, JIT). JIT p revodilac delim in o ili p o tp u n o pretvara p ro g ram u lokalni m ainski kod, tako da JVM ne m o ra da ga in terp re tira, pa je izvravanje m n o g o bre. Kada klasa m o ra da se u ita (o b ino , prvi p u t kada elite da napravite objekat te klase), pronalazi se d atoteka .class i b ajtk od te klase se uitava u m em o riju. U to m tre n u tk u jedan p ristu p m oe biti da p rim e n im o JIT p rev o enje na sav k od , ali to im a dve m ane. Prvo, neto vie traje (to, kada se n ak u p i kroz ivotni vek p ro g ram a , ne m oe da b u d e zanem arljivo). D rugo, poveava se izvrni p ro g ram (bajtk od je z n a tn o k o m p ak tniji od raspakovanog JIT koda), pa m o g u d a se izm en e m em o rijsk e stranice, to defin itiv n o u sporava program . A lternativni p ristu p je lenja p ro cen a (engl. lazyevaluation), to znai da se II I prevoenje ne radi sve d o k nije n e o p h o d n o . T im e se postie da kod koji se nikad a ne izvri, m oda nikada ne b ud e JIT p reveden. T ehnologije Java H o tS p o t u n ovijim razvojnim o k ru en jim a (JDK) p o stu p aju slino, jer o p tim iz u ju svaki deo koda svaki p u t kada se on izvrava - to se kod vie izvrava, p o staje sve bri.

136

Misliti na Javi

Inicijalizacija lanica
fava se ba tru d i d a g a ra n tu je k ako e sve p ro m en ljiv e biti p ra v iln o in i jalizo v an e p re korienja. Kad su p ro m en ljiv e definisane lo kaln o u m eto d i, ovo g a ra n tu je prevodilac (jer inae prijavljuje grek u ). Z nai, ako napiete:
void f() { int i ; i++; // Greka -- i nije inicijalizovana

dob iete p o ru k u o greci s n a p o m e n o m d a p ro m en ljiv a i m o d a nije in i jalizo v an a. N aravno, prev o d ilac je m o g ao d a d o d eli p rom en ljiv o j i p o d ra z u m e v a n u v red n o st, ali je neinicijalizovana lo k aln a p ro m en ljiv a v ero v atn o greka p ro g ra m e ra , a p o d ra zu m ev an a v re d n o st b i to p rik rila . P risiljav an jem p ro g ra m e ra d a o b ezb ed i v red n o sti za inicijalizaciju , poveava se v ero v atn o a o tk riv a n ja greaka. A ko je p ro st tip p o lje neke klase, stvari su neto drugaije. Kao to ste vieli u poglavlju Sve je objekat, svako polje p ro sto g tip a u svakoj klasi g aran to v an o e d o b iti inicijalnu vred n o st. Evo p rim e ra koji to d o k azuje i tih vrednosti:
/ / : i n i c i j a l i z a c i j a / I n i c i j a l n e V r e d n o s t i . java / / Prikazuje podrazumevane i n i c i j a l n e vred no sti. import s t a t i c n e t . m in d v i e w . u t il. P r i n t . * ; p u b lic class I n ic ija ln e V r e d n o s t i { boolean t ; char c; byte b; short s ; i nt i ; 1ong 1; flo a t f ; double d; I n i cija ln e V re d n o sti reference ; void i s p i s l n i c i j a l n e V r e d n o s t i () { p r i n t ( " T i p podatka I n i c i j a l n a vrednost"); " + t); p r i n t ( boolean print("char [" + c + "] " ) ; prin t("b y te " + b); print("short " + s) print("int " + i)

pri nt("1ong
p rin t("flo a t

" + 1)
+ f)
" + d);

print("double printC'reference

" + reference);

p u b lic s t a t i c void m a in ( S trin g [] args) { I n ic ija ln e V r e d n o s t i iv = new I n ic i ja l n e V r e d n o s t i( ) ;

Poglavfje 5: lnic[jalizacija i ienje

137

iv.ispislnicijalneVrednosti(); /* U ovom sluaju takoe moete napisati: new InicijalneVrednosti().ispislnicijalneVrednosti(); */ }


} / * I s p is : Tip podatka boolean char byte short in t I n i c i j a l n a vrednost fa ls e [ 1 0 0 0 0 0.0 0.0 null

long
flo a t

double
reference

* I I I,V idite d a se v re n o sti, iako nisu n avedene, au to m a tsk i inicijalizuju (p o d raz u m ev an a vredn o st za tip c h a r je nula, to se ispisuje kao razm ak.). Stoga je b arem elim inisan a opasnost o d rad a s neinicijalizovanim pro m en ljiv am a. Kada defini.ete referencu na objekat u n u ta r neke klase, a ne inicijalizujete je, ta referenca d o b ija p o se b n u v red n o st null.

Zadavanje inicijalizacije
ta se deava ako elite da p rom enljivoj d odelite p o e tn u vrednost? D irek tan n ain d a to urad ite jeste da joj d o d elite v red n o st na m estu gde tu p rom enljivu definiete u klasi. (To nije m o gue u C + + -u , iako poetn ici u jeziku C + + to uvek pokuavaju.) U sledeem p rim eru definicije polja u klasi In ic ija ln e V r e d n o s t i p ro m en jen e su da bi obezbedile p o etne v red n osti:
/ / : i n i c i j a l i z a c i j a / I n ic ija ln e V r e d n o s t i 2 . ja v a / / E k s p lic itn o zadavanje i n i c i j a l n i h v r e d n o s ti. p u b lic class In ic ija ln e V re d n o s ti2 { boolean b = tru e ; char c = ' x ' ; byte B = 47; short s = Oxff ; i n t i = 999; long 1 = 1 ; f l o a t f = 3 .1 4 f; double d = 3.14159;

III--

1 38

Misliti na Javi

O bjekte m o ete inicijalizovati n a isti nain . A ko je definisan a klasa D u b in a , definiite p ro m en ljiv u i in i ja liz u jte je n a sledei nain: / / : in ic ija liz a c ija /M e r a .ja v a c la s s Dubina {} pu b lic c la s s Mera { Dubina d = new Dubina ( ) ; / / 1 ///:Ako prom enljivoj d n iste dali p o e tn u v red n o st, a ipak p ok u ate da je koristite. d obiete greku p ri izvravanju koja se u Javi naziva izuzetak (engl. exception), to je o b ra eno u poglavlju O brada greaka potnou izuzetaka. M oete ak d a pozovete m e to d u d a b iste obezb edili v re d n o st za inicijalizaciju: / / : in ic ija liz a c ija /In ic ija liz a c ija M e to d o m .ja v a p ublic c la s s In icijalizacija M e to d o m { in t i = f () ; in t f( ) { re tu rn 11; }; } ///:N aravno, ta m eto d a m o e im ati a rg u m en te , ali ti arg u m e n ti ne m og u biti ostali lanovi klase koji jo nisu inicijalizovani. Stoga ovo m oete d a u rad ite: / / : i n i c i j a l iz a c ij a / I n i c i j a l iz a c ijaMetodom2.java p u blic c la s s In icijalizacijaM eto d o m 2 { in t i = f (); in t j = g ( i ) ; in t f ( ) { re tu rn 11; } irit g (in t n) { re tu rn n * 10; }

III--

Ali ovo ne m oete d a urad ite: / / : in ic ija liz a c ija /I n ic ija liz a c ija M e to d o m 3 . java p ublic c la s s I n i c ij a l izacijaMetodom3 { / / ! in t j = g ( i ) ; / / Nedozvoljeno re f e re n c ir a n je unapred in t i = f (); in t f ( ) { re tu rn 11; } in t g ( in t n) { re tu rn n * 10; }

1 III-Na ovom m estu se p revodilac, sasvim o p rav d an o , bu ni zbog referenciranja u nap red . Razlog lei u redosledu inicijalizacije (p ro m en ljiv a i se koristi pre nego to je definisana), a ne u nain u na koji se p ro g ra m prevodi. O vakav p ristu p inicijalizaciji je d n o stav an je i d irek tan . O granien je tim e to svnki objekat tip a In ic ija ln e V re d n o sti im a iste inicijalizacione v red n o sti. Ponekad vam ba. to treba, ali dru gi p u t e v am zatreb ati vie fleksibilnosti.

Poglavlje 5: Inicljalizacija i ienje

1 39

Inic/jalizacija konstruktorima
K o n stru k to r m oe d a se k oristi za inicijalizaciju. To daje veu fleksibilnost u p ro g ra m ira n ju , je r m o ete da pozivate m e to d e i izvravate akcije to k o m rad a p ro g ra m a kako biste od red ili p o etn e v red n o sti. Jenu stvar treb a im ati na u m u : tim e ne spreavate a u to m a tsku inicijalizaciju koja se odigrava p re nego to se u e u k o n stru k to r. Stoga, ako biste, na p rim er, napisali:
/ / : in ic ija liz a c ija /B ro ja c .ja v a p u b lic class Brojac { in t i ; Brojac() { i = 7; }

/ /

III--

p ro m en ljiv a i bi p rv o bila inicijalizovana v red n o u 0, a zatim vred n o u 7. O vo vai za sve p ro ste tipove i reference n a objekte, u k lju u ju i i o n e koji su eksplicitno inicijalizovani na m e stu definicije. Prevodilac zato n e p ok uav a da vas p risili d a inicijalizujete elem ente na bilo k o m m estu u k o n stru k to ru ili p re nego to ih k o ristite - inicijalizacija je ve sigurno obavljena.

Redosled inicijalizacije
Redosled inicijalizacije u n u ta r klase o d re en je red o sled o m kojim su p rom enljive definisane. D efinicije pro m en ljiv ih m ogu biti razbacane u n u ta r i izm e u definicija m eto d a, ali se p rom enljive inicijalizuju pre poziva bilo koje m eto d e - ak i k o n stru k to ra . Na p rim er:
/ / : i n i c i ja liz a c ija /R e d o s le d ln ic ija liz a c ije .ja v a / / Pokazuje redosled i n i c i j a l i z a c i j e . import s t a t i c n e t.m in d v ie w .u ti1. P r i n t . * ; / / Kada se pozove kon stru k tor za p ra v lje n je / / objekta klase Prozor, videete poruku: class Prozor { P ro z o r(in t marker) { P ro z o r(in t marker) { p r i n t ( " P r o z o r ( " + marker + " ) " ) ;

f
class Kuca { Prozor pl = new P r o z o r ( l) ; / / Pre konstruktora Kuca() { / / Ukazuje da smo unutar konstruktora p r i n t ( 'KucaO" ) ; p3 = new Prozor(33); / / Ponovo i n i c i j a l i z u j e p3

}
Prozor p2 = new Prozor(2); / / Nakon konstruktora void f () { p r i n t ( " f ( ) " ) ; } Prozor p3 = new Prozor(3); / / Na kraju

140

Misliti na Javi

pu blic class R e d o s le d ln ic ija li z a c ij e { pu blic s t a t i c void m a in (S trin g [] args) { Kuca k = new Kuca(); k . f ( ) ; / / Prikazuje da j e zavreno p r a v lje n je

}
} / * Is p is : Prozor(l) Prozor(2) Prozor(3) Kuca() Prozor(33) f() * ///:-

U klasi Kuca, definicije objekata klase Prozor n a m e rn o su razb acan e d a bi se dokazalo kako e sve biti inicijalizovane p re ulaska u k o n stru k to r. P o red toga, p3 se p o n o v o inicijalizuje u n u ta r k o n stru k to ra . Iz izlaza vidite d a se referenca p 3 inicijalizuje d v ap u t, je d n o m p re i je d n o m za v rem e poziva k o n stru k to ra. (Prvi objekat e biti n ap u te n i kasnije sak u p ljen kao sm ee.) Isprva ovo m oe da izgleda neefikasno, ali g aran tu je p rav iln u inicijalizaciju. Sta bi se desilo ako bi bio definisan preklopljeni k o n stru k to r koji nc inicijalizuje p3, a ,,p o d razu m ev an a inicijalizacija za p3 pri njenoj efiniciji nije zadata?

Inicijalizacija statinih elemenata


Za statine podatke postoji je in stv en o skladite, bez o b zira na to koliko je o bjekata napravljeno. R ezervisanu re sta tic ne m oete p rim e n iti na lokalne pro m en ljiv e, pa se ona p rim en ju je sam o na polja. I kada je statin o polje p ro sto g tip a, a vi ga ne inicijalizujete, o n o e dobiti sta n d a rd n u v red n o st. R eferenca na objek at d obija p o d ra z u m e v a n u p o etn u vred nost null. Ako inicijalizaciju elite da izvrite na m estu definicije, to e izgledati isto kao i kod nestatinih podataka. Evo p rim e ra iz kojega ete videti u kom tre n u tk u se inicijalizuje sta ti n o skladite:
/ / : in ic ija l iz a c ija /S ta tic k a ln ic ija liz a c ija .ja v a / / Zadavanje poetnih vrednosti u d e f i n i c i j i klase. import s t a t i c n e t.m in d v ie w .u ti1. P r i n t . * ; class C in ija { C i n i j a ( i n t marker) { p r i n t ( " C i n i j a ( " + marker + " ) " ) ;

}
void f l ( i n t marker) { p r i n t ( " f l ( " + marker + " ) " ) ;

} }

Poglavlje 5: Inicijalizacija i ienje

141

class Sto { s t a t i c C i n ij a c i n i j a l = new C i n i j a ( l ) ; S to() { p rin t("S to () ) ; c in ija 2 .f(l);

}
void f 2 ( i n t marker) { p r i n t ( " f 2 ( " + marker + " ) " ) ;

}
s t a t i c C i n ij a c in i j a 2 = new C i n i j a (2 );

class Kredenac { C i n ij a c in i j a 3 = new C i n i j a ( 3 ) ; s t a t i c C i n ij a c i n i j a 4 = new C i n i j a ( 4 ) ; Kredenac() { p r i n t (" K r e d e n a c ( )" ); c in ija 4 .fl(2 );

}
void f 3 ( i n t marker) { p r i n t ( " f 3 ( " + marker + " ) " ) ;

}
s t a t i c C in ija ci n i j a 5 = new C i n i j a ( 5 ) ;

}
pu b lic class S t a t i c k a l n i c i j a l i z a c i j a { pu b lic s t a t i c void m a in (S trin g [] args) { p r i n t ( " P r a v l je n je novog objekta klase Kredenac() u metodi main"); new KredenacO; p r i n t ( " P ra vlje n je novog objekta klase Kredenac() u metodi main " ) ; new Kredenac( ) ; s t o . f 2 ( 1) ; k r e d e n a c . f3 ( l) ;

}
s t a t i c Sto sto = new S to () ; s t a t i c Kredenac kredenac = new Kredenac(); } / * I s p is : C in ija (l) Cini j a (2) Sto() fl(l) Ci ni j a(4) C in ija ( 5 ) Cini ja (3) Kredenac() f 1 (2) P r a v lje n je novog objekta klase Kredenac() u metodi main C in ija ( 3 ) KredenacO

142

Misliti na Javi

fl(2 ) P ra v lje n je novog objekta klase Kredenac() u metodi main C in ija ( 3 ) KredenacO f 1 (2) f 2 (1) f 3 (1)

* ///:Klasa Cinija om oguava da p ra tite pravljenje objekata, a klase Sto i Kredenac na vie m esta u n u ta r definicija im aju statin e lanove klase Cinija. O b ra tite p a n ju na to da Kredenac pre statinih definicija p rav i nestatian objekat Cinija cinija3. Iz rezu ltata p re th o d n o g p ro g ra m a v idite d a se inicijalizacija statin ih elem en ata javlja sam o ako je n e o p h o d n a. A ko n e n a p rav ite objekat klase Sto i ako se n ik ad a ne o b ratite m e to d a m a Sto.cinijal ili Sto.cinija2, statin i objekti Cinija cinijal i cinija2 nik ad a nee b iti n apravljeni. O n i se inicijalizuju sam o kada se p rav i prvi o bjek at klase Sto (ili kada se p rv i p u t p ristu p a statin o m p o d a tk u ). N akon toga, vie se ne obavlja p o n o v n a inicijalizacija statin ih objekata. Prvo se inicijalizuju statin i elem en ti, ako to ve nije u in jen o , a zatim nestatin i. D okaz za to vidite u rezu ltatu p re th o d n o g p ro g ram a . D a b i se izvrila statin a m e to d a m ain( ), m o ra b iti uitan a klasa Staticlnitialization, a zatim se inicijalizuju n jen a statin a polja sto i kredenac, to p ro u zro k u je uitavanje tih klasa; p o to o b e te klase sadre statin e o b jekte Cinija, zatim se uitava Cinija. Tako se sve klase ovog p ro g ra m a uitavaju p re poetka izvravanja m etode main( ). To najee nije sluaj, p o to u tip i n o m p ro g ram u sve nee b iti povezano statin im e lem en tim a kao u ovom p rim eru . Da b ism o napravili saetak procesa pravljenja objekta, p o sm a tra jm o klasu p o d im en o m Pas: 1. Iako se u njoj rezervisana re sta tic ne upotrebljava eksplicitno, k o n stru k to r jeste statin a m eto d a. Stoga, p ri p rv o m pravljenju objekta tip a Pas ili kada se prvi p u t p ristu p a statikoj m eto d i ili statik o m polju klase Pas, Javin in te rp re te r m o ra da p ro n a e d ato tek u Pas.class, to ini pretraivanjem p u ta n je klasa (engl. classpath). 2 . Pri uitavanju P as.class (pri em u se pravi objekat klase C lass, o em u e kasnije biti rei) pokree se inicijalizacija svih statin ih elem enata. Z nai, o n i se inicijalizuju sam o jed an p u t, kada se odgovarajui objekat klase C lass uitava p o prvi put. 3. Kada napravite objekat o p eracijo m n ew Pas(), proces kon stru k cije objekta nalae da se prvo zauzm e do v o ljn o skladinog p ro sto ra u d in am i k o m m em o rijsk o m p ro sto ru . 4 . Taj p ro sto r se p o p u n i n u lam a, im e se au to m atsk i zadaje p o d ra zu m e v a n a v red n o st svih pro m en ljiv ih pro sto g tipa u to m o bjektu Pas (n u la za brojeve i njen ekvivalent za b o o le a n i c h a r), a sve reference d obijaju vre n o st nu ll. 5. Izvravaju se sve inicijalizacije koje se javljaju na m estu definicije polja. 6. Izvravaju se k o n stru k to ri. Kao to ete videti u poglavlju Ponovno korienjc klasa , to m oe da b u d e veliki posao, n aro ito p ri nasledivanju.

Poglavlje 5: Inidjalizacija i ienje

143

Eksplicitna inicijalizacija statinih elemenata


Java om o guava da statike inicijalizacije u n u ta r klase grup iete p o seb n im statikim od red bam a" (koje se p o n ek ad nazivaju i statiki blokovi). To ovako izgleda:
//in ic ija liz a c ija /K a s ik a .ja v a p u b lic class Kasika { s ta tic in t i ; s ta tic { i = 47;

} } ///= O vo podsea na m eto d u , ali je to sam o rezervisana re sta tic posle koje sledi b lo k koda. Ovaj k o d se, kao i d ru g e inicijalizacije sta ti n ih elem enata, izvrava sam o je d n o m , kada prvi p u t p rav ite objekat te klase ili kada p rv i p u t p ristu p a te stati n o m lan u klase (ak i ako n ik ad a n e n ap rav ite o b jek at te klase). N a p rim e r:
/ / : in ic ija liz a c ija /Iz ric ito S ta tic k a .ja v a / / E k s p lic itn a i n i c i j a l i z a c i j a s t a t i n i h elemenata preko stati n og bloka. import s t a t i c n e t . m in d v i e w . u t il. P r i n t . * ; class S o ljic a { S o l j i c a ( i n t marker) { p rin tC 'S o l ji c a C ' + marker + " ) " ) ;

}
void f ( i n t marker) { p r i n t ( " f ( " + marker + " ) " ) ;

class S o l jic e { s t a t i c S o ljic a s o l j i c a l ; s t a t i c S o ljic a s o ljic a 2 ; s ta tic { s o l j i c a l = new S o l j i c a ( l ) ; s o l jic a 2 = new S o l jic a ( 2 ) ;

}
S o l jic e ( ) { p rin t("S o lj i c e ( ) " ) ;

p u b lic class I z r i c i toS tatic ka { p u b lic s t a t i c void m a in (S trin g [] args) { p rin t ( " U n u t a r metode m a in () "); S o l j i c e . s o l j i c a l . f (9 9 ) ; / / (1)

}
/ / s t a t i c S o l jic e s o l j i c e l = new S o l j i c e ( ) ; / / s t a t i c S o ljic e s o ljic e 2 = new S o l j i c e ( ) ; / / (2) / / (2)

144

Misliti na Javi

} / * I s p is : Unutar metode main() S o ljic a (l) S o ljic a ( 2 ) f (99)

* ///:Statini elem en ti klase S oljice in icijalizu ju se bilo da se sta ti n o m o b jek tu s o ljic a l p ristu p a u red u o zn a en o m sa (1), b ilo d a je red (1) k o m en ta risan a iz redova o zn aen ih sa (2) uk lo n jen k o m en tar. A ko su i (1) i (2) k o m en tarisan i, statin i elem en ti nee b iti inicijalizovani, kao to v id ite iz rezu ltata p ro g ra m a . Takoe nije b itn o da li je k o m e n ta r uklonjen isp red sam o je d n o g red a ili o b a re d a o zn aen a sa (2); statin i elem en ati e se inicijalizovati sam o je d n o m . V eba 13: (1) P roverite n are d b e iz p re th o d n o g pasusa. V eba 14: (1) N ap rav ite klasu s je d n im sta tin im p o ljem tip a S trin g koje se inicijalizuje n a m e stu definisanja i d ru g im p o lje m koje in i ja liz u je statian blok. D oajte statin u m e to d u koja ispisuje o b a p o lja i p o k azu je d a se o b a inicijalizuju p re u p o treb e.

Inicijalizacija nestatinih instanci


Z a inicijalizaciju n estatin ih p ro m en ljiv ih svakog objekta, Java obezbeuje slinu sintaksu, n azvanu inicijalizacija instanci. Sledi p rim er:
// : inicijalizacija/Solje.java / / Javina " I n i c i j a l i z a c i j a i n s t a n c i. " i mp o r t s t a t i c n e t . m i n d v i e w . u t i 1 . P r i n t . * ;

class Solja { S o l j a ( i n t marker) { p r i n t ( " S o l j a ( " + marker + " ) " ) ;

}
void f ( i n t marker) { p r i n t ( " f ( " + marker + " ) " ) ;

} }
pu blic class S olje { So lja s o l j a l ; So lja s o lja 2 ;

{
soljal = new S o l j a ( l ) ; solja2 = new So l j a ( 2 ) ; print("soljal i solja2 inicij a l iz ov an i");

}
S o lje () { print("S ol j e ( ) ");

}
S o lje (in t i) {

Poglavlje 5: Inicijalizacp i ienje

145

p rin t("S o lje (in t)");

}
p u b lic s t a t i c void m a in (S trin g [] args) { p r i n t ( " U n u t a r metode m a in ( ) " ) ; new Sol j e ( ) ; p rin t ( " n o v e S o lje ( ) zavrene"); new S o l j e ( l ) ; p r in t ( "n o v e S o l j e ( l ) zavrene");

}
} / * I s p is ; Unutar metode main() S o lja (l) S olja (2 ) s o l j a l i s o lja 2 i n i c i j a l i z o v a n i Sol j e ( ) nove S o lje ( ) zavrene Sol j a ( l ) S olja (2 ) s o l j a l i so lja 2 i n i c i j a l i z o v a n i Sol j e ( i n t ) nove S o l j e ( l ) zavrene

* ///:O b ra tite p an ju na to da o d re d b a inicijalizacije instanci: {


s o l j a l = new S o l j a (1); so lja 2 = new Solj a(2 ); p r i n t ( " s o l j a l i solja 2 i n i c i j a l i z o v a n i " ) ;

} izgleda gotovo istovetno kao o d red b a za inicijalizaciju statin ih elem enata, sam o to nedostaje rezervisana re static. O va sin tak sa je n e o p h o d n a da bi se obezbedila p o d rk a za inicijalizaciju anortim nih unutranjih klasa (poglavlje U nutranje klase), ali o m o g u u je i da jam ite kako e se o d re en e op eracije izvriti bez o bzira na to koji e eksplicitni kons tru k to r biti pozvan. Iz rezultata p ro g ram a v id ite da se o d re d b a inicijalizacije instanci izvrava p re svih k o n stru k to ra.

Veba 15: (1) N aprav ite klasu s je d n im p o ljem tip a String koje se inicijalizuje p o m o u
inicijalizacije instanci.

Inicijalizacija nizova
Niz je sekvenca objek ata ili elem en ata pro sto g tip a koji su jed n o g istog tip a i upak o v an i zajed no p o d jed n im im e n o m za identifikaciju. N izovi se definiu i koriste p o m o u operatora indeksiranja [ ]. R eferencu niza definiete tako to iza im ena tip a stavite prazn e uglaste zagrade:
i n t [ ] a l;

146

Misliti na Javi

Ako uglaste zagrade stavite posle id en tifik ato ra, znaenje e b iti p o tp u n o isto. in t a l [ ] ; O vo se uklap a u oekivanja C i C + + p ro g ra m era. Prvi stil sadri v erovatno p ristu p aniju sintaksu, je r p o k azu je d a je tip p ro m en ljiv e n iz celih brojeva. U ovoj knjizi bie korien taj stil. Prevodilac n e dozvoljava d a navedete veliinu niza. To nas vraa na p itan je referenci. Sve to u ovom tre n u tk u im ate jeste referenca n a niz (rezervisali ste dovoljno p ro sto ra za tu referencu) i nikakav p ro s to r za sm etanje sam og niza nije rezervisan u m em oriji. D a biste napravili skladite za niz, m o rate da napiete izraz za inicijalizaciju. Kod nizova, inicijalizacija m oe da se pojavi bUo gde u kodu, ali m o ete da koristite i p o seb n u v rstu izraza za in i jalizaciju nizova koji m oe da se pojavi sam o na m estu definicije niza. O va specijalna inicijalizacija zadaje skup v re d n o sti izm e u vitiastih zagrada. U ovom sluaju, prevodilac vodi rau n a o zau zim an ju skladinog p ro sto ra (kao da ste koristili new ). N a p rim er:
int [] al = { 1, 2, 3, 4, 5 };

Pa, em u o n d a slue reference n a nizove bez d o d eljen ih nizova? in t [] a2; Poto je u Javi m ogue da jed an niz d o d elite d ru g o m , m o ete i da napiete: a2 = a l; Pri to m e se, u stvari, sam o k o p ira referenca, to n a re d n i k o d pokazuje:
/ / : in i c i j a l i z a c i j a / N i z o v i P r o s t i h T i p o v a . j a v a import s t a t i c n e t .m in dvi e w . u t i 1 . P r in t pu b lic class NizoviProstihTipova { pu b lic s t a t i c void m a in (S trin g [] args) { i n t [ ] al = { 1, 2, 3, 4, 5 }; i n t [ ] a2; a2 = a l ; f o r ( i n t i = 0; i < a2.1ength; i++) a 2 [ i] = a 2 [ i] + 1; f o r ( i n t i = 0; i < a l . l e n g t h ; i++) p r i n t ( " a l [ " + i + "] = " + a1 [ i ] ) ;

}
} / * Is p is : a l [0] = 2 al [1] = 3 a l[2 ] = 4 a l [3] = 5 a l [4] = 6

* ///:-

Poglavlje 5: Inicijalizacija i ienje

1 47

O b ra tite p an ju n a to da je n izu a l d o d eljen a inicijalizaciona v red n o st, a nizu a2 nije; referenci a2 v red n o st je d o d eljen a k asnije - u ovom sluaju, o n a pokazuje n a d ru g i niz. Poto su a2 i a l tak o p o stali p se u d o n im i istog niza, izm en e sprovedene preko a2 o d raavaju se i u a l. Svi nizovi im aju lan (bilo da su n izovi o b jek ata ili nizovi elem enata p ro sto g tip a) koji m oete itati - ali n e i m en jati - da biste znali koliko elem en ata im a u nizu. Taj lan je len g th . Poto u Javi, kao u C -u i C + + -u , n izovi p o in ju o d indeksa 0, najvei elem en t koji m oete da indelcsirate je l e n g t h - 1. A ko izaete van granica, C i C + + to utke p rih v ataju i dozvoljavaju da vrljate dalje p o m em o riji, to pred stav lja izvor m n o g ih greaka. Java vas, m e u tim , titi o d takvih p ro b lem a tak o to pro izv o d i greku p ri izvravanju ( izu zetak) ako zakoraite v an g ran ica. ta ako ne zn ate koliko e v am e lem en ata b iti p o tre b n o u n izu d o k piete p rogram ? Sam o u p o tre b ite o p e ra to r n e w z a p rav ljen je elem en ata niza. U n a re d n o m p rim e ru koristi se new , iako se pravi niz iji su elem en ti p ro sto g tip a (n e w n e m oe d a n ap rav i sam o jed nu p ro m en ljiv u p ro sto g tipa):
/ / : in i c i j a l i z a c i j a / N i z o v i I N e w . j a v a / / P ra v lje n je nizova operatorom new. import j a v a . u t i 1 . * ; import s t a t i c n e t . m in d v i e w . u t il. P r i n t . * ; pu b lic class NizoviINew { pu b lic s t a t i c void m a in (S trin g [] args) { i n t [ ] a; Random slu caja n = new Random(47); a = new i n t [ s l u c a j a n . n e x t l n t ( 2 0 ) ] ; p r in t ( " d u z in a niza a = " + a . le n g th ) ; p rin t(A rra y s .to S trin g (a ));

}
} / * I s p is : duzina niza a = 18

[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] } ///Poto se veliina niza bira p seu d o slu ajn o (m e to d o m R a n d o m .n e x tIn t(), iji je re zu k tat sluajan broj izm e u nule i n jen o g arg u m e n ta ), jasn o je da se niz zaista pravi za v rem e izvravanja. Pored toga, iz rezultata ovog p ro g ram a v id ite da elem en ti nizova p ro stih tipova au to m atsk i d o b ijaju p o d raz u m ev an e v red n o sti. (Za n u m erik e i p rom enljive tipa ch a r to je nula, a za b o o le a n to je false.) M etod a A rra y s.to S trin g (), koja p rip ad a sta n d a rd n o j Javinoj biblioteci ja v a .u til, daje verziju je d n o d im e n z io n a ln o g niza koja se m oe ispisati.
5 N a ra v n o , p ro v e ra v a n je sv ak o g p ris tu p a n iz u iz isku je v re m e i k o d i tu p ro v e ru n e m o e te d a iskljuite, to z n a i a p ris tu p i n iz o v im a m o g u b iti izv or n e e fik a sn o s ti u v a e m p ro g r a m u ako s e ja v lja ju n a van im m e stim a . T v o rci |a v e su s in a tra li d a v re d i rtv o v a ti n e to e fik a sn o s ti n a u trb s ig u rn o s ti n a Inte r n e tu i p r o d u k tiv n o s ti p ro g ra m e ra . M a d a ete m o d a d o i u isk u e n je d a sam i p iete k o d k o ji bi tre b a lo e fik asn ije da p ris tu p a n iz o v im a , to je g u b lje n je v re m e n a , p o to e a u to m a ts k e o p tim iz a c ije to k o m p re v o e n ja i iz v rav a n ja u b rz a ti p ris tu p a n je n iz o v im a .

148

Misliti na Javi

N aravno, u ovom sluaju niz je m ogao d a b u d e definisan i inicijalizovan u istoj naredbi:


i n t [] a = new i n t [ s l u c a j a n . n e x t l n t ( 2 0 ) ] ;

\k o m oete, bilo bi bolje da rad ite tako. Kada n ap rav ite n iz o bjekata, u stvari p rav ite niz referenci. R az m o trim o o m o ta k i tip In teg er, koji je klasa, a n e p ro st tip:
/ / : in ic ij a l i z a c i j a / N i z O b j e k a t a . j a v a / / P ra v lje n je niza i j i elementi nisu prostog t i p a . import j a v a . u t i l . * ; import s t a t i c n e t . m in d v i e w . u t il. P r i n t . * ; p u b lic class NizObjekata { p u b lic s t a t i c void m a in (S trin g [] args) { Random slu caja n = new Random(47); I n t e g e r [ ] a = new I n t e g e r [ s lu c a ja n . n e x t l n t ( 2 0 ) ] ; p r in t ( ''d u z in a niza a = 1 1 + a . le n g th ) ; f o r ( i n t i = 0; i < a .le n g th ; i++) { a [ i ] = s lu c a ja n . n e x t ln t ( 5 0 0 ) ; / / Autopakovanje p rin t(A rra y s .to S trin g (a ));

)
} / * I s p is : (Primer) duzina niza a = 18 [55, 193, 361, 461, 429, 368, 200, 22, 207, 288, 128, 51, 89, 309, 278, 498, 361, 20]

* U /-.U g o rn jem p rim e ru , ak i kada o p e ra to ro m n ew n a p rav im o niz:


Integ er [] a = new I n t e g e r [ s lu c a ja n . n e x t l n t ( 2 0 ) ] ;

d o b iem o sam o niz referenci pa inicijalizacija nee biti p o tp u n a sve do k ne inicijalizujem o sam e reference pravei nove objekte tipa Integer (u ovom sluaju, preko autopakovanja):
a [ i ] = new Integer ( s lu c a ja n . n e x tln t ( 5 0 0 ) ) ;

Ako to k o m izvravanja zab o rav ite da n ap rav ite objekat, javie se izuzetak kada po k u ate da u p o tre b ite p ra z n u lokaciju u nizu. Niz o b jekata tak o e m o ete da inicijalizujete koristei listu izm eu vitiastih zagrada. Za to p o sto je dva oblika:
/ / : in ic ija liz a c ija /In ic ija liz a c ija N iz a .ja v a / / I n i c i j a l i z a c i j a niza. import j a v a . u t i l . * ; p u b lic class I n i c i j a l i z a c i j a N i z a { p u b lic s t a t i c void m a in ( S tr in g [] args) { Integerf ] a = { new I n t e g e r ( l ) ,

Poglavlje 5: Inicijalizaaja i ienje

149

new I n t e g e r ( 2 ) , 3, / / Autopakovanje

);
In te g e r[ ] b = new Integer [ ] { new I n t e g e r ( l ) , new In te g e r( 2 ) , 3, / / Autopakovanje

);
S y s t e m . o u t. p r in t ln ( A rr a y s . to S tr in g ( a ) ) ; System.out. p r i n t ln ( A r r a y s . t o S t r i ng(b)) ;

};
} / * I s p is : [1. 2, 3] [1, 2, 3]

* ///:U ob a sluaja, zavrni zarez u listi inicijalizatora nije obavezan. (Z ato je lake o d ravanje dugakih lista.) Poslednji zarez u listi inicijalizatora je o p cio n i. (O va m o g u n o st je n am en jen a lakem o d ravan ju d ugakih lista.) lako je p rv i oblik k oristan , njegova o g ran ien ja su vea, je r se m oe u p o treb ljav ati sam o na m estu definicije niza. D rugi i trei oblik m oete u p o tre b iti b ilo gde, ak i u n u ta r poziva neke m etode. Na p rim er, m ogli biste n ap rav iti niz o bjekata tip a String koje ete proslediti m etod i main() neke d ru g e m etode, da biste n a taj n ain zadali a ltern ativ n e arg u m en te k o m a n d n e Iinije toj m eto d i main():
/ / : i n i c i j a l i z a c i j a / D i n a m i c k i N i z . java / / I n i c i j a l i z a c i j a nizova. pu b lic class inamickiNiz { pu b lic s t a t i c void m ain(S trin g [ ] args) { Druga.main(new S t r in g [ ] { " t r a " , " l a " , " l a " } ) ;

}; }
class Druga { p u b lic s t a t i c void m ain(S trin g [ ] args) { f o r ( S t r in g s : args) Sys tem .ou t.print(s + " " ) ;

};
} / * Is p is : t r a la la

* lll'Niz napravljen za arg u m en t m etode Druga.main() n ap rav ljen je n a m estu poziva te m etode, pa altern ativ n e arg u m en te m oete zadati ak i u tre n u tk u to g poziva. Veba 16: (1) N apravite niz objekata tipa String i svakom elem e n tu d o d elite p o jed a n String. Ispiite niz petljom for.

150

Misliti na Javi

Veba 17: (2) N aprav ite klasu s k o n stru k to ro m koji p rim a arg u m en t tip a String. T okom
p ravljenja ispiite taj a rg u m en t. N apravite niz referenci objek ata te klase, ali n e m o jte p ra viti objekte koje ete d o d eliti to m n izu. K ada p o k ren ete p ro g ram , p ro v erite d a li se ispisuju inicijalizacione p o ru k e iz poziva k o n stru k to ra .

Veba 18: (2) D o p u n ite p re th o d n u vebu p ravljenjem o bjekata koje ete d o d eliti n iz u referenci.

Liste promenljivih parametara


D rugi oblik (inicijalizacije niza) o b ezb e u je p o g o d n u sin tak su za pravljenje i pozivanje m eto d a koje m o g u d a p ro izv ed u isti efekat kao lista p ro m en ljiv ih p a ra m e ta ra u C -u (p o zn a ta i kao varargs). Lista m o e im ati n e p o z n a t bro j arg u m en ata, a m o g u se p o jav iti i nep o z n ati tipovi. Poto sve klase u o sn o v i n asle u ju zajedniku k o ren sk u klasu Object (o em u ete vie saznati n a p re d u ju i k ro z o v u knjig u ), m o ete d a n a p rav ite m e to d u iji je arg u m e n t n iz elem en ata tip a Object i d a je pozovete na sledei nain:
/ / : in ic ija liz a c ija /P ro m e n ljiv a L is ta .ja v a / / Korienje sin takse nizova za p r a v lje n je promenljive l i s t e parametara. class A { } pu b lic class Prom enljivaLis ta { s t a t i c void is p i s i N i z ( O b j e c t [ ] argumenti) { fo r(O b je c t obj : argumenti) S ys te m .o u t.p rin t(o b j + " " ) ; S y s t e m . o u t. p r in t ln ( ) ;

}
pu b lic s t a t i c void m a in (S trin g [ ] argumenti) { is p is iN iz (ne w O bje ct[ ] { new In te g e r(4 7 ), new F lo a t(3 .1 4 ), new Double ( 11.11)

});
is p is iN iz (ne w O bje ct[ ] {"je d a n ", "dva", " t r i " } ) ; i spisiNiz(new O bje ct[ ] {new A (), new A ( ) , new A( ) } );

}
} / * I s p is : (Primer) 47 3.14 11.11 jedan dva t r i A@la46e30 A@3e25a5 A@19821f

* ///:M oete v ideti da p r in t( ) u zim a niz elem enata tip a O b jec t, a zatim foreach sintaksom prolazi kroz niz i ispisuje sve njegove objekte. Klase sta n d a rd n e Javine biblioteke daju razum ljiv ispis, ali o bjekti ovde n ap rav ljen ih klasa tam p aju im e klase, znak @i heksadecim aln e cifre. D akle, p o d ra z u m e v a n o p o n aan je (ako za svoju klasu ne definiete m eto d u to S trin g ( ), to e b iti o p isan o u nastavku knjige) jeste da se ispisuje im e klase i adresa olijekta.

Poglavlje 5: Inicjjalizacija i ienje

151

O vako su se pre pojave Jave SE5 pisali Java p ro g ram i d a bi se d ob ile p rom enljive liste p ara m e ta ra . M ed u tim , u Javi SE5 pojavila se du go zahtevana m o g u n o st da se p ro m en ljive liste p a ra m e ta ra definiu p o m o u o p era to ra tri take, kao to vidite u sledeoj m e to d i

ispisiNiz():
/ / : i n i c i ja lizacija/N ovaProm enlj i v a L i s t a . j a v a / / Korienje sintakse vezane za nizove, za p ra v lje n je promenljive l i s t e / / parametara. p u b lic class NovaPromenljivaLista { s t a t i c void is p i s i N i z ( O b j e c t . . . argumenti) { f o r ( 0 b je c t obj : argumenti) S y s tem .ou t.print(ob j + " " ) ; S y s t e m . o u t . p r i n t ln ( ) ;

}
p u b lic s t a t i c void m ain (S trin g [] argumenti) { / / Moe p r i m i t i pojedinane elemente: isp isiN iz(n e w Inte g e r(4 7 ), new F1oat(3.14), new Double( 11 .1 1)); is p is iN iz ( 4 7 , 3.14F, 11.11); i s p is iN iz ( " j e d a n " , "dva", " t r i " ) ; isp is iN iz (ne w A (), new A (), new A ( ) ) ; / / 111 n iz : i s p i s i N i z ( ( O b j e c t [ ])new In te g e r[ ] { 1, 2, 3, 4 } ) ; i s p i s i N i z ( ) ; / / Prazne l i s t e su dozvoljene

}
} / * I s p is : (Uzorak) 47 3.14 11.11 47 3.14 11.11 jedan dva t r i A@lbab50a A@c3c749 A@150bd4d 1 2 3 4

* ///:Kada se radi o prom enljiv im p a ra m e trim a, vie ne m o ra te eksplicitno da ispisujete sintaksu nizova - to e prevodilac u initi um esto vas im zadate prom en ljiv e p a ram etre. Svoj niz ete svakako d o b iti, zbog toga print( ) m oe foreach sintak som da p ro d e kroza sve e lem en te niza. M ed utim , na delu je vie od p roste a u to m atsk e konverzije liste elem en ata u niz. O b ra tite pan ju na p retp osled n ji red p ro g ram a, gde se niz elem enata tipa Integer (n ap rav ljen ih autop ak o v a n jem ) eksplicitno pretvara u niz elem en ata tip a Object (da bi se izbeglo up o zo ren je prevodioca) i p rosleuje m eto d i isp isiN iz( ). Jasno, prevo dilac vidi da je to ve niz i na njem u ne obavlja nikakvu konverziju. D akle, ako im ate g ru p u stavki, m oete ih proslediti kao listu, a ako niz ve im ate, prevodilac e ga p rih v atiti kao listu p ro m en ljiv ih arg u m en ata (p aram etara). Poslednji red p ro g ra m a p o kazuje cla se listi pro m en ljiv ih p a ra m e ta ra m oe prosled iti nula arg u m en a ta. To je korisno kada im ate neobavezne pratee argu m en te:

152

Misliti na Javi

/ / : in ic ij a li z a c i ja / O p c io n i P r a t e iA r g u m e n t i. java pu blic class OpcioniPrateiArgumenti { s t a t i c void f ( i n t obavezan, S t r i n g . . . p r a te i) { System.out.print("obavezan: " + obavezan + " " ) ; f o r ( S t r in g s : p ra te i) S yste m .o u t.p rin t(s + " " ) ; S y s t e m . o u t . p r i n t ln ( ) ;

)
pu blic s t a t i c void m a in ( S tr in g [] args) { f (1, " j e d a n " ) ; f (2, "dva", " t r i " ) ; f(0 );

}
} / * I s p is : obavezan: 1 jedan obavezan: 2 dva t r i obavezan: 0

* ///:I ovo pokazuje da prom enljiv.e p a ra m e tre m oete u p o treb ljav ati i sa zadatim tip o m koji nije Object. U p re th o d n o m p rim e ru , svi p ro m en ljiv i p a ra m e tri m o ra ju biti String objekti. U p ro m en ljiv im p a ra m e trim a m o g u se ko ristiti svi tip ov i, i prosti. U sledeem p rim e ru po k azu jem o d a lista p ro m en ljiv ih p a ra m e ta ra p ostaje niz i da je veliina to g niza nula ukoliko ta lista n e m a sadraja:
//: in ic ija liz a c ija / T ip P r o m e n ljiv o g P a r a m e tr a .ja v a

p u blic class TipPromenl jivogParametra { s t a t i c void f ( C h a r a c t e r . . . argumenti) { S ystem .ou t.prin t(arg um e nti. getCla ss( ) ) ; System.out. p r i n t l n (" length " + argumenti. 1e n g th ) ;

}
s t a t i c void g ( i n t . . . argumenti) { S y s te m .o u t.p rin t(a rg u m e n ti.g e tC la s s ()) ; System.out. p r i n t l n (" duzina " + argumenti. 1e n g th );

}
public s t a t i c void main( S t r i n g [] argumenti) {
f('a'); f(); g(i); g();

Sys tem .ou t.prin t l n ( " i n t [ ] :

" + new i n t [ 0 ] . getClass( ) ) ;

}
} / * I s p is : class [1ja v a .la n g .C h a ra c te r; duzina 1 class [1ja va .la n g .C h a ra cte r; duzina 0 class [ I duzina 1 class [ I duzina 0 i n t [ ] : class [ I

* ///:-

Poglavlje 5: Inicijalizaja i ienje

153

M etoda getClass() je deo Objecta i bie p o tp u n o ra zm o tre n a u poglavlju Podaci o tipu. O na daje klasu objekta, i kada je ispiete, d o b ijate k o d ira n znakovni niz koji predstavlja tip te klase. Vodee [ p o k a zu ju d a se rad i o n izu tip a koji sledi. I n azn au je pro sti int; radi jo jed n e provere, u p o sled n jem re d u sam n ap rav io niz tipa int i ispisao njegov tip. T im e je do kazan o da u p o tre b a p ro m en ljiv ih p a ra m e ta ra ne zavisi o d au to p ak o v an ja, nego da zaista rad i s p ro stim tipovim a. M e u tim , p ro m en ljiv i p a ra m e tri lepo ra d e sa au to p ak o v a n jem . N a p rim e r:
//: in ic ija liz a c ija /A u to p a k o v a n je P ro m e n ljiv ih P a ra m e ta ra .ja v a

p u b lic class AutopakovanjePromenljivihParametara { p u b lic s t a t i c void f ( I n t e g e r . . . argumenti) { f o r ( I n t e g e r i : argumenti) S y s te m .o u t.p rin t( i + " " ) ; S y s t e m . o u t . p r i n t ln ( ) ;

}
p u b lic s t a t i c void m a in ( S tr in g [] argumenti) { f(new I n t e g e r ( l ) , new I n t e g e r ( 2 ) ) ; f ( 4 , 5, 6, 7, 8, 9); f ( 1 0 , new I n t e g e r ( l l ) , 12);

}
} / * Is p is :

1 2
4 5 6 7 8 9 10 11 12

* ///:Im ajte u v idu da u istoj listi arg u m e n a ta m oete m eati tipove i da au to p ak o v a n je selektivno u n a p re u je in t a rg u m en te u tip In teg er. Prom enljivi p a ra m e tri k o m p lik u ju p o stu p a k p rek lap an ja, iako to na p rv i pogled izgleda sasvim bezopasno:
//: in ic ija liz a c ija /P r e k la p a n je P r o m e n ljiv ih P a ra m e ta ra .ja v a

p u b lic class PreklapanjePromenljivihParametara { s t a t i c void f ( C h a r a c t e r . . . argumenti) { S y s t e m . o u t . p r i n t ( " p r v i" ) ; for(C h ara cte r c : argumenti) S y s te m .o u t.p rin t(" " + c ) ; S y s t e m . o u t . p r i n t ln ( ) ;

}
s t a t i c void f ( I n t e g e r . . . argumenti) { S y s t e m . o u t. p r in t ( " d r u g i" ) ; f o r ( I n t e g e r i : argumenti) S y ste m .o u t.p rin t(" " + i ) ; S y s t e m . o u t . p r i n t ln ( ) ;

154

Misliti na Javi

s t a t i c void f( L o n g . . . argumenti) { S y s t e m . o u t . p r i n t ln ( " t r e i" ) ;

}
public s t a t i c void m a in (S trin g [] argumenti) { f C a ' , ' b ' , 1c ' ) ; f (i); f(2 . i) ; f (0 ); f (0 L ); / / ! f ( ) ; / / Nee se prevesti - - vieznano

}
} / * Is p is : prvi a b c drugi 1 drugi 2 1 drugi 0 trei

* ///:U svakom o d p re th o d n ih sluajeva, p rev o d ilac u p o tre b ljav a au to p ak o v a n je da b i se p rilagodio preklopljenoj m eto d i i poziva n ajp rik la d n iju m eto d u . Ali kada pozovete f() bez a rg u m en ata, o n n e zna k o ju m e to d u da pozove. Iako je ta greka razum ljiva, o n a e v ero v atn o izn en a d iti p ro g ra m e ra klijenta. P roblem biste m ogli p o k u ati da reite d o d a v an je m n ep ro m en ljiv o g a rg u m e n ta jed n o j o d m etoda:
/ / : i n i c i j a l iz a c ija /PreklapanjeProm enljivihParam etara2.java / / {CompileTimeError} (Nee se p r e v e s ti) pu blic class PreklapanjePromenljivihParametara2 { s t a t i c void f ( f l o a t i , C h a ra c te r... argumenti) { S y s t e m . o u t . p r i n t ln ( " p r v i" ) ;

}
s t a t i c void f (C h aracte r.. . argumenti) { S y s t e m .o u t.p r in t("d ru g i" ) ;

}
p u blic s t a t i c void main ( S t r in g [] argumenti) { f ( l , ' a ' ); fC a ', 'b ');

} } ///:O znaka {C o m p ileT im eE rro r} u k o m e n ta ru uklanja ovu d ato tek u iz sk rip ta za prevoenje p rim era iz ove knjige. Ako je prevedete ru n o , d o b iete p o ru k u o greci: reference to f is am b ig u o u s, b o th m e th o d f(float,java.lang.C haracter...) in P reklapanjeP rom enIjivihP aram etara2 a n d m eth o d f(java.lang.C haracter...) in P rek lap an jeP ro m en ljivihParam etara2 m atch

Poglavlje 5: Inicijalizacija i ienje

1 55

A ko o bem a m e to d am a date n ep ro m en ljiv a rg u m e n t, upalie:


//: inicijalizacija/PreklapanjePromenljivihParametara3.java public class PreklapanjePromenljivihParametara3 { static void f(float i, Character... argumenti) System.out.println("prvi"); {

}
static void f(char c, Character... argumenti) Sy stem.out.println("drugi"); {

}
public static void main(String[] argumenti) f(l, 'a'); f (' a ', 1b 1); {

}
} /* Ispis: prvi drugi

* ///:Po pravilu, listu p ro m en ljiv ih arg u m e n a ta treb alo bi d a u p o treb ljav ate sam o u jed n o j od p rek lo p ljen ih m eto d a. Ili, u o p te n e u p o trebljavajte.

Veba 19: (2) N apiite m e to d u koja p rim a String niz p ro m en ljiv ih a rg u m en ata. U verite se da toj m e to d i m oete proslediti listu elem en ata tip a String razdvojenih zarezim a ili String[]. Veba 20: ( 1) N apiite m e to d u main() koja u m esto u o b iajen e sintakse u p o treb ljav a p ro menljive a rg u m en te. Ispiite sve elem en te rezu ltu ju eg niza args. Ispitajte tu m e to d u p o m ou razliitog b ro ja arg u m e n ata s k o m a n d n e linije.

Nabrojani tipovi
Naizgled m ali d o d a tak u favi SE5 jeste rezervisana re e n u m koja m n o g o olakava ivot kada tre b a da g ru piete i u p otreb ite skup nabrojanih tipova (engl. enum erated types). N ekada biste napravili skup k o n sta n tn ih c elo b ro jn ih v red n o sti, ali one ,,ne znaju da ine skup, pa se rizinije i tee upo trebljavaju. Inae, n a b ro jan i tip o v i su toliko esto p o tre b n i da su oduvek postojali u C -u , C + + -u i u vie d ru g ih jezika. Pre Jave SE5, p ro g ra m e ri na Javi m orali su m n o g o d a znaju i da paze kako bi valjano proizveli e n u m efekat. Sada i Java ima e n u m , a o n je ak i m oniji o d o noga to im aju C /C + + . Evo je d n o stav n o g p rim era :
/ / : i n i c i j a l iza ci j a / L j u t o . java pu b lic enum Ljuto { NE, BLAGO, SREDNJE, M NO G O , PALI

} ///:-

156

Misliti na Javi

O vim e je n ap rav ljen n a b ro ja n i tip Ljuto s p et im en o v an ih v red n o sti. Poto su p rim e rci n a b ro ja n ih tip o v a k o n stan te, p o konvenciji ih piem o velikim slovim a (ako im e sadri vie rei, razd v ajam o ih d o n jo m crtico m - po d v lak o m ). K ada v am zatreb a enum, n ap rav ite referencu to g tipa i d odelite je n ek o m p rim e rk u (instanci):
/ / : in ic ijalizacija /Je d n o sta v n a llp o tre b a N a b ro ja n ih T ip o v a .ja va pu b lic class JednostavnatlpotrebaNabrojanihTipova { p u b lic s t a t i c void m a in (S trin g [] args) { Lju to k c l i k o l j u t o = Ljuto.SREDNJE; S y s t e m . o u t. p r in t ln (k o lik o l j u t o ) ;

}
} / * Is p is : SREDNJE

* ///:K ada n ap rav ite enum, p revodilac m u au to m atsk i dod aje korisne m o g u n o sti. N a p rim er, prav i m e to d u toStringO k o jo m m o ete lako da prik aete im e enum instance. (U pravo tako je p re th o d n a n ared b a print dala rezultat. Prevodilac pravi i m e to d u ordinal() koja daje red n i bro j d eklarisanja o d red en e enum k o n stan te u n jenoj klasi, i statin u m e to d u values() koja daje niz v red n o sti enum k o n stan ti p o re a n ih p o red o sled u kojim su bile deldarisane:
/ / : in ic ija liz a c ija /R e d o s le d N a b ro ja n ih T ip o v a .ja v a p u b lic class RedosledNabrojanihTipova { p u b lic s t a t i c void m a in (S trin g [] args) { f o r ( L j u t o s : L ju t o . v a lu e s ()) S y s te m .o u t.p rin tln (s + ", " + s . o r d i n a l( ) + ". po redu");

}
} / * Is p is : NE, 0. po redu BLAGO, 1. po redu SREDNJE, 2. po redu MN0G0, 3. po redu PALI, 4. po redu

* ///:lako enum izgleda kao nov tip p o d atak a, ta rezervisana re sam o o d re u je p o n aan je p revodioca to k o m generisanja klase za enum, pa m oete u m n o g o m e da ga tre tira te kao i svaku d ru g u klasu. U stvari, n a b ro jan i tipovi i jesu klase sa sopstvenim m eto d am a. Posebno je zg odno kako se n ab ro jan i tipovi m o g u u p o trebljavati u n u ta r n ared b e

svvitch:
//: i n i c i j a l i z a c ija / P lje s k a v ic a . ja v a

pu b lic class Pljeskavica { Spiciness stepen; pu b lic P1jeska vica(Ljuto stepen) { th is.ste p e n = stepen;}

Poglavlje 5: Inicijalizacija i ienje

157

p u b lic void o p i i (} { System .out.print("O va plje ska vic a j e " ) ; switch(stepen) { case NE: System .ou t.println ("po tp un o nezainje na." ) ; break; case BLAGO; case SREDNJE: S y s t e m . o u t . p r i n t l n ( " l j u t k a s t a . " ) ; break; case M NO G O : case PALI: d e fa u lt: S y s te m .o u t.p rin tln ("p re lju ta .");

} }
p u b lic s t a t i c void m a in (S trin g [] args) { P 1jeskavic a bez = new P1jeskavica(Ljuto.N E), bib e r = new P1jeska(Zainjenost.SREDNJE), fefero n = new P1jeska(Zainjenost.MNOGO); bez.opii ( ) ; b ib e r . o p i i ( ) ; f e f e r o n . o p i i ( ) ;

}
} / * I s p is : Ova p lje s k a v ic a j e potpuno nezainjena. Ova p lje s k a v ic a j e lj u t k a s t a . Ova p lje s k a v ic a j e p r e l j u t a .

* ///:Poto sw itc h slui za izbo r iz ogranier.og sk u p a m o g u ih v rednosti, idealno o dg ov ara definisanju tipova n ab rajan jem . O b ratite pan ju na to kako im ena n a b ro jan ih tipova m nogo jasnije p o kazu ju ta e p ro g ram urad iti. U opte uzev, enum m oete da koristite kao d ru g i nain efinisanja tipova p o dataka . To je i b io cilj, pa na njih vie ne m o rate o b raati p o seb n u panju. Pre uvoenja n ab ro janih tip o v a u Javu SE5, trebalo je uloiti dosta tru d a za pravljenje ekvivalentnog n a b ro janog tipa koji je b ezb ed n o upotrebljavati. O vo je do voljno za shvatanje i korienje je d n o stav n ih n ab ro jan ih tipova. N jim a sm o posvetili p o seb n o poglavlje, N a b m ja n i tipovi.

Veba 21: (1) D efiniite enum o d est n ajm an je v red n ih tipova p ap irn ih novanica. M eto d o m values() p ro d ite kroz sve n ab ro jan e v red n o sti, ispiite svaku v red n o st i (m e to d o m ordinai()) njen redni broj. Veba 22: (2) N apiite naredba switch za enum iz p re th o d n o g p rim era. Za svaki case,
ispiite op is te novanice.

158

Misliti na Javi

Saetak
P rilino razra en m e h a n iz am ini jalizacije, kakav je k o n stru k to r, treb alo bi da ukae na o g ro m n u vanost koja je u o v o m jeziku d ata inicijalizaciji. Jedno od p rv ih zapaanja o p ro d u k tiv n o sti u C -u , koje je S tro u stru p izneo d o k je p ro jek to v ao jezik C + + , bilo je da nep rav iln a in icijaliza ja p ro m en ljiv ih p ro u zro k u je znaajan deo pro b lem a p ri p ro g ram iran ju . O v u v rstu greaka je teko p ro n ai, a slini p ro b lem i javljaju se i p ri nepraviln o m ienju. Poto k o n stru k to ri o m o g u u ju da garantujete prav iln u inicijalizaciju (prevodilac nee d o zv o liti d a o b jek at b u d e n ap rav ljen bez valjanog poziva k o n stru k to ra ), d obijate p o tp u n u k o n tro lu i sig urno st. U C + + -u u n itav an je je v eom a b itn o , je r objekti n apravljen i o p e ra to ro m n ew m o raju eksplicitno da b u d u u n iten i. Sakuplja sm ea u Javi au to m atsk i oslobaa m em o riju , tak o da u veini sluajeva o d g o varaju e m eto d e za ienje nisu p o treb n e. (Ali kada jesu, m o rate sam i sve d a u ra d ite .) Kada v am ne treb a p o n a a n je nalik na d estru k to re, Javin sakuplja sm ea u m n o g o m e p o jed n o stav lju je p ro g ram ira n je i dodaje preko p o tre b n u sig u rn o st u u p rav ljan ju m e m o rijo m . N eki sakupljai sm ea m o g u ak da iste i drug e resurse, kao to su id en tifik ato ri d ato tek a i grafikih objekata. M e u tim , sakuplja sm ea dod aje reijske trokove to k o m izvravanja p ro g ram a, iju je cenu teko pro cen iti zbog tra d i o n a ln e sp o ro sti Javinih in te rp re ta to ra. Iako su se p erfo rm an se Jave z n atn o poboljaie to k o m v rem en a, taj jezik se, zbog svoje spo ro sti, i dalje ne p rim en ju je u nekim situacijam a. O k o n stru k to rim a p osto ji jo stvari koje nisu ra zm o tren e u ovom poglavlju. Poto garancija ko n stru k cije vai i kada p rav ite nove klase kom pozicijom ili nasleivanjem , p o trebn a je d o d a tn a sin tak sa da bi se to podralo. O kom poziciji, nasleivanju i to m e kako te operacije u tiu n a k o n stru k to re , nauiete u n ared n im poglavljim a.
R eenja o d a b ra n ih vebi d a ta su u e le k tro n sk o m d o k u m e n tu The Thinking in Java Am iotated Solu-

tion Guide, k oji se m o e k u p iti na lo kaciji www.M indView.com.

Kontrola pristupa
K ontrola p ristu p a (ili sakrivan je realizacije) zn ai m oi p o p ra v iti ono to p r v i p u t nije bilo dobro u ra en o .
SVI DOBRI PISCI - UKLJU U JU I T U I O N E KOJI PlSU SOFTVER - ZN A JU DA NITA NIJE D O BRO

n ap isan o d o k se vie p u ta n e p rerad i. A ko d eo svog k o d a na neko v rem e ostavite u fioku i zatim ga p o n o v o p ro itate, m o d a ete o tk riti m n o g o bolji n a in d a ga u rad ite. To je jedan o d glavnih razloga za p o n o v n u p o d e lu na p ro ste faktore, dakle za p re ra d u fu n k cio n alnog k o d a da bi se uin io itljiviji i razum ljiviji, a sam im tim e se i lake o d rav ati.1 M e u tim , posto je i p ro b lem i zbog n asto jan ja d a se p ostojei k o d m en ja i poboljava. esto korisnici (p ro g ra m eri klijen ti) rau n aju na to da e o d re e n i aspekat koda ostati neizm enjen. D akle, vi h o ete d a ga m en jate, a o n i d a o n o stan e neizm en jen . Z ato je jed an od o sn o v n ih ciljeva o b jek tn o o rijen tisan o g dizajna razdvojiti o n o to se m en ja o d stvari koje o sta ju iste. O vo je p o se b n o vano za biblioteke. K orisnici biblioteke m o ra ju b iti sig u rn i kako se m ogu o slo n iti na deo koji k o riste i kako nee m o rati p o n o v o d a p iu p ro g ra m e k ad a se poiavi nova verzija biblioteke. S d ru g e stran e, tvorac biblioteke m o ra im ati slo b o d u da pravi izm en e i u n a p re e n ja a da p ri to m b u d e siguran kako te izm ene nee uticati na kod klijentskog p rog ram a. Sve to m o e da se ostvari p rek o konvencija. Na p rim er, p ro g ra m e r biblioteke m o ra da se sloi kako nee izbacivati postojee m eto d e kada m enja klasu u b iblioteci, je r b i tim e n aru io k o d p ro g ram era klijenta. O b rn u ta situacija je z n atn o sloenija. A ko je re o polju, kako tv orac biblioteke m oe da zna kojim su p oljim a p ristu p ali p ro g ra m e ri klijenti? Isto vai i za m eto d e koje su sam o deo realizacije klase i nisu n am en jen e da ih d ire k tn o u p o trebljavaju p ro g ram e ri klijenti. ta ako tvorac biblioteke eli d a p o tp u n o izbaci staru realizaciju i napie novu? M enjanje bilo koje od gore p o m e n u tih lanica m oglo bi d a n arui ko p ro g ra m e ra klijenta. Stoga su tvorcu biblioteke vezane ruke i ne m oe nita d a m enja. Da bi se reio ovaj p ro b lem , u Javi postoje specifikatori p ristu p a koji tv o rcu biblioteke o m o g uava ju da naznai ta je p ro g ra m e rim a klijentim a d o stu p n o , a ta nije. N ivoi d o zvoljenog p ristu p a od p o tp u n o g p ristu p a" d o najm anjeg p ristu p a jesu p u b lic , p ro te c ted, p ak etn i p ristu p ( 7 a koji ne p ostoii rezervisana re) i p riv a te . N a o sn o v u p re th o d n o g m oete p o m isliti da kao p ro jek tan t b iblioteke treb a sve da uvate to ,,privatnije m ogue i da izloite sam o m etode koje elite da p ro g ram eri klijenti koriste. To je sasvim tano, iako je esto su p ro tn o intuiciji stru n ja k a koji p ro g ram ira ju na d ru g im jezicim a (posebno na C -u ) i koji su navikli da svem u p ristu p aju bez ogranienja. D o kraja ovog poglavlja trebalo bi d a se uverite u znaaj k o n tro le p ristu p a u Javi.

'

V ideti liefactoring: Improving tlie Design o f Existing Code, koji su napisali M artin Fow!er i dr. (Addison-W esley, 1999). Povrem eno se neko pobuni protiv po n o v n ep o d ele na proste faktore, sa argum entom da je kod koji radi savreno d o b ar i da preraivati ga znai gubiti vrem e. Takav argum ent nije vaijan zato to lavovski deo vrem ena i novca koji se uloi u projekat ne o tp ad a na prvo pisanje koda, nego na njegovo odravanje. U initi kod razum ljivijim , znai utedeti m nogo novca.

16 0

Misliti na Javi

K oncept biblioteke k o m p o n e n a ta i k o n tro le p ristu p a o v im nije zavren. I dalje postoji p itan je kako se k o m p o n e n te p o v ezu ju u je d n u k o h e re n tn u bib liotek u. U Javi se paketi prave p o m o u rezervisane rei package, a n a specifikatore p ristu p a utie da li je klasa u istom ili u nekom d ru g o m p ak etu . N a p o e tk u ovog poglavlja nauiete kako se ko m p o n e n te biblioteke stavljaju u pakete, a n ak o n toga m oi ete da raz u m e te p o tp u n o znaenje specifikatora p ristu p a .

Paket: biblioteka jedinica


Paket sadri g ru p u klasa, o b jed in jen u u isto m prostoru im ena. N a prim er, b ib liotek a sa alatk am a java.util deo je sta n d a rd n e d istrib u cije Jave. ArrayList je jed n a o d klasa u p ak etu java.util. Jedan o d n a in a u p o tre b e klase ArrayList jeste da navedete n jen o p u n o im e java.utiI.ArrayList.
/ / : pristup/PunoIme.java p u b lic class Punolme { pu blic s t a t i c void m a in (S trin g [] args) { j a v a . u t i l . A r r a y L i s t l i s t a = new j a v a . u t i l . A r r a y L i s t ( ) ;

} } ///= O vo brzo p ostaje zam o rn o , p a ete u m e sto nav o enja p u n o g im en a klase verovatno radije upotreb ljav ati rezerv isan u re import. Ako elite da uvezete sam o je d n u klasu, njeno im e m oete da n avedete u n aredb i import:
/ / : pristu p/Jed an lm po rt.ja va import j a v a . u t i l . A r r a y L i s t ; pu b lic class Jedanlmport { pu b lic s t a t i c void m a in (S trin g [] args) { A rra y L is t l i s t a = new j a v a . u t i 1 . A r r a y L is t ( ) ;

} } ///= Sada ArrayList m oete da k o ristite bez im en a paketa. M e u tim , nije d o stu p n a nijedna d ru g a klasa iz p aketa java.util. Da biste uvezli sve klase biblioteke, u p o tre b ite * kao to je u rad e n o u ostalim p rim e rim a u ovoj knjizi:
import j a v a . u t i l . * ;

O vo uvoenje se rad i da bi se obezbed io m eh an izam za upravljanje im ensk im pro storim a (engl. nam e spaces). Im en a svih vaih lanica klasa su izolovana jedn a od dru gih. M eto d a f() iz klase A nee se su d a riti s m eto d o m f() koja im a isti p o tp is (listu argum enata) iz klase B. Ali, kako stoje stvari sa im en im a klasa? P retp o stav im o da n apravite klasu Stek koja se instalira na ra u n a r na k om e ve postoji klasa Stek koju je neko d ru g i napisao. Z bog p o ten cijaln ih suk ob a im ena b itn o je da im ate p o tp u n u k o n tro lu im enskih prostora u Javi i da za svaku klasu p rav ite jed in stv en u k o m b in aciju identifikatora.

Poglav[je 6: Kontrola pristupa

161

Do sada se vei d eo p rim e ra u ovoj knjizi nalazio u je d n o j d ato teci i bio je n ap isan za lokalnu u p o tre b u , te se nije b avio im e n im a pak eta. U tim p rim e rim a klase su ip ak bile u paketu, tzv. n eim en o v an o m ili podrazumevanom paketu. To se svakako m oe rad iti, i u nastavku knjige takav p ristu p bie korien gde g o d je m ogue, je d n o stav n o sti radi. Ali ako nam eravate da prav ite b iblioteke ili p ro g ram e koje e v o d iti ra u n a i o d ru g im p ro gram im a na istom ra u n a ru , m o ra te da v o d ite ra u n a o sp reav an ju su k o b a im en a klasa. Kada p rav ite dato tek u sa izv o rn im k o d o m za Javu, ta d ato te k a se esto naziva i jedinica za prevoenje (engl. compilation unit, translation unit ). Im e svake jed in ice za p revoenje m ora da se zavrava sa .java, a u n u ta r nje m oe da p o sto ji je d n a jav n a (public) klasa istog im ena kao i d atoteka (uk lju u ju i i velika i m ala slova, ali bez nastavka .java). U svakoj jedinici za prevo en je m oe d a se nalazi sam o jedna jav n a klasa, in ae se prev o d ilac b u n i. Ostale klase iz te jed in ice za prev o en je, ako ih u o p te im a, skrivene su o d sp o ljn o g sveta izvan tog paketa, jer o n e nisu javne i p red stav ljaju kiase za ,,p o d rk u glavnoj javnoj klasi.

Organizacija koda
Pri prev o en ju dato tek e .java, d o b ijate p o je d n u izlaznu d a to te k u za svaku klasu iz d ato teke .java. Svaka o d tih izlaznih d ato tek a im a isto im e kao o d g o v araju a klasa u d atoteci .java, ali s nastavkom .class. Stoga iz m alog b ro ja d a to tek a .java m oete da dobijete p rilian broj datotek a .class. Ako ste p ro g ram ira li n a jeziku koji se p rev o d i u izvrni oblik, verovatno ste navikli da prevodilac p rav i m e u o b lik (o b in o d ato te k u .obj) koji zatim s drugim d ato tek am a istog oblika p ak u jete p o m o u povezivaa (engl. linker) d a biste napravili izvrnu d atoteku ili to inite p o m o u b ib lio tek ara (engl. librarian) da biste n a p ra vili biblioteku. Java ne radi n a taj nain . P ro g ram koji rad i je sk u p d ato te k a .class koje m ogu da b u d u spakovane i k o m p rim o v an e u JAR arh iv u (p o m o u Javinog arh iv ara jar.)
Posao Javinog in te rp re ta to ra je da p ro n a e, u ita i in te rp re tira 2 te d atoteke. Biblioteka je skup takvih d ato tek a sa klasam a. Svaka izvorna d ato tek a o b in o im a jednu klasu koja je javna i proizvoljan broj n ejavnih klasa, pa za svaku izv o rn u d ato tek u p ostoji je d n a javna (p u b lic) k o m p o n en ta . Ako elite d a n azn aite da sve te k o m p o n e n te (koje se nalaze u o d vojenim d ato te k am a .java i .class) id u zajedno, k o risti se rezervisana re package. Ako ko ristite n ared b u package, o n a mora da b u d e u p rv o m re d u koji nije k o m e n ta r u datoteci. Kada napiete:
package p r is tu p ;

naznaavate da je ta jedinica za p rev o en je d eo b iblioteke p o d im e n o m p ris tu p . O d n o sno, drugaije reeno, naznaavate da je jav n o im e klase, iz te jed in ice za prev o en je, p o d kio brano m im ena p ristu p . Ako neko eli da k oristi to im e, m o ra ili da p o tp u n o navede dato im e ili da u p o treb i rezervisanu re im p o rt u k o m b in aciji s im e n o m p ris tu p , kao to je ranije objanjeno. (Im ajte u v id u d a konvencija za d avanje im en a Javinim p ak etim a nalae da se koriste sam o m ala slova, ak i za rei u sred in i im ena.)

Java b a ni sa im n e n a m e e u p o tre b u in te rp r e ta to ra . P o sto je Java p re v o d io c i u lo k a ln i k o d koji p ra v e je d n u iz v rn u d a to te k u .

1 62

Misliti na Javi

Na p rim er, p retp o stav im o d a je im e d ato tek e MojaKlasa.java. To znai da u njoj m oe d a po stoji jed n a i sam o je d n a jav n a klasa i d a im e te klase m o ra d a b u d e MojaKlasa (s to m k o m b in acijo m velikih i m alih slova):
/ / : pristup/mojpake t/M ojaKlasa .java package pristu p.m ojpaket; pu b lic class MojaKlasa {

// } ///:A ko neko eli da koristi klasu MojaKlasa ili n ek u d ru g u ja v n u klasu iz paketa pristup, m o ra d a u p o tre b i rezervisanu re im port da bi im e ili im en a iz pak eta pristup po stala dostu p n a. Takoe m oe da u p o tre b i p o tp u n o o d re e n o im e:
/ / : pristup/OdreenaMojaKlasa.java p u b lic class OdreenaMojaKlasa { pu b lic s t a t i c void m a in (S trin g [] args) { pristup.mojpaket.MojaKlasa m = new pristup.mojpaket.M ojaKlasa( ) ;

} } ///:R ezervisana re im port z n a tn o d o p rin o si jed n o stav n o sti:


/ / : pristup/OdreenaMojaKlasa.java import p ris tu p .m o jp a k e t.* ; pu b lic class UvezenaMojaKlasa { pu b lic s t a t i c void m a in (S trin g [] args) { MojaKlasa m = new MojaK1asa();

} } ///:Vredi im ati na u m u da p ro jek tan tu b iblioteke, rezervisane rei p ack ag e i im p o rt o m o guavaju da p odeli jed an g lobalni im enski p ro sto r i izbegne viesm islenost im en a, bez o bzira na to koliko se Ijudi povee na In t''rn e t i p o n e da pie klase n a Javi.

Pravljenje jedinstvenih imena paketa


B udui da paketi nikada zaista n isu ,,u p ak o v an i u je d n u d ato tek u , jed a n paket m oe da se sastoji o d vie d ato tek a .class i stvari m o g u da p o sta n u p rili n o kom plikovane. Logian potez b io bi da stavite sve d ato tek e .class je d n o g paketa u p o seb an d ire k to riju m ; o d n o sno, da iskoristite h ijerarhijsku stru k tu ru sistem a dato teka o p erativ n o g sistem a. To je jed an nain na koji Java reava p o m e n u ti p ro b lem ; dru g i nain ete videti kasnije, kada b u d e predstavljana alatka jar.

Poglavjje 6: Kontrola pristupa

1 63

G ru p isan je d ato teka jed n o g pak eta u zaseban d ire k to riju m reava i jo dva p ro b lem a: pravljenje jedinstv enih im en a paketa i pro n alaen je ldasa koje m o g u biti zak o p an e negde u d u b in i stru k tu re direkto riju m a. To se p ostie tako to se p u ta n ja do d ato tek a .class ub acuje u im e paketa. Po konvenciji, p rv i deo im e n a pak eta je im e In te rn e t d o m e n a tvorca ldase, u o b rn u to m redosledu. Poto su im en a In te rn e t d o m e n a g aran to v an o jed in stv ena, ako p rim en ju je te ovu konvenciju, im e vaeg pak eta e sig u rn o b iti jed in stv en o i nee doi do suk o b a im en a (tj. d o k neko d ru g i ne p reu zm e va d o m e n i n e po n e da pie k o d na Javi sa istim p u ta n ja m a koje ste i vi koristili). N aravno, ako n e m a te svoj d o m e n , sm islite m alo v ero v atn u k om b in aciju (kao to je vae im e i prezim e) d a b iste n ap rav ili je d in stv en o im e paketa. A ko ste odluili da objavljujete k o d p isan n a Javi, isp lati se ulo iti relativ n o m ali n a p o r u pribavljanje linog do m en a. D ru g i d eo ove tehnike je d a preslikate im e p ak eta n a im e d ire k to riju m a n a v aem rau n a ru . Kada izvrni sistem treb a da u ita d a to te k u .class, m oe da p ro n a e d ire k to riju m u k o m e se o n a nalazi. Javin in te rp re ta to r radi sledee: prv o p ro n alazi sistem sku p ro m en ljiv u CLASSPATH3 (koja se postavlja u o p erativ n o m sistem u, to p o n ek a d u ra d i in stalacioni p ro g ram koji na ra u n a ru instalira Javu ili na Javi zasnovanu alatk u ). CLASSPATH sadri je d a n ili vie direk to riju m a koji se koriste kao korenski za traen je d ato tek a .class. In te rp re ta to r u im en u paketa zam enjuje svaku taku kosom crto m da bi generisao p u ta n ju od korena definisanog p rom enljivom CLASSPATH (tako paket foo.bar.baz postaje foo\bar\ baz ili foo/bar/baz ili m o da neto drugo, u zavisnosti od vaeg o p erativ n o g sistem a). To se zatim spaja s raznim d irek to riju m im a iz p u tan je CLASSPATH. N a tim m estim a in te rp re ta to r trai d ato teku .class ije im e odgovara klasi koju p okuavate d a napravite. (O n tak o e p retrau je i neke stan d a rd n e d irektoriju m e u zavisnosti o d m esta n a k o m e se in te rp re ta to r nalazi.) D a biste ovo shvatili, razm o trite im e m og d o m e n a - MindView.net. Kada o b rn e m o redosled i svc ispiem o m alim slovim a, net.m indview ustanovljava se jed in stv en o globalno im e za m oje klase. (N astavci com , edu, org itd. ranije su u Javinim pak etim a pisani velikim slovim a, ali ie to p ro m e n je n o u Javi 2, tako da se celo im e pie m alim slovim a.) Ako od luim da n ap rav im biblioteku p o d im en o m simple, u n eu p o d elu u g lobalno im e i d o biti im e paketa:
package net.mindview.simple;

Sada ovo im e paketa m oe da se koristi kao im enski p ro s to r za sledee dve datoteke:


/ / : ne t/m indview/simple/Vector.java / / P ra v lje n je paketa package net.mindview.simple; p u b lic class Vector { pu b lic Vector() { S y ste m .o u t.p rin tln (" n e t.m in d v ie w .s im p le .V e c to r" );

} )///:S iste m sk e p ro m e n ijiv e c m o p isati v elik im s io v im a (n p r . C L A SSPA TH ).

164

Misliti na Javi

Kao to je ve bilo reeno, n are d b a package m o ra da b u d e u p rv o m red u koji nije kom en tarisan . D ru g a d ato tek a je v eo m a slina:
//: net/mindview/simple/List.java // Pravljerje paketa. package net.mindview.simple; public class List { public List() { System.out.println("net.nrindview.simple.List");

III--

O be dato tek e se sm etaju u p o d d ire k to riju m na m o m sistem u: C:\DOC\JavaT\net\mindview\simple (O b ra tite p a n ju n a to da se u p rv o m re d u k o m en tara u svakoj dato teci u ovoj knjizi daje m esto d ire k to riju m a te d ato tek e u stab lu izvornog koda. To je u ra e n o zbog alatke koja u ovoj knjizi a u to m a tsk i p ro n a laz i k o d .) A ko k ren ete o d kraja ove p u ta n je, v id ite im e p aketa net.mindvievv.simple, ali ta je s prv im d elo m putan je? O to m e je v o en o ra u n a u sistem skoj prom enljivoj CLASSPATH koja, na m o m ra u n a ru , im a sledei sadraj:
CLASSPATH=. ; D: \JAVA\LIB; C: \D0C\JavaT

V idite da p ro m en ljiv a CLASSPATH m oe da sari vie a ltern ativ n ih p u ta n ja za pretraivanje. M e u tim , kada k o ristite JAR dato tek e, p o sto je izvesne izm ene. U CLASSPATH m o rate da stavite i im e JAR dato tek e, a n e sam o p u ta n ju d o nje. Z nai, ako bi se JAR dato tek a zvala grozd.jar, u vaoj p ro m en ljiv o j CLASSPATH treb alo bi da pie:
CLASSPATH=.; D :\J A V A \L IB ;C :\u k u si\g ro z d .ja r

Kada p ro m en ljiv u CLASSfATH p o d esite kako treba, n ared n u d ato tek u m oete da stavite u bilo koji d irek to riju m :
/ / : p r is tu p / L ib T e s t .ja v a / / K o r i s t i b i b li o t e k u . import ne t.m in dview .sim ple .*; pu b lic class LibTest { pu b lic s t a t i c void m a in ( S tr in g [] args) { Vector v = new V e c to r (); L i s t 1 = new L i s t ( ) ;

}
} / * I s p is : net.m indview.sim ple.V ector net.mi nd vie w .sim p le .L ist

* ///:-

Poglavlje 6: Kontrola pristupa

165

Kada p revodilac naide na n a re d b u im port za biblio tek u simple, o n poinje da p retrauje d ire k to riju m e n av ed en e u prom enljiv o j CLASSPATH, traei p o d d ire k to riju m net\mindview\simple a zatim i prevedene dato tek e odgo v araju ih im en a (Vector.class za klasu Vector i List.class za klasu List). Z ap am tite da obe klase kao i p o tre b n e m eto d e u klasam a Vector i List m o raju da b u d u javne. Pravilno podeavanje p rom enljive CLASSPATH bio je veliki p ro b lem poetnicim a u Javi (meni takoe, kada sam poinjao), p a je SU N u kasnijim verzijam a Javeopam etio razvojno okruenje. Kada instalirate Javu, prim etiete da ete m oi da prevodite i pokreete osnovne p ro g ram e u Javi, ak i ako n e podesite prom enljivu CLASSPATH. M eutim , da biste preveli i izvravali izvorni k o d iz ove knjige (d o stu p an n a lokaciji w w w .M indV iew .net), prom enljivoj CLASSPATH m o raete d od ati osnovni direkto riju m stabla koda laijige. Veba 1: (1) N aprav ite ldasu u nekom p ak etu . N aprav ite p rim e ra k te Idase izvan tog paketa.

Dvosmislenost i sukobi imena


ta se deava ako se p rek o * uvezu dve biblioteke koje sadre ista im ena? N a p rim er, p retpostavim o da u p ro g ra m u pie:
import ne t.m in dview.sim ple .*; import j a v a . u t i l . * ;

Poto i java.util.* tak o e sadri klasu Vector, to m oe d a izazove sukob im ena. M eutim , dokle g od ne napiete k o d koji izaziva d vosm islenost, sve e b iti u red u to je do b ro , inae biste m o rali m n o g o da piete d a biste izbegli sukob koji se nik ad a nee d ogoditi. Sukob nastaje ako sada p o k u ate da n a p rav ite objekat klase Vector:
Vector v = new V e c to r () ;

Na koju se klasu Vector ovo odnosi? Prevodilac to ne m oe da zna, kao ni italac. Stoga se prevodilac ali i zahteva da b u d ete izriiti. Ako, na p rim er, elite sta n d a rd n i Javin Vector, m o ra te da napiete:
j a va .u ti1 .Vector v = new ja va .u ti l. Ve ct or ();

Poto ovo (zajedno s p ro m en ljiv o m CLASSPATH) p o tp u n o od re u je lokaciju klase

Vector, n ared b a import.java.util.* postaje n e o p h o d n a sam o ako hoete da koristite i neto d ru g o iz biblioteke java.util.
Sukobe im ena biste m ogli spreiti tako to ete uvesti sam o je d n u klasu u m esto celog paketa, ukoliko u istom p ro g ra m u ne u p o treb ite oba sukobljena im ena. (U to m sluaju, m orali biste da navedete njihova celo k u p n a, tj. p o tp u n o o d re e n a im ena.) Veba 2: (1) P retvorite delove koda u ovom o d eljk u u p ro g ram i uverite se da se sukobi zaista deavaju.

166

Misliti na Javi

Lina biblioteka sa alatkama


Poto sada sve ovo znate, u stanju ste da napravite svoje biblioteke sa alatkam a kako biste sm anjili ili ukinuli ponavljanje koda. N a prim er, pogledajte pseudonim za n aredb u System.out.println() koji sm o koristili da bism o sm anjili koliinu teksta koji m o ram o da piem o. Neka bu d e deo klase p o d im en o m Print, tako da na kraju dobijem o itljiv statiki uvoz:
/ / : n e t /m in d v i e w / u t il / P r i n t . ja v a / / Metode is p is iv a n j a koje se mogu u p o t r e b lja v a t i bez odreenja / / paketa i putanje, kada ih u Javu SE5 uvezemo kao s t a t i k e : package n e t . m in d v ie w . u t il; import j a v a . i o . * ; p u b lic class P r in t { / / I s p i i i prei u nov red: p u b lic s t a t i c void p r i n t (Object obj) { S y s t e m . o u t . p r i n t ln ( o b j ) ;

}
/ / Samo prei u nov red: pu b lic s t a t i c void p r i n t () { S y s t e m . o u t . p r i n t ln ( ) ;

}
/ / I s p i i bez prelaska u nov red: pu b lic s t a t i c void p rin tn b (Object ob j) { S y s t e m . o u t. p r i n t ( o b j) ;

}
/ / Nova metoda p r i n t f ( ) Jave SE5 (kao u C-u): p u b lic s t a t i c PrintStream p r i n t f ( S t r i n g format, O b je c t . . . argumenti) { return S y s t e m . o u t. p r in t f(f o r m a t, argumenti);

} )///O ve skraenice m oete da k o ristite za ispisivanje svega, bilo s prelaskom u novi red

(print()) ili bez prelaska u novi red (printnb()).


O va d ato tek a m o ra da se nalazi u d ire k to riju m u koji p o in je na nekoj lokaciji iz p ro m enljive CLASSPATH i zatim se nastavlja sa net/m indview. N akon prevodenja, statike m etod e print() i printnb() m o ete da ko ristite bilo gde na svom sistem u p o m o u n ared be irnport static:
/ / : p r is tu p / P r in t T e s t . ja v a / / K o r i s t i s t a t i k e metode za is p i s i v a n j e , koje pripadaju kla si / / P r in t . ja v a . import n e t.m in d v ie w .u ti1. P r i n t . * ; p u b lic class P rin tT e st { pu b lic s t a t i c void m a in (S trin g [] args) { print("D ostupna od sada pa n a d a lje ! " ) ; p r i n t ( lO O ) ;

Poglavlje 6: Kontrola pristupa

167

p rin t ( lO O L ) ; p r i n t ( 3 . 14159);

}
) / * I s p is : Dostupna od sada pa na dalje ! 100
100

3.14159

* ///:D ru g a k o m p o n e n ta ove b ib lio tek e m o g u b iti m eto d e range( ) koje ste u p o zn ali u poglavlju Kontrolisanje izvravanja. O n e om o g u av aju zadavanje jed n o stav n ih nizova celih b ro jeva fo reach sintaksom :
/ / : n e t/m in dview /util/R an ge .java / / Metode za p r a v lje n j e nizova koje se mogu u p o t r e b lja v a ti / / bez potpuno odreenih imena, k o r i s te i Java SE5 naredbe s t a t i c import: package n e t . m in d v ie w . u t il; p u b lic class Range { / / Napravi niz [ 0 . .n) p u b lic s t a t i c i n t [ ] ra n g e (in t n) { i n t [ ] r e z u lt a t = new i n t [ n ] ; f o r ( i n t i = 0; i < n; i++) re z u lta t[i] = i; return r e z u l t a t ;

}
/ / Napravi niz [p o e ta k.. k ra j) p u b lic s t a t i c i n t [ ] ran g e (in t poetak, i n t k r a j) { i n t sz = k raj - poetak; i n t [ ] r e z u lt a t = new i n t [ s z ] ; f o r ( i n t i = 0 ; i < sz; i++) r e s u l t [ i ] = poetak + i ; return r e z u l t a t ;

}
/ / Napravi niz [ s t a r t . . e n d ) uz inkrement korak pu b lic s t a t i c i n t [ ] ra n g e (in t poetak, i n t k r a j , i n t korak) { i n t sz = ( k r a j - poetak)/korak; i n t [ ] r e z u lt a t = new i n t [ s z ] ; f o r ( i n t i = 0; i < sz; i++) r e z u l t a t [ i ] = poetak + (i * korak); return r e z u l t a t ;

} } / / / =O d sad a m oete u svoju bib lio tek u d o d ati svaku k o risn u novu alatku kada god naiete na nju. B iblioteci n e t.m in d v ie w .u til do d av aem o k o m p o n e n te kroz celu knjigu.

1 68

Misliti na Javi

Korienje uvoenja za promenu ponaanja programa


U Javi nedostaje uslovno prevoenje iz C -a koje o m og uava d a p ro m e n o m v red n o sti nekog sem afora p ro m e n ite p o n aan je p ro g ra m a, bez d ru g ih izm en a u ko du . Takva m ogun o st je izostavljena iz Jave vero vatn o zbog toga to je u C -u najee ko rien a za reavanje p itan ja razliitih p latfo rm i: u zavisnosti o d p la tfo rm e za koju se p ro g ra m prevodi, prevode se razliiti delovi koda. Poto je p red v i e n o da Java a u to m atsk i p rav i p renosiv kod, takva m o g u n o st je sm a tra n a n e p o tre b n o m . Postoje, m e u tim , i d ru g e k orisne u p o tre b e uslov no g prev o enja. O n o se najee koristi za ispravljanje greaka u p ro g ra m u . Funkcije za o tk lan jan je greaka su u k lju en e u razvojnoj fazi, a u fin a ln o m p ro izv o d u su o n em o g u e n e. P ro m e n o m p aketa koji uvozite, b irate verziju za o tk lan jan je greaka ili verziju za isp o ru k u . O va teh n ik a m oe da se k oristi za bilo koju v rstu uslo v n o g prevoenja.

Veba 3: (2) N ap rav ite dva paketa: debug i debugoff koji sadre id e n ti n u kiasu s m eto d o m d eb u g( ). P rv a verzija p rik azuje n a k o nzoli svoj a rg u m e n t tipa String, a d ru g a ne rad i nita. U p o treb ite n a re d b u static im port da biste u p ro g ra m za ispitivanje uvezli tu klasu i pokaite na d elu u slovno prevoenje.

Upozorenje pri radu s paketima


Treba zap am titi sledee: svaki p u t kada p rav ite paket i d ate m u im e, im p lic itn o o d re u jete stru k tu ru d ire k to riju m a . Paket tnora d a se nalazi u d ire k to riju m u na koji uk azuje im e paketa i taj d ire k to riju m m o ra da b u d e d o stu p a n p retraiv an jem p ro m en ljiv e CLASSPATH. E ksperim entisanje rezervisanom rei p a ck ag e u p o etk u m oe da zb u n i jer e se, ako se ne drite p ravila o im en u paketa i im en u d ire k to riju m a , pojaviti m no tvo tajan stvenih greaka p ri izvravanju. Na p rim er, m oete d a d o b ijete p o ru k u kako nije bilo m o gue nai o d re e n u klasu i ako se o n a nalazi u teku em d ire k to riju m u . Ako d o bijete takvu p o ru k u o greci, stavite n ared b u pack ag e po d k o m e n ta r i ako p ro g ra m p ro ra d i, znaete gde Iei problem . Povedite rau n a o to m e da se prevedeni kod esto ne sm eta u d ire k to riju m u kojem je izvorni ko, ali izvrnom o k ru en ju Jave m o rate o m o g u iti da p revedeni kod p ro n a e p o m o u prom enljive CLASSPATH.

Specifikatori pristupa u Javi


Specifikatori p ristu p a (engl. access specifiers) u Javi su p u b lic , p ro te c te d i p riv a te . Stavljaju se ispred svake definicije svakog lana klase, bilo da je u p itan ju polje ili m etod a. Svaki specifikator p ristu p a k o n trolie p ristu p sam o p ojedino j definiciji. U koliko ne zadate specifikator p ristu p a, p o d razu m ev a se paketni pristup". Na jedan ili na drug i nain, za sve je definisana neka vrsta p ristu p a. U n ared n im odeljcim a, nauiete sve o razn im v rstam a p ristu p a.

Poglavlje 6: Kontrola pristupa

169

Paketni pristup
U svim p rim e rim a d o ovog poglavlja specifikator p ristu p a u o p te nije bio naveden. Za p odrazu m ev an i p ristu p ne p o sto ji rezervisana re, ali se o n o b i n o naziva p a k e tn i (katkada i ,,prijateljski ). To znai da sve ostale klase iz tekueg p ak eta im aju p ristu p to m lanu, ali sve klase van tog paketa taj lan v ide kao p riv a tn i (i n e m o g u da m u p ristu p e ). Poto jedinica za prevoenje - d ato tek a - m o e d a p rip a d a sam o je d n o m p ak etu , sve klase u n u tar nje a u to m atsk i su d o stu p n e jed n a d ru g o j, zato to im aju p ak etn i p ristu p . Paketni p ristu p om o g uav a da g ru piete sro d n e klase u je d a n pak et ta k o d a m o g u lako m e u so b n o da k o m u n iciraju . Kada klase grup iete u pak et, im e p a k e tn im lano vim a dozvoljavate m e u so b n i p ristu p , vi po stajete vlasnik" k o d a u to m p aketu. Im a logike u tom e d a sam o k o d u vaem vlasnitvu treb a d a im a p ak e tn i p ristu p o stalo m k o d u u vaem vlasnitvu. M oe se rei da p ak etn i p ristu p o p ravdava g ru p isa n je klasa u pakete. U m n o gim jezicim a, definicije m oete svakojako da organ izu jete, ali ste u Javi p risiljen i da ih organizujete n a razu m an nain . Pored toga, v ero v atn o ete odv ojiti klase koje n e tre b a da im aju p ristu p klasam a defin isan im u tek u em pak etu . Klasa k ontrolie odakle se m oe p ristu p a ti n jen im lano vim a. K od iz d ru g o g pak eta n e m oe da se pojavi, kae: Z dravo, ja sam Perin p rijatelj i oekuje da e d o b iti p ristu p zatienim , p ak etn im i p riv a tn im lan o vim a klase Pera. lan u se dozvoljava p ristu p sam o na sledee naine: 1 . C lan proglasite jav n im (p u b lic). Tada svako i sa svakog m esta m oe da m u p ristu p i. 2. lan u om og u ite paketni p ristu p izostavljajui bilo kakve specifikatore p ristu p a , a ostale klase stavite u isti paket s njim . Tada ostale klase to g paketa m o g u da p ristu p aju to m lanu. 3. Kao to ete videti u poglavlju Ponovno korienje klasa posv een om n asleivanju, p o to m a k klase m oe da p ristu p a i zatienim i jav n im lano vim a (ali ne i priv atn im lanovim a) roditeljske klase. lan o v im a s p a k etn im p ristu p o m m oe da p ristu p a saino ako su te dve klase u istom p ak etu . O nasleivanju i zatienim lan ovim a n em o jte sada da b rinete. 4 . O bezbedite m eto d e koje itaju i m enjaju eljenu v re d n o st (koje se tak o e nazivaju i m etode p ro itaj/po stav i - engl. get/set rnethods). U O O P -u je to n ajp am etn iji p ristu p i sutinski je znaajan za zrn a Jave, kao to ete videti u poglavlju Grafika korisnika okruenja.

public: interfejs za pristup


Kada u p o treb ite rezervisanu re p u b lic , oznaavate da je definicija lana koji sledi o d m ah iza nje d o stu p n a svim a, p o seb n o p ro g ra m e ru k lijentu koji k oristi biblioteku. P retp o stavim o da definiete paket d e se rt koji sadri sledeu d ato tek u:
/ / : p r is tu p / d e s e rt /K o la c ic . ja v a / / Pravi b ib lio t e k u package p r is tu p . d e s e rt ;

170

Misliti na Javi

pu b lic class Kolacic { pu b lic Kolacic() { S y ste m .o u t.p rin tln (" K o n s tru k to r klase K olacic1 1 );

}
void z a g r iz i( ) { S y s t e m . o u t . p r i n t l n ( " g r i c " ) ; }

} III--Z apam tite, d atoteka klase koju e n ap rav iti Kolacic.java m o ra da se nalazi u p o d d irek to riju m u desert d irek to riju m a pristup (ukazuje na poglavlje K ontrolapristupa ove knjige) koji m o ra da b u d e u n u ta r jed n o g o d d irek to riju m a d efinisanih u p rom enljivoj CLASSPATH. Pogreiete ako p om islite da e Java uvek da pogleda u tek u i d ire k to riju m kao je n u o d p o etn ih taaka za pretraivanje. Ako ne navedete taku kao je d n u o d p u ta n ja u svojoj prom enljivoj CLASSPATH, Java nee uzeti u razm atra n je tek u i d irek to riju m . Ako sada n ap rav ite p ro g ra m koji k oristi klasu Kolacic:
/ / : pristu p/Vecera.ja va / / K o r i s t i b i b li o t e k u . import p r i s t u p . d e s e r t . * ; pu b lic class Vecera { pu blic s t a t i c void m a in (S trin g [] args) { Kolacic x = new Kolacic ( ) ; S y ste m .o u t.p rin tln ("K o n s tru k to r klase Kolacic " ) ; / / ! x . z a g r i z i ( ) ; / / P ris tup n i j e mogu

}
} / * I s p is : Konstruktor klase Kolacic

* ///:m oete da nap rav ite objek at klase K olacic, je r je njegov k o n stru k to r javni i klasa je javna. (K oncept javnih klasa detaljnije em o razm o triti kasnije.) M ed u tim , m eto d a lanica zag rizi() nije d o stu p n a klasam a iz datoteke V ecera.java, jer m eto d a z a g rizi() o m oguava p ristu p sam o klasam a iz paketa d e se rt, pa e vas prevodilac spreiti da je u p o treb ite.

Podrazumevani paket
M oda e vas iznenaditi kada o tk rijete da e naredni p ro g ra m biti ispravno preveden iako na prvi pogled izgleda da kri pravila:
/ / : p r is tu p / T o r t a . ja v a / / Pristupa k la s i iz druge (zasebne) je d in ic e za prevoenje (dato teke). class Torta { pu b lic s t a t i c void m a in (S trin g [] args) { P ita x = new P i t a ( ) ; x .f 0 ;

}
} / * I s p is : P i t a . f ()

* ///:-

Poglavlje 6: Kontrola pristupa

171

D ruga d ato teka treb a da b u d e u isto m d irek to riju m u :


/ / : p r i s t u p / P it a . ja v a / / Druga klasa. class Pita { void f ( ) { S y s t e m . o u t . p r i n t l n ( " P i t a . f ( ) ' ' ) ; }

} ///:U p rv o m tre n u tk u , ove dve dato tek e m ogli biste d a p o sm a tra te kao p o tp u n o odvojene pa ip a k o b jek at klase Torta m oe d a n ap ra v i objek at klase Pita i d a pozove n je n u m e to d u f()! (V odite ra u n a o to m e d a u svojoj prom en ljiv o j CLASSPATH m o ra te im a ti ta k u da bi se ove datotek e p rav iln o prevele.) Pom islili biste d a klasa Pita i f() im aju p a k e tn i p ristu p i d a su stoga n e d o stu p n e klasi Torta. O n e imaju p ak etn i p ristu p taj d eo zakljuka je ispravan. D o stu p n e su klasi u dato teci Torta.java zato to se nalaze u isto m direktoriju m u i n em aju izriito im e paketa. Takve dato tek e Java tre tira kao deo p o d razu m ev an o g p aketa za taj d ire k to riju m i stoga om o g u av a p ak etn i p ristu p svim o stalim k lasam a u istom d ire k to riju m u .

private: to ne sme da se dira!


R ezervisana re private oznaava da nekom lanu niko ne sm e da p ristu p a osim klase koja sadri taj lan, i to u n u ta r n jen ih m eto d a. Takvi lanovi se zovu p riv atn i. D ru g e klase iz istog paketa ne m o gu da p ristu p a ju p riv atn im lanovim a, pa kao da ste izolovali kiasu. S d ru g e stran e, nije n eob in o da paket pravi nekoliko Ijudi, p a v am rezervisana re private o m o gu av a da slo b o n o m enjate taj lan bez brige kako e to da utie na d ru g e klase iz istog paketa. P odrazum ev an i paketni p ristu p esto obezbeuje d ovoljno sakrivanje; setite se, lan kojem se p ristu p a pak etno nije d o stu p a n p ro g ram eru k lijentu koji tu klasu upotrebljava. To je d o b ro , je r o bino i koristite p o d razu m ev an i p ristu p (a i njega dobijate ako zaboravite da d o a te neki specifikator p ristu p a). Stoga ete o b in o da razm iljate o p ristu p u lanovim a koje izriito elite da proglasite javnim i d o stu p n im p ro g ra m e ru klijentu. U poetku verovatno neete esto razm atrati u p o treb u rezervisane rei private, jer m oete da p ro ete i bez nje. Ispostavlja se, m e u tim , da je d osledna u p o treb a rezervisane rei private veom a vana, p o seb n o za vienitni rad (kao to ete videti u poglavlju Paralelno izvravatije). Siedi p rim e r u p o tre b e rezervisane rei private:
/ / : pris tu p /S la d o le d .ja v a / / Prikazuje rezervisanu re " p r iv a t e " . class SladoledSaVocem { p r iv a t e SladoledSaVocem() {} s t a t i c SladoledSaVocem napraviSladoledSaVocem() { r e tu rn new SladoledSaVocemf);

172

Misliti na Javi

public class Sladoled { public s ta tic void m ain(String[] args) { //! SladoledSaVocem x = new SladoledSaVocem(); SladoledSaVocem x = SladoledSaVocem.napraviSladoledSaVocem();

) } ///= P retho d ni p ro g ram predstavlja p rim e r kada rezervisana re private m oe zgodno da p oslui: m oete da poelite da kontro liete pravljenje objekta i da spreite nekog da d irektno p ristu p i o d re en o m k o n stru k to ru (ili svim k o n stru k to rim a). U p re th o d n o m p rim eru , objekat klase SladoledSaVocem ne m oete da nap rav ite preko k o n stru k to ra; um esto toga m o rate d a pozovete m eto d u napraviSladoledSaVocem() koja e to da u ra d i um esto vas.4 Svaku m eto d u , za k o ju ste sig u rn i d a je sam o ,,p o m o n a m e to d a te klase, m o ete da proglasite p riv a tn o m kako je n e b iste slu ajn o u p o tre b ili negde u p a k e tu - tako ete pred u p re d iti ev en tu aln u k asn iju iz m e n u ili izbacivanje te m etode. A ko m e to d u p roglasite p riv atn o m , tu m o g u n o st g ara n to v an o zadravate. Isto vai i za p riv a tn a p o lja u n u ta r klase. O sim k ad a osn o v n a realizacija m o ra d a b u d e vidljiva (to je m n o g o m an je v ero v atn o n ego to m oete da po m islite), sva polja treb a da proglasite p riv atn im . M e u tim , to to je referenca n a neki objekat p riv atn a u n u ta r klase, ne znai da neki d ru g i objek at ne m oe da im a jav n u referencu n a taj isti objekat. (Pseud o n im i su o b ra en i u d o d a cim a knjige d o stu p n im na M rei.)

protected: pristup nasleivanjem"


Da biste razum eli specifikator p ristu p a p ro te c te d , p o im o m alo u n ap re d . Kao prvo, pom irite se s tim da ne m o ra te da razu m ete ovaj deo da biste m ogli da nastavite d o nasleivanja (poglavlje Ponovno korienje klasa). Ali, opte slike radi, bie d a t k ratak opis i p rim e r korienja rezervisane rei p ro te c te d . Rezervisana re p ro te c te d o d n o si se na k o n cep t koji se naziva nasleivanje, u kom e se uzim a postojea klasa i njoj d o d a ju novi lanovi, bez izm ena na postojeoj klasi (koju em o zvati osnovna klasa). T akoe, m o ete da izm en ite p o n aan je postojeih lanica klase kada su u novoj klasi. Da b iste nasledili o d re e n u klasu, kaite da nova klasa pro iru je (engl. e.rtends) tu p o sto jeu klasu, na p rim er:
class Naslednik extends Osnova {

O statak definicije klase je isti kao i ranije. Ako n ap ravite nov paket i ako n asledite neku klasu iz d ru g o g paketa, im aete p ristu p sam o javn im lanovim a iz o rig in a ln o g paketa. (N aravno, ako nasleivanje u rad ite u istom paketu, m oi ete da p ristu p a te svim njegovim lan o v im a koji im aju paketni p ristu p .) P onekad a u to r o sn o v n e klase poeli da o m o g u i p ristu p o d re e n o m lan u iz izvedene klase, ali ne iz ostalih klasa. To se p o stie rezerv isan o m reju p ro te c te d . p ro te c te d daje i paketni p ristu p , tj. ostale klase u isto m pak etu m o g u p ristu p a ti zatienim (engl. protected) lanovim a.
U o v o m slu a ju n a sta je jo je d n a p o sle d ic a : p o to je d e fin isa n je d in o p o d ra z u m e v a n i k o n s tru k to r koji je p riv a tn i, n a sle d iv a n je klase n e e b iti m o g u e . (O to m e e kasn ije jo b iti rei).

Poglav[je 6: Kontrola pristupa

173

Ako p o n o v o ra z m o trite d a to tek u Kolacic.java, videete da sledea klasa ne m oe da p ristu p i lanu zag rizi( ) s p a k e tn im p ristu p o m :
//: pristup/CokoladniKeks.java // N ije mogu pristup lanu s paketnim pristupom u drugom paketu import pristup.desert.*; public class CokoladniKeks extends Kolacic { public CokoladniKeks () { System.out.println(''Konstruktor klase CokoladniKeks");

}
public void njam() { //! z a g r iz i(); // Nije mogu pristup metodi zagrizi

}
public s ta tic void m ain(String[] args) { CokoladniKeks x = new CokoladniKeks(); x.njam ();

}
} /* Isp is: Konstruktor klase Kolacic Konstruktor klase CokoladniKeks

* ///:Jedna o d vanih o so b in a n asleivanja je sledea: ako m eto d a zagrizi() po sto ji u klasi Kolacic, o n d a o na p o stoji i u svakoj klasi nasledenoj iz klase Kolacic. Ali poto m eto d a zagrizi() im a paketni p ristu p i nalazi se u d ru g o m pak etu , o n a n am nije d o stu p n a u tek u em paketu. M ogli biste, n arav n o , da je p rogiasite za javnu, ali bi o n d a svi im ali p ristu p , a to m o d a niste eleli. Ako klasu Kolacic izm en im o n a sledei nain:
//: pristup/kolacic2/Kolacic.java package pris tu p.kolacic2; publi c class Kolacic { public K o l a c i c O { Sy stem.out.println("Konstruktor klase Kolacic");

}
protected void zagrizi() { System.out.pri ntln ("gri c " ) ;

III--

sada je m eto d a zagrizi() d o stu p n a svim a koji nasled u ju klasu Kolacic:


//: pristup/CokoladniKeks2.java import pr istup.kolacic2.*; public class CokoladniKeks2 extends Kolacic { public CokoladniKeks2 () { Sy stem.out.println ("Konstruktor klase Co ko la dn iK ek s2) ;

174

Misliti na Javi

public void njam() { z a g r iz i(); } // Zatiena metoda public s ta tic void m ain(String[] args) { CokoladniKeks2 x = new CokoladniKeks2(); x.njam ();

}
} /* Isp is: Konstruktor klase Kolacic Konstruktor klase CokoladniKeks2 zagrizi

* ///:Povedite ra u n a o to m e d a zag rizi( ) d o d u e im a p ak etn i p ristu p , ali mjt javna.

Veba 4: (2) Pokaite da zatiene m e to d e im aju p a k e tn i p ristu p iako nisu javne. Veba 5: (2) N ap rav ite ldasu s jav n im , p riv a tn im , zatienim lanovim a i lanovim a
(poljim a i m e to d a m a ) s p a k e tn im p ristu p o m . N ap rav ite objekat te klase i p roverite kakve p o ru k e dobijate kada p o k u ate d a p ristu p ite svim lan o v im a klase. Im ajte u v id u da su klase u istom d ire k to riju m u d eo ,,p o d razu m ev an o g paketa.

Veba6: (1) N apravite klasu sa zatienim p o d a c im a . N apravite d ru g u klasu u istoj datoteci s m eto d o m koja ru k u je zatienim p o d ac im a u prvoj klasi.

Interfejs i realizacija
K ontrola p ristu p a se esto naziva i sakrivanje realizacije. Pakovanje p o d atak a i m etoda u n u ta rk la sa u k o m binaciji sa skrivanjem realizacije esto se naziva kapsuliranje.' Kao rezultat dobija se tip p o d atak a sa svojstvenim obelejim a i o d re e n im p o n aan jem . K ontrola p ristu p a n am ee o g ran ien ja u n u ta r tip a p o d a ta k a iz dva b itn a razloga. Prvi je da od red ite ta p ro g ram eri klijenti sm eju da koriste, a ta ne. In tern e m eh an izm e m oete slo b o d n o da u g rad ite u stru k tu ru , bez b rig e o to m e da e p ro g ram e ri klijenti sluajno koristiti in tern e delove interfejsa. To nas uvodi d ire k tn o u d ru g i razlog, a to je razdvajanje interfejsa i realizacije. A ko se stru k tu ra koristi u vie p ro g ra m a, a p ro g ram e ri klijenti sam o m ogu da alju p o ru k e javn o n i interfejsu, tad a m oete da m enjate sve to nije jav n o (znai sve to je prijateljsko, za.tieno ili p riv atn o ), a da ne po k v arite klijentske p ro g ram e. Radi boljeg razum evanja, kao stil pravljenja klasa m o ete usvojiti da na p o etk u stavite javne lanove, a zatim zatiene, p ak etn e i p riv atn e. P red n o st ovog naina je to to korisnik klase m oe d a k ren e o d v rh a i prvo vidi o n o to m u je b itn o (javne lanove, jer njim a m oe da p ristu p i izvan date datoteke) a zatim e se zau stav iti kada naide na ostale lanove koji su deo in tern e realizacije:
//: pri stup/RasporedPoNaci nuPri s t u p a .java public class RasporedPoNacinuPristupa { public void javl( ) { /* ... */ } public void jav2( ) { /* ... */ } public void jav3( ) { / * . . . * / } 5 P od k a p su lira n je m n e k i esto p o d ra z u m e v a ju s a m o s a k riv a n je realizacije.

Poglavlje 6: Kontrola pristupa

175

private private private private

void p r iv l( ) { / * . . . * / } void priv2( ) { / * . . . * / } void priv3( ) { / * . . . * / } ir t i ;

/ / } ///:O vo e sam o delim ino olakati itanje jer su interfejs i realizacija i dalje zajedno. O d n o sno, i dalje vidite izvorni ko d - realizaciju - je r se o n a nalazi tu , u klasi. Pored toga, dok um entacioni k o m en tari koje p odrava alatka Javadoc u m a n ju ju znaaj itljivosti prog ram a za prog ram era klijenta. Prikazivanje interfejsa k o risniku klase u stvari je posao itaa klasa (engl. class browser), alatke iji je zadatak d a nap rav i pregled svih raspoloivih klasa i pokae ta se s njim a korisno m oe u rad iti (tj. koje lanice su d o stu p n e). Prikazivanje d o k u m en tacije razvojnog okruenja za Javu p o m o u itaa W eba daje isti rezultat kao i ita klasa.

Pristup klasama
Specifikatori p ristu p a u Javi tak o e se m o g u u p o tre b iti d a o d red e koje e klase u n u ta r b iblioteke biti d o stu p n e n je n o m k o risn ik u. A ko elite d a klasa b u d e d o stu p n a p ro g ra m e ru kliientu, stavite rezervisanu re p u b lic n a elo definicije klase. T im e od re u jete d a li pro gram er klijent u op te m oe da n ap rav i objekat date klase. Da bi kon tro lisao p ristu p klasi, specifikator m o ra da se n a e pre rezervisane rei class. Stoga m oete d a napiete:
public class Spravica {

Ako je im e vae biblioteke p ris tu p , svaki p ro g ra m e r klijent m oe da p ristu p i klasi Spravica kada napie:
import pristup.Spravica;

import pristup.*;

M eu tim , postoje d o d a tn a ogranienja: 1. U svakoj jedinici za p rev o d en je (datoteci) m oe da p o stoji sam o jed n a javna klasa. Ideja je da svaka jedinica za prev o en je im a sam o jed an javni interfejs koji ta javna klasa predstavlja. Jedinica za prev o d enje m oe d a im a proizvoljan broj p o m o n ih klasa s p ak etn im p ristu p o m . A ko u n u ta r jed in ice za p revo en je im ate vie o d jed n e javne klase, prevodilac e p rijaviti greku. 2. Im e jav ne klase m o ra ta n o da se poklap a sa im en o m datoteke koja sadri jed in icu za prevoenje, uklju u ju i m ala i velika slova. Z nai, za klasu S p rav ica im e d ato teke m o ra da b u d e S p rav ica.jav a, nikako sp ra v ic a .ja v a ili SPR A V IC A .java. Ako se im ena ne slau, p o n o v o ete d o b iti p o ru k u o greci. 3. M ogue je, iako nije uo biajeno, da im ate je d in icu za p rev o en je u kojoj uopte nem a javne klase. U to m sluaju, d ato tek u m oete da nazovete kako god elite (iako e proizv o ljn o im enovanje zb u n iti o n e koji itaju i o d ravaju taj kod).

176

Misliti na Javi

ta ako u n u ta r b iblioteke pristup im ate klasu k o ju k o ristite sam o za zadatke koje izvrava klasa Spravica ili neka d ru g a javna klasa iz te biblioteke? N eete d a piete d o k u m e n tacije za p ro g ra m e ra k lijenta i m islite kako ete k asnije v ero v atn o h te ti d a p o tp u n o izm en ite stvari i celu tu klasu izbacite i zam e n ite je n ek o m novom . D a b iste zadrali tu m o g u n ost, m o ra te o sig u rati da n ijed an p ro g ra m e r klijen t ne p o sta n e zavisan o d o d re e n ih detalja realizacije skrivenih u n u ta r biblioteke pristup. Da biste to postigli, izostavite rezervisanu re public ispred klase, p ri em u o n a d o b ija p a k e tn i p ristu p . (Ta klasa m oe d a se k o risti sam o u n u ta r paketa.)

Veba 7: ( 1) N ap rav ite bib lio tek u pristup n a o sn o v u p re th o d n ih delova k oa koji o p isu ju n ju i klasu Spravica. Z atim n a p rav ite o b jek at klase Spravica u klasi koja nije d eo pak eta pristup.
K ada n ap rav ite k lasu s p ak etn im p ristu p o m , im a sm isla d a n jen a p o lja b u d u p riv a tn a p olja b i uvek treb alo d a b u d u p riv atn a ali je p o p rav ilu u p u tn o d ati m e to d a m a je d n ak i (p aketni) p ristu p k ao klasi. Poto se klasa s p ak e tn im p ristu p o m o b i n o u p o treb ljav a sam o u n u ta r svog pak eta, m eto d e takve klase tre b a da u in ite jav n im sam o ako m o rate , a to e v am rei prevodilac. Z ap am tite da klasa n e m oe da b u d e p riv a tn a (tim e bi postala d o stu p n a sam o sebi) n iti zatiena.6 Za p ristu p klasi, stoga, im ate sam o dva izbora: p ak etn i ili javni. A ko elite da nik o dru g i n e m a p ristu p nekoj klasi, sve k o n stru k to re m o ete da p roglasite p riv atn im , im e ete spreiti sve osim sebe da n aprave ob jek at te klase. (I vi ete to m oi sam o u n u ta r statin e lanice te klase.) Evo p rim era:
//: pristup/Rucak.java // Pokazuje specifikatore pristupa k la s i. Klasu pretvarate // u privatnu proglaavajui njene konstruktore privatnim: class Supal { private Supal() {} // (1) Dozvoljava pravljenje statikom metodom: public s ta tic Supal napraviSupu() { return new Supal( ) ;

}
class Supa2 { private Supa2() {} // (2) Napravi statin i objekat i vra ti referencu ako je zahtevano. // ("Sing ularni" projektni obrazac): private s ta tic Supa2 psl = new Supa2(); public s ta tic Supa2 pristupO { return psl;

}
public void f ( ) {}

"

U n u tra n ja (e n g l. inner) klasa m o c b iti p riv a tn a ili z ati e n a, ali to je s p ec ija la n sluaj. Te klase e b iti o b ja n je n e u p o g la v lju Unutranje klase.

Poglavlje 6: Kontrola pristupa

177

// Dozvoljena je samo jedna javna klasa u d atoteci: public class Rucak { void te s tP riv a te () { // Ovo ne moe da se uradi! Konstruktor je privatan: //! Supal supa = new SupalO;

}
void te s tS ta tic n i() { Supal supa = Supal.napraviSupu();

}
void te stSin g u la rn i() { Su p a 2 .p ristu p ().f();

} } ///:D o sada, veina m e to d a je vraala ili void ili p ro st tip, p a definicija:


public s ta tic Supal napraviSupuO { return new S u p a l();

} isprva m oe m alo da zbuni. Re Supal p re im ena m eto d e (napraviSupu) g o v o ri ta m etoda vraa. Do sada je u ovoj knjizi to najee bila re void, to znai da ne vraa nita. Ali tak o d e m oete da vratite i referencu na objekat, to se ovde i eava. M eto d a vraa referencu na objekat klase Supal. Klase S u p a l i S u p a2 pokazu ju kako se proglaavanjem k o n stru k to ra p riv a tn im sp reava d ire k tn o stv aranje objekata. Z apam tite, ako izriito ne n a p ra v ite n ijed an k o n stru k tor, au to m atsk i e biti n apravljen p o d razu m ev an i k o n stru k to r (k o n s tru k to r bez arg u m en ata). Ako sam i napiete p o d razu m ev an i k o n stru k to r, spreiete d a se o n a u to m atski napravi. U koliko ga zatim proglasite p riv atn im , niko nee m o i d a n ap rav i objekat te klase. Ali, kako e sada iko tu klasu d a koristi? P re th o d n i p rim e r p o k azu je dva n a in a . U S u p a l napravljena je statina m etoda koja pravi novi objekat klase S u p a l i v raa referencu na njega. To m oe da b u d e k o risn o ako elite da sa o b jek to m klase S u p a l u rad ite jo neke operacije pre nego to v ratite referencu ili ako elite da v o d ite ra u n a o broju naprav ljenih o b jekata kiase S u p a l (recim o da biste o graniili n jih o v u p o p u laciju ). U S u pa2 koristi se projektni obrazac, o em u vie m oete da p ro itate u knjizi T hinking in Patterns (w ith Java), na adresi w w w .M indV iew .net. U po treb ljen i o b razac u p rim e ru naziva se singularan", jer dozvoljava da b u de naprav ljen sam o je d a n objekat. O b jek at klase Supa2 je napravljen kao statini i priv atn i lan klase S upa2, pa p o stoji jed a n i sam o jed an i njem u m oete da p ristu p ite sam o preko javne m eto d e p ris tu p (). Kao to je ranije n a p o m e n u to , ako ne stavite nijedan specifik ato r p ristu p a za klasu, pod razu m ev a se da je u p ita n ju paketni p ristu p . To znai da b ilo koja d ru g a klasa u paketu m oe da n apravi ob jek at date klase, d o k o n e izvan paketa to ne m ogu. (Z a p a m tite da su sve datotek e koje se nalaze u istom d ire k to riju m u i koje n em aju izriitu p a k e tn u deklaraciju, eksplicitno svrstane u p o d razu m ev an i paket za taj d ire k to riju m .) A ko je, m e u tim , statini lan te klase javni, p ro g ram e r klijent jo uvek m oe da p ristu p a sta ti n im lanovim a, iako ne m oe da n ap rav i objekat te klase.

178

Misliti na Javi

Veba 8: (4) P ratei fo rm u iz p rim e ra Rucak.java, n ap rav ite klasu p o d im en o m UpravljacVezama koja u p rav lja fiksnim n izo m objek ata klase Veza. P ro g ram er k lijent ne sm e da b u d e u m o g u n o sti d a izriito p rav i objekte klase Veza; treb alo b i da ih d obija sam o prek o statin e m eto d e iz klase UpravljacVezama. Kada klasi UpravIjacVezama p o n estane objek ata, o n a treb a da vraa referencu null. Klase testirajte u m e to d i main(). Veba9: (2) N apravite sledeu d a to te k u u d irek to riju m u pristup/lokal (k o ja b i treb alo da je u vaoj p ro m en ljiv o j CLASSPATH):
///: pristup/lokal:UpakovanaKlasa.java package pr is tup.lokal; class UpakovanaKlasa { public UpakovanaKlasa () { System.out.print1n("Pravljenje upakovane klase1 ');

} } Z a tim na p rav ite sledeu d ato te k u u n ek o m d ru g o m d irek to riju m u :


///: pristup/strana/Strana.java package pristup.strana; import pristu p. lo ka l.*; public class Strana { public static void main (String [] args) { UpakovanaKlasa uk = new U p a k o v a n a K l a s a O ;

} } O b ja sn ite zato prevodilac p rijavljuje greku. Da li bi se ita p ro m e n ilo ako bi klasa

Strana p ostala deo paketa pristup.lokal?

Saetak
U svakoj vezi b itn o je da sve uklju en e stran e p o tu ju izvesne granice. Kada pravite biblio tek u , vi uspostavljate vezu s k o risn ik o m te biblioteke - p ro g ra m e ro m klijen to m - koji je tako e p ro g ram er, ali koji koristi vau b iblioteku da bi n ap rav io neku aplikaciju ili jo veu biblioteku. Kad ne bi bilo pravila, p ro g ra m e ri klijenti m ogli bi da rade ta im je volja sa svim lan o v im a klase, ak i k ada biste vi vie voleli da ne rade d irek tn o s nekim od njih. Sve bi bilo p o tp u n o izloeno. O vo poglavlje se o d n o silo na pravljenje klase da bi se fo rm irala biblioteka: prvo, na koji nain se klase p ak u ju u je d n u bib lio tek u i dru g o , nain na koji klasa kontrolie pristu p svojim lanovim a.

Poglavlje 6: Kontrola pristupa

179

P ro cen a je da pro jek ti pisani n a jeziku C p o in ju da se uru av aju kada d o stig n u izm e u 50 K i 100 K redova koda. C im a je in stv en im enski p ro sto r, p a im en a p o in ju d a se su d a ra ju te ih m o rate raspetljavati. U Javi, rezervisana re package, te h n ik a im en o v an ja pak eta i rezervisana re import, o b ezb e u ju p o tp u n u k o n tro lu n ad im e n im a , im e se p ro b le m sa su d ara n je m im en a lako zaobilazi. Postoje dva razloga za k o n tro lu p ristu p a lanovim a. Prvi je obezb ed iti da korisn ici d re ru k e podalje o d delova koje ne bi sm eli da diraju . Ti delovi su n e o p h o d n i za in te rn i ra d klase, ali nisu deo interfejsa koji je p o treb an p ro g ram e rim a k lijentim a. Z ato p ro g laavanjem m eto d a i p olja p riv atn im p rav ite uslugu p ro g ram e rim a k lijen tim a - lako m o g u da razlue ta je za njih b itn o , a ta m o g u d a ignoriu. Z bog to g a bolje ra z u m e ju klase. D ru g i i najvaniji razlog k o n tro le p ristu p a jeste o m o g u iti p ro je k ta n tu bib lio tek e da p ro m e n i u n u tra n ji izgled ldase b ez brige o to m e kako e to u ticati n a p ro g ra m e ra klijen ta . N a p rim e r, ldasu isprva n ap rav ite n a jed an nain , a zatim otk rijete kako e se rek o n stru k cijo m k oda b itn o u b rz ati izvravanje. Ako su interfejs i realizacija jasn o razd v o jen i i zatieni, to m oete da postig n ete bez p rim o ra v a n ja p ro g ra m e ra k lijenta da p o n o v o p iu svoje p ro g ram e. K ontrola p ristu p a osigurava d a n ijed an p ro g ra m e r k lijent ne p o sta n e zavisan o d realizacije koja se nalazi u osn o v i klase. K ada im ate m o g u n o st da m en jate o sn o v n u realizaciju, ne sam o da m o ete da u n a pre u je te projekat, ve i da prav ite greke. Bez ob zira na to koliko paljivo p lan ira te i p ro jek tu jete, naprav iete greke. Ako zn ate da greke nee izazvati m n o g o tete, biete vie rasp olo eni za eksperim ente, bre ete uiti i bre zavravati projekte. K orisnik vidi javni interfejs, pa je to, p rilik o m analize i p ro jek to v an ja p ro g ra m a , najvaniji deo klase koji treba da u rad ite kako valja. ak vam je i tu d ata izvesna slo b o d a da m en jate. A ko interfejs ne ,,pogodite iz prve, uvek m oete da dodate d ru g e m eto d e, dokle god ne izbacite neke koje je p ro g ram e r klijent ve u p o treb io u svom p ro g ra m u . O b ra tite pan ju na to da se kon tro la p ristu p a u sred sre u je na o d n o s - i n ek u v rstu k o m u n ik a c ije - izm eu au to ra biblioteke i n jenih spoljnih klijenata. Im a m n o g o situacija u kojim a to nije sluaj. P rim era radi, sam i piete sav kod ili rad ite zajedno s m a lo m ekip o m i sve to napiete ide u isti paket. U tim situacijam a p o tre b n a je drugaija k o m u n ikacija, pa k ru to prid ravan je pravila p ristu p a m oe da sm eta. Tada bi p o d ra zu m e v a n i (p ak etn i) p ristu p m ogao da b u d e o p tim alan .
Reenja o d a b r a n i h vebi data su u e l e k tr o n sk o m d o k u m e n t u Thinking Iti Java A nnotated Solution Guide, koji se m o e kupiti na Iokaciji www.M indView.net

Ponovno korienje klasa


Jedna od najlepih m ogunosti u Javijeste ponovno korienje koda. Ali, da biste u tom p o slu bili revolucionarni, sa kodom m orate u m eti da uradite m nogo vie od p u ko g kopiranja i menjanja. U PRO C ED U RA LN IM JEZICIM A KAO TO JE C , K O D SE PO N O V O KORISTI TAKO STO SE KOPIRA i izm eni, ali se takav p ristu p i nije ba n ajb o lje p okazao. Kao i sve ostalo u Javi, reenje se v rti oko klase. K od p o n o v o k o ristite tak o to p rav ite n o v e klase ali, u m e sto da ih p ravite ,,od n u le , uzim ate postojee klase koje je n eko ve n a p ra v io i o istio o d greaka. Reenje je d a se u p o tre b e klase b ez m e n ja n ja p ostojeeg k o d a. U o v o m poglavlju n au iete dva n ain a d a to p o stig n ete. Prvi je p rili n o jasan: p ra v ite objekte postojee klase u n u ta r nove klase. To se naziva kom pozicija, je r je n o v a klasa sastavljena o d o b jekata p o stojeih klasa. P onovo se k o risti fu n k c io n aln o st koda, a n e njegova fo rm a. D ru g i p ristu p je su p tiln iji. N ova klasa se p rav i kao tip p o sto jee klase. Vi b u k v aln o uzim ate o b lik postojee klase, d o d ajete jo j k o d i n ita n e m e n ja te n a njoj sam oj. O va teh n ik a se naziva nasleivanje i p rev o d ilac tu obavlja vei d eo posla. N asleivanje (engl. inheritance) predstavlja jed a n o d k a m e n a tem eljaca o b je k tn o o rije n tisan o g p ro g ram ira n ja i p o d raz u m ev a jo neke p o stu p k e koji e b iti istraen i u poglavlju Polimorfizam. Ispostavlja se d a je d o b a r d eo sin tak se i o so b in a k o m p o z i je i nasleivanja slian (to im a sm isla, je r se oba k o riste za pravljenje n o v ih tipova o d p o sto jeih ). U ovom poglavlju uiete o tim m e h a n izm im a p o n o v n o g korienja koda.

Sintaksa kompozicije
D o ovog m esta u knjizi esto sm o k oristili k o m p o ziciju - sm etan je referenci na objekte u n u ta r nove klase. P retp o stav im o , na p rim e r, da elite da n a p rav ite objekat koji uva nekoliko o bjekata tip a S trin g , nekoliko p ro stih tip o v a i o b jek at neke d ru g e klase. Kod sloenih tipova, u n u ta r nove klase stavljate reference, d o k p ro ste tipove d ire k tn o definiete:
//: ponovnaupotreba/Prskalica.java // Ponovna upotreba koda kompozicijom.

class Izvor { private String s; Izvor() { System.out.pri n t ln("Izvor()"); s = "Konstruisan";

}
public String t o S t r i n g O

{ return s; }

}
public class Prskalica { private String ventill, ventil2, ventil3, ventil4; private Izvor izvor = new Izvor();

Poglavjje 7: Ponovno korienje klasa

181

private int i ; private float f; public String t o S t r i n g O return {

"ventill = " + ventill + " " +


"venti!2 = " + ventil2 + " 1 1 + "ventil3 = " + ventil3 + " " + "ventil4 = " + ventil4 + "\n" + "i = 1 1 + i + " " + "f = " + f + " " + "izvor = " + izvor;

}
public static void main(String[] args) System.out.println(prskal i c e ) ; { Prskalica prskalice = new Prskalica();

}
} /* Ispis: Izvor() ventill = null ventil2 = null ventil3 = null ventil4 = null i = 0 f = 0.0 izvor = Konstruisan

* ///:Jedna o d m eto d a d efin isan ih u o b e ldase je p o seb n a: to je m e to d a toString(). Svaki objekat im a m e to d u toStringO koja se poziva u p o seb n im situacijam a, ako prev o d io cu treb a objekat klase String, a u m esto toga m u je p ro sle en neki d ru g i objekat. Stoga u izrazu u Prskalica.toString():
"izvor = " + izvor;

prevodilac p rim eu je kako p okuavate da na objek at tipa String ("izvor = ") nadoveete objekat tipa Izvor. Poto na objek at tip a String m o ete da nadoveete sam o d ru g i takav olijekat, o n kae: pretv oriu Izvor u tip String po ziv an jem m e to d e toString(). N akon toga, o n m oe da k o m b in u je dva o b jek ta klase String i da rezultat pro sled i m eto d i System.out. println() (ili p re th o d n o u knjizi d efin isan im statin im m eto d a m a print() i printnb()). Kad god elite da klasi koju p rav ite o m o g u ite takvo p o n aan je, sam o nap iite m e to d u toString(). Prom enljive pro stog tipa koje su polja klase, au to m a tsk i su inicijalizovane v rednou nula, kao to je n a p o m e n u to u poglavlju Sve je objckat. M e u tim , reference na objekte inicijalizovane su v red n o u null i ako preko bilo koje od tih referenci p o k u ate da pozovete neku m eto d u , izazvaete izuzetak - greku to k o m izvravanja. D o b ro je to ipak m oete da ispiete null referencu, a da se p ri to m ne javi izuzetak. Im a sm isla to to prevodilac ne pravi p o d razu m ev a n i objek at za svaku referencu, je r bi to u m n o g im sluajevim a izazvalo n ep o tre b n e reijske trokove. Ako elite da reference b u d u inicijalizovane, to m o rate da u rad ite sam i: 1. Na m estu gde se objekti definiu. To znai d a e uvek biti inicijalizovani p re nego to k o n stru k to r b u d e pozvan. 2. U k o n stru k to ru te klase.

182

Misliti na Javi

3. N eposredno p re nego to v am je zaista taj o bjek at p o tre b a n . To se esto naziva i lenja inicijalizacija. O n a m o e d a sm an ji reijske troko ve o n d a k ad a je pravljenje objekta skupo, a ne treb a ga p ra v iti svaki p u t. 4. Inicijalizacijom instance (p rim erk a ). Sva etiri p ristu p a p rik azan a su ovde:
//: ponovnaupotreba/Kada.java // Inicijalizacija u konstruktoru i kompozicija. import static ne t.mindview.util.Print.*; class Sapun { private String s; Sapun() { print{"Sapun()"); s = Konstruisan";

}
public String t o S t r i n g O

{ return s; }

}
public class Kada { private String // Inicijalizacija na mestu definicije: s 1 = "Srean", s2 = "Srean", s3, s4; private Sapun sapuncic; private int i; private float igracka; public Kada() { print("Unutar klase Kada()"); s3 = "Radostan"; igracka = 3 .14f; sapuncic = new Sapun();

}
// Inicijalizacija instance (primerka): { i = 47; } public String toStringO { if(s4 == null) // Odloena inicijalizacija: s4 = "Radostan"; return " sl = " + sl + "\n" + "s2 = " + s2 + "\n" + "s3 = " + s3 + "\n" + "s4 = " + s4 + "\n" + "i = " + i + "\n" + "igracka = 1 1 + igracka + "\n" + "sapuncic = " + sapuncic;

}
public static void main(String[] args) { Kada b = new Kada();

Poglavlje 7: Ponovno korienje klasa

183

p rin t(b ); }
} /* Ispis: Unutar klase Kada() Sapun() sl = Srean s2 = Srean s3 = Radostan s4 = Radostan i = 47 igracka = 3 . 1 4 sapuncic = Konstruisan

* ///:O b ra tite p an ju na to da se u k o n stru k to ru klase K ad a n a re d b a izvrava p re nego to se u ra d i bilo koja inicijalizacija. Kada inicijalizaciju ne u ra d ite n a m e stu definicije, n e p ostoji g arancija da e objekat ili referenca biti inicijalizovani p re nego to im poaljete p oru k u - izuzev neizbenog izuzetka p rilik o m izvravanja. Kada se pozove to S trin g O , o n p o p u n ja v a s4 tak o d a su sva p o lja p rav iln o inicijalizovana u tre n u tk u njihovog korienja. Veba 1: (2) N apravite jed n o stav n u klasu. U n u ta r d ru g e klase definiite referencu objekta prve klase. Za in stan ciranje tog objekta k o ristite len ju in i jalizaciju .

Sintaksa nasleivanja
N asleivanje je sastavni deo Jave (i u o p te o b jek tn o o rijen tisa n ih jezika). U stvari, kada pravite klasu, ispostavlja se da uvek p rim en ju jete nasleivanje. A ko eksplicitno ne nasledite neku klasu, im plicitno ete n aslediti favinu p o d ra z u m e v a n u ko ren sk u kiasu O b ject. S intaksa kom pozicije je oigledna, ali se za n asleivanje up otreb ljav a p o seb n a sin tak sa. Pri nasleivanju kaete nova klasa ie kao ta sta ra klasa. U p ro g ra m u to navodite pre p o etn e vitiaste zagrade tela klase, piui rezervisanu re e x te n d s iza koje sledi im e osnovnc kluse. Kada to uinite, au to m atsk i p reu zim a te sva polja i m eto d e o sn o v n e klase. Evo p rim era :
//: ponovnaupotreba/Deterdzent.java // Sintaksa i osobine nasleivanja. import static net.mindview.util.Print.*; class Cistac ( private String s = "Cistac"; public void dodaj(String a) { s += a; } public void razredi() { dodaj(" razredi()"); } public void sipaj() { dodaj(" sipaj()"); } public void ribaj() { dodaj(" ribaj()"); } public String toStringO { return s; } public static void main(String[] args) { Cistac x = new Cistac();

184

Misliti na Javi

x .ra z re d i(); x .s ip a j(); x .r ib a j ( ) ; p r in t(x );

} }
public class Deterdzent extends Cistac { // Promeni metodu: public void rib a j() { dodaj(" D eterdzent.ribaj( ) " ) ; su p e r.rib aj( ) ; // Poziva verziju iz osnovne klase

}
// Dodaje metode k la si: public void zapeni() { dodaj(" z a p e n i()"); } // Testira novu klasu: public s ta tic void m ain(String[] args) { Deterdzent x = new Deterdzent(); x .razred i( ) ; x .s ip a j( ) ; x .rib aj ( ) ; x.zapeni( ) ; pri nt (x ); p rin t("T e stira n je osnovne k la s e :"); Cistac.m ain(args);

}
} /* Isp is: Cistac razrediO s ip a j() Deterdzent. ribaj () r ib a j() zapeni() Testiranje osnovne klase: Cistac razrediO s ip a j() r ib a j()
*

III--

O vim se p o kazuje vie oso b in a. Prvo, u m eto d i dodaj klase Cistac, objekti klase String se nadovezuju na sadraj p ro m en ljiv e s p o m o u o p e ra to ra + = . Taj o p e ra to r (kao i o p erato r + ) au to ri Jave su ,,preklopili tako da rad i i sa o b jek tim a klase String. D rugo, obe klase, Cistac i Deterdzent sadre m eto d u inain(). Svaka o d vaih klasa m oe d a sadri in eto d u main(); teh n ik o m stavljanja m eto d e main() u svaku klasu, o m o guavate lako testiran je svake klase. Kada ga zavrite, m e to d u main() ne m o rate da uklanjate; m oete d a je o stavite za kasnije testiranje. I kada im ate p u n o klasa u p ro g ra m u , bie pozvana sam o m eto d a main() klase p o zvane s k o m a n d n e linije. Stoga e u ovom sluaju biti pozvana m etoda Deterdzent.main() ako napiete java Deterdzent. M e u tim , tak o e m oete da napiete java Cistac im e pozivate m e to d u Cistac.main(), iako klasa Cistac nije javna. ak i ako klasa im a paketni p ristu p , javn a m eto d a main() je d o stu p n a . U ovom sluaju o b ra tite p an ju na to da m eto d a Deterdzent.main() izriito poziva m e to d u Cistac.main(), p ri em u joj p ro sled u je iste a rg u m en te koje je sam a d o b ila s kom a n d n e linije (u p rin c ip u , m oete da pro sled ite bilo koji niz elem en ata tipa String). Vano je d a sve m eto d e klase Cistac b u d u javne. Setite se da se, ako izostavite specifik a to r p ristu p a, p o d raz u m ev a d a lan im a p ak etn i p ristu p koji dozvoljava p ristu p sam o lanov im a istog paketa. Stoga bi, ako ne navedete specifikator p ristu p a, svaki lnn tog paketa m ogao da koristi te m etode. Za klasu Deterdzent, na prim er, ne bi biio problem a.

Poglavlje 7: Ponovno korienje klasa

185

M e u tim , ako bi n ek a klasa iz nekog d ru g o g pak eta nasledila klasu Cistac, o n a b i m ogla da p ristu p a sam o n je n im jav n im lanovim a. D akle, da biste om oguili nasleivanje klase, opte p rav ilo je da sva polja b u d u p riv atn a, a sve m eto d e javne (izvedena klasa tak o e m oe da p ristu p a zatienim lanovim a; o to m e neto kasnije). N aravno d a u o d re en im sluajevim a m o ra te u in iti izvesna prilag o av an ja, ali ovo je k o risn a sm ernica. Klasa Cistac im a g ru p u m eto d a u svom interfejsu: dodaj(), razredi(), sipaj(), ribaj() i toString(). Poto je klasa Deterdzent izvedena iz klase Cistac (p o m o u rezervisane rei extends), njen interfejs au to m atsk i sadri i navedene m eto d e, iako ne v id ite d a su o n e u njoj izriito definisane. Z nai d a nasleivanje m o ete d a p o sm a tra te kao p o n o v n o korienje klase. Na p rim e ru m eto d e ribaj() v id i se d a je m og ue m en jati m e to d u koja je ve bila definisan a u o sn o vno j klasi. U ovom sluaju, iz n ove verzije je p ozv an a i m eto d a iz osno v n e klase. M e u tim , u m e to d i ribaj() nije ispravno d a se pozove sam o m eto d a ribaj(), je r bi to b io rek u rziv an poziv, a to niste n am eravali. D a b i reila ovaj p ro b lem , Javina rezervisana re super oznaava natk lasu (engl. superclass ) koju tek u a klasa nasleuje. Stoga n aredba super.ribajO poziva verziju m eto d e ribaj() iz o sn o v n e klase. Pri n asleivanju ne m o ra te ko ristiti sam o m eto d e o sn o v n e klase. Izvedenoj klasi m o ete d a dod ajete nove m eto d e na isti nain na koji b iste to radili i s bilo k o jo m d ru g o m klasom : definiite ih. P rim e r za to je m etod a za p e n i(). U m eto d i Deterdzent.main() v idite da preko objekta klase Deterzent m o ete da p o zivate m eto d e koje su raspoloive za klasu Cistac i o n e za klasu Deterdzent (na prim er,

zapeni()).
Veba 2: (2) Iz klase D e te rd z e n t nasledite novu klasu. Redefiniite m eto d u rib a j(), a d o dajte i n o v u m e to d u p o d im e n o m ste rilisi().

Inicijalizacija osnovne klase


Poto su sada u p riu ukljuene dve klase - o sn o v n a i izvedena - u m esto sam o jed n e, p o m alo zb u n ju je p o m isa o na to kako izgleda objckat koji se d ob ija o d izvedene klase. Spolja izgleda kao d a Tiova klasa im a isti interfejs kao o sn o v n a klasa, m oda uz p o n e k u d o d a tn u m eto d u ili polje. Ali nasledivanje ne kopira sam o interfejs o sn ov ne klase. Kada n apravite objekat izvedene klase, on sadri podobjekat (engl. subobject) osn ov ne klase. Taj p o d o b jekat izgleda isto kao da ste napravili sam objekat osn o v n e klase. Jedino je, spolja gledano, p o d o b jek at o sn o v n e klase u pakovan u objek at izvedene klase. N aravno, n e o p h o d n o je da podobjekat osnovne klase b u de pravilno inicijalizovan i postoji sam o jedan nain da se to i garantuje: da se inicijalizacija obavi u k o n stru k to ru p ozivanjem k o n stru k to ra osnovne klase. Taj k o n stru k to r im a sve p o tre b n o znanje i privilegije da inicijalizuje o sn ovn u klasu. Java auto m atsk i dodaje poziv k o n stru k to ra o snovne klase u k o n stru k to r izvedene klase. N aredni prim er pokazuje rad s tri nivoa nasleivanja:
//: ponovnaupotreba/Karikatura.java // Poziv konstruktora pri nasleivanju. import s ta tic net.m indview .util.P r in t.* ;

186

Misliti na Javi

class UmetnickoDelo { UmetnickoDelo() { print("Konstruktor klase UmetnickoDelo"); }

}
class Crtez extends UmetnickoDelo { Crtez() { print("Konstruktor klase C rtez");

}
public class Karikatura extends Crtez { public KarikaturaO { print("Konstruktor klase Kari k atu ra"); public s ta tic void m ain(String[] args) { Karikatura x = new K a rik a tu ra ();

}
} /* Isp is: Konstruktor klase UmetnickoDelo Konstruktor klase Crtez Konstruktor klase Karikatura

III-V idite da se k o n stru k cija obavlja o d osn o v n e ka izvedenim klasam a, pa je osn o v n a klasa inicijalizovana p re nego to k o n stru k to r izvedene klase m oe da joj pristu p i. ak i ako za klasu Karikatura n e n a p ra v ite k o n stru k to r, prevodilac e um esto vas n apraviti p o d ra zum ev an i k o n stru k to r koji e pozv ati k o n stru k to r o sn o v n e klase.

Veba3: (2) D okaite p re th o d n u tv rd n ju (da prevodilac pravi p o d razu m ev an e k o n stru k tore u m esto vas).

Veba4: (2) D o kaite da se k o n stru k to ri osn o v n e klase (a) uvek pozivaju i (b) pozivaju
pre k o n stru k to ra izvedene klase.

Veba 5: (1) N ap rav ite dve klase, A i B, sa p o d ra z u m e v a n im k o n stru k to rim a (bez arg u m en ata) koje javljaju svoje p o sto jan je. Iz klase A nasledite n o v u klasu C i u njoj n apravite objekat lan Idase B. N e m o jte da p rav ite k o n stru k to r klase C. N apravite objekat te klase i
p o sm atrajte rezultate.

Konstruktori sa argumentima
U p re th o d n o m p rim e ru su k orieni p o d ra zu m e v a n i k o n stru k to ri; o d n o sn o , oni nisu im ali nikakve arg u m e n te . P rev o d io cu je lako da ih poziva, je r nem a su m n je koje arg u m ente treb a p roslediti. A ko o sn o v n a klasa n em a p o d raz u m e v a n i k o n stru k to r ili ako elite da pozovete k o n stru k to r o sn o v n e klase kom e su p o tre b n i a rg u m e n ti, m o rate izriito pozvati k o n stru k to r o sn o v n e klase koristei rezervisanu re s u p e r i proslediti m u odgovarajuu listu arg u m en ata:
//: ponovnaupotreba/Sah.java // Nasleivanje, konstruktori i argumenti. import s ta tic net.m indview.uti1 .P r in t .*;

Poglavlje 7: Ponovno korienje klasa

187

class Igra { Ig ra (in t i) { printC'Konstruktor klase Ig ra ");

} }
class IgraNaTabli extends Igra { IgraN aTabli(int i ) { su p e r(i); print("Konstruktor klase IgraNaTabli" ) ;

} }
public class Sah extends IgraNaTabli { Sah() { s u p e r (ll); print("Konstruktor klase Sah");

}
public s ta tic void m ain(String[] args) { Sah x = new Sah ( ) ;

}
} /* Isp is: Konstruktor klase Igra Konstruktor klase IgraNaTabli Konstruktor klase Sah

* ///:Ako u k o n stru k to ru klase Ig raN aT ab li() ne pozovete k o n stru k to r o sn o v n e klase, p revodilac e se aliti da ne m oe da p ro n a e k o n stru k to r oblika Ig ra(). Pored toga, prvo se mora pozvati k o n stru k to r osn o v n e klase, i to n ajp re m orate da u rad ite u k o n stru k to ru izvedene klase. (Prevodilac e vas p odsetiti ako pogreite.) V eba 6: ( 1) P om ou dato tek e S ah .jav a d okaite tv rd n je iz p re th o d n o g pasusa. V eba 7: ( 1) Izm enite vebu 5 tako da A i B u m esto p o d razu m ev a n ih k o n stru k to ra im aju k o n stru k to re sa a rg u m e n tim a. N apiite k o n stru k to r klase C i svu inicijalizaciju obavite u n u ta r njega. V eba 8: ( 1) N apravite o sn o v n u klasu koja im a sam o jed an n ep o d razu m ev an i k o n stru k to r i izvedenu klasu koja im a i p o d razu m ev an i i n ep o d razu m ev an i k o n stru k to r. Pozovite k o n stru k to r osno v ne klase iz k o n stru k to ra izvedene klase. Veba 9: (2) N apravite klasu p o d im en o m K oren koja sadri p o jed n u instancu klasa (koje takod e treba da napravite) p o d im enim a K o m p o n e n ta I, K o m p o n en ta2 i K o m p o n en ta3 . Iz klase K oren izvedite klasu S tab ljik a koja takode im a po instancu svake k om ponente. Svaka klasa treba da im a p o d razu m ev an i k o n stru k to r koji ispisuje p o ru k u o njoj. Veba 10: ( 1) Izm enite p re th o d n u vebu tak o da svaka klasa im a i n ep o d razu m ev an i konstru k to r.

188

Misliti na Javi

Delegiranje
Java ne podrava d ire k tn o trei o d n o s koji se naziva delegiratije. To je neto na pola p u ta izm eu nasleivanja i k om pozicije, p o to u klasu k o ju p ra v ite sm etate o b jek at lan (kao kom pozicija), ali u novoj klasi isto v rem en o ek sp o n ira te sve m eto d e to g objekta lana (kao nasleivanje). N a p rim er, svem irskom b ro d u treb a m o d u l za upravljanje:
//: ponovnaupotreba/UpravljakiUreajiSvemirskogBroda.java public class UpravljakiUreajiSvemirskogBroda void navie(int brzina) void nanie(int brzina) void nalevo(int brzina) void nadesno(int brzina) void napred(int brzina) void nazad(int brzina) {} {} {} {} {} {} {} {

void turboPotisak(int brzina)

} // / = ~ Jedan o d nain a d a se n a p ra v i svem irski b ro d bilo b i nasleivanje:


//: ponovnaupotreba/SvemirskiBrod.java public class SvemirskiBrod extends UpravljakiUreajiSvemirskogBroda private String ime; public SvemirskiBrod(String ime) { this.ime = ime; } public String to S trin g () { return ime; } public s ta tic void m ain(String[] args) { SvemirskiBrod t i t = new SvemirskiBrod("PVO t i t " ) ; tit.napred(lO O ); {

} } ///:N aravno, S v em irsk iB ro d u stvari n ije jed an od o bjekata klase U prav Ijak iU re ajiS ve m irsk o gB ro d a, ak i ako o bjektu klase S v em irsk iB ro d , p rim e ra radi, m oete narediti" da ide n a p red (). Tanije je rei da S v em irsk iB ro d sadri U p rav Ijak iU re ajiS v em irsk o g B roda. Istovrem eno, u o b jek tu ldase S v em irsk iB ro d ek sp o n iran e su sve m eto d e objekta klase U prav ljakiU red ajiS v em irsk o g B ro d a. O vu n ed o u m icu reava delegiranje:
//: ponovnaupotreba/DelegiranjeSvemirskogBroda.java public class DelegiranjeSvemirskogBroda { private String ime; private UpravljakiUredajiSvemirskogBroda upravljakiUreaji = new Upravl jakiUreajiSvemirskogBroda(); public DelegiranjeSvemirskogBroda(String ime) { this.ime = ime;

}
// Delegirane metode: public void nazad(int brzina) {

Poglavlje 7: Ponovno korienje klasa

189

upravljakiUreaji.nazad(brzina);

}
public void nanie(int brzina) { upravljakiU reaji.nanie(brzina);

}
public void napred(int brzina) { upravljakiUreaji.napred(brzina);

}
public void nalevo(int brzina) { upravljakiU reaji.nalevo(brzina);

}
public void nadesno(int brzina) { upravljakiUreaji.nadesno(brzina);

}
public void turboPotisak(int brzina) { up ravljakiUreaji.turboPotisak(brzina);

}
public void navie(int brzina) { upravljakiU reaji.navie(b rzina);

}
public s ta tic void m ain(String[] args) { DelegiranjeSvemirskogBroda t i t = new DelegiranjeSvemirskogBroda("PVO t i t " ) ; tit.napred(lO O );

III---

V idi se kako su m etode pro sle en e p rip a d n o m o b jek tu tip a u p ra v lja k iU re a ji, pa je interfejs zato je d n a k kao pri nasleivanju. M e u tim , deleg iran je o m o g u u je vie k o n tro le, p oto u objekat lan m oete p ren eti proizvoljan p o d sk u p svih m etoda. Iako Java ne podrava delegiranje, im a m n o g o razv o jn ih alatki koje ga p odravaju. Recim o, gornji p rin ie r je au to m atsk i g enerisan p o m o u razvojnog o k ru en ja JetB rains Idea. V eba 11: (3) Izm enite D e te rd z e n t.ja v a tako da se u njoj koristi delegiranje.

Kombinovanje kompozicije i nasleivanja


K om pozicija i nasleivanje se esto koriste zajedno. N ared n i p rim e r po k azu je pravljenje sloenije klase, korienjem kom pozicije i nasledivanja, kao i n e o p h o d n e inicijalizacije k o n stru k to rim a:
//: ponovnaupotreba/PostavkaStola .java // Kombinovanje kompozicije i nasleivanja. import s ta tic net.m indview.util.P r in t.* ; class Tanjir { T a n jir(in t i) { print("Konstruktor klase T a n jir");

190

Misliti na Javi

class TanjirZaVeceru extends T anjir { TanjirZaVeceru(int i) { su p er(i); print("Konstruktor klase TanjirZaVeceru");

} }
class Pribor { Prib o r(in t i) { print("Konstruktor klase P rib o r");

} }
class Kasika extends Pribor { Kasika(int i ) { su p er(i); print("Konstruktor klase Kasika");

} }
class Viljuska extends Pribor { V ilju sk a(in t i) { super(i) ; print("Konstruktor klase V ilju s k a ");

} }
class Noz extends Pribor { Noz(int i ) { s u p e r(i); print("Konstruktor klase Noz");

} }
// Kulturan nain da se neto uradi: class Obicaj { O bicaj(int i) { print("Konstruktor klase O b icaj");

public class PostavkaStola extends Obicaj { private Kasika ks; private Viljuska v i l j ; private Noz nz; private TanjirZaVeceru tv ; public PostavkaStola(int i) { super(i + 1); ks = new Kasika(i + 2); v i l j = new V ilju sk a (i + 3); nz = new Noz(i + 4); tv = new TanjirZaVeceru(i + 5); print("Konstruktor klase PostavkaStola");

Poglavlje 7: Ponovno korienje klasa

191

}
public s ta tic void main(String[] args) { PostavkaStola x = new PostavkaStola(9);

}
} /* Isp is: Konstruktor Konstruktor Konstruktor Konstruktor Konstruktor Konstruktor Konstruktor Konstruktor Konstruktor Konstruktor
* ///:-

klase klase klase klase klase klase klase klase klase klase

Obicaj Pribor Kasika Pribor Viljuska Pribor Noz Tanjir TanjirZaVeceru PostavkaStola

Iako vas prevodilac prisiljava da inicijalizujete osn ov ne klase i zahteva da to u in ite odm ah n a p o etk u k o n stru k to ra , o n ne vodi ra u n a o to m e d a li ste inicijalizovali objekte lanove, pa m o rate sam i o to m e voditi rau na. P rilino je zadivljujue kako su klase jasn o razdvojene. N ije vam ak p o tre b a n ni izvorni k od m eto da da biste ih p o n o v o koristili. U veini sluajeva treb a sam o d a uvezete paket. (O vo vai i za nasleivanje i za ko m poziciju.)

Garantovanje pravilnog ienja


Java n em a nita slino destruktoru iz C + + -a, tj. n em a m e to d u koja se au to m atsk i poziva pri u n itav an ju objekta, verovatno zato to je u Javi prak sa da objekte zabo rav ite u m esto da ih u nitavate i da p u stite sakuplja sm ea da p o v rati m e m o riju kad a je p o tre b n o . esto je to sasvim u redu, ali u nekim sluajevim a klasa m oe d a obavlja izvesne aktivnosti koje zahtevaju ienje. Kao to je p o m e n u to u poglavlju Inicijalizacija i ienje, vi ne m oete znati kada e sakuplja sm ea biti pozvan ili da li e u op te biti pozvan. Stoga, ako je za neku klasu p o tre b n o ienje, m o rate izriito da napiete p o seb n u m e to d u i obezbedite da p ro g ram er klijent zna da m o ra pozvati tu m e to d u . P o vrh svega kao to e biti o p isan o u poglavlju Obrada greaka pom ou izuzetaka - o d izuzetaka m o ra te d a se zatitite tako to ete odg o v araju u p ro c e d u ru za ienje u g rad iti u blo k finally. R azm o trim o p rim e r sistem a za projek tov an je p o m o u ra u n a ra koji na ek ran u crta slike:
//: ponovnaupotreba/CADSistem.java // Obezbeivanje pravilnog ienja package ponovnaupotreba; import s ta tic net.mindview.uti1. P rin t.*; cl ass Obli k { O blik( i nt i) { print("Konstruktor klase O b lik "); void ciscenje() { print(" ienje klase O b lik ");

} }

192

Misliti na Javi

class Krug extends Oblik { Krug(int i) { s u p e r(i); p rint("C rtanje kruga");

}
void ciscenje() { p rin t("B ris a n je kruga"); su p e r.ciscen je ();

class Trougao extends Oblik { Trougao(int i ) { s u p e r(i); p rin t("C rtan je tro u g la ");

}
void ciscenjeO { p rin t("B ris a n je tro u g la"); su p er.ciscen jeO ;

class L in ija extends Oblik { private in t pocetak, kraj; L in ija (in t pocetak, in t kraj) { super(pocetak); this.pocetak = pocetak; th is.k ra j = kraj; p rin t("C rtan je l i n i j e : " + pocetak + ", " + k ra j);

}
void cisce n je() { p rin t("B ris a n je l i n i j e : " + pocetak + ", " + k ra j); super.ciscenjeO ;

} }
public class CADSistem extends Oblik { private Krug c; private Trougao t; private L in ija [] l i n i j e = new L ini j a [3 ]; public CADSistem(int i) { superfi + 1); fo r (in t j = 0; j < 1in ije .le n g th ; j++) 1in i j e [ j ] = new L in i j a ( j , j * j ) ; c = new K ru g (l); t = new Trougao(l); pri nt("Kombi novani konstruktor");

}
public void c isce n je() { print("CADSistem.ci sc e n je ()" ) ;

Poglav[je 7: Ponovno korienje klasa

193

// Poredak ienja je inverzan u odnosu na poredak in ic ija liz a c ije t .c is c e n je (); c .c is c e n je O ; fo r (in t i = lin ije .le n g th - 1; i > = 0; i--) 1in i j e [ i ] .c is c e n je (); su p er.ciscen jeO ;

}
public s ta tic void m ain(String[] args) { CADSistem x = new CADSistem(47); try { // Kod i obrada izuzetaka... } fin a lly { x .c is c e n je ();

} }
} /* Is p is : Konstruktor klase Oblik Konstruktor klase Oblik Crtanje l i n i j e : 0, 0 Konstruktor klase Oblik Crtanje l i n ij e : 1, 1 Konstruktor klase Oblik Crtanje l i n i j e : 2, 4 Konstruktor klase Oblik Crtanje kruga Konstruktor klase Oblik Crtanje trougla Kombinovani konstruktor CADSistem.ciscenje() Brisanje trougla ienje klase Obli k Brisanje kruga i enje klase Obli k Brisanje li n i j e : 2, 4 ienje klase Obli k Brisanje li n i j e : 1, 1 ienje klase Oblik Brisanje li n i j e : 0, 0 ienje klase Oblik ienje klase Obli k

* ///:U ovom sistem u sve je neka v rsta Oblika (koji je i sam tip a Object, jer im p licitn o nasleuje k orensku klasu). Svaka klasa redefinie m e to d u ciscenje() klase Oblik i p o red ostalog, poziva i verziju te m etod e iz osn o v n e klase p o m o u rezervisane rei super. Pojedine vrste klase Oblik - Krug, Trougao i Linija - im aju svoje k o n stru k to re koji crtaju", p rem d a bilo koja m e to d a to k o m ivotnog veka objekta m oe da u rad i neto to zahteva ienje. Svaka klasa im a svoju m e to d u ciscenje() koja vraa stvari bez ikakve veze s m em o rijo m , u stanje u kom e su bile pre nego to je objekat napravljen.

194

Misliti na Javi

U m eto d i main() m o ete d a p rim e tite dve n o ve rezervisane rei koje nee biti d etaljn o o b janjene sve d o poglavlja O brada greaka pom ou izuzetaka, a to su try i finally. Rezervisana re try naznaava da je b lo k koji sledi (u n u ta r v itiastih zagrada) zatieni region, to znai d a im a p o seb an tre tm a n . Jedan d eo to g p o se b n o g tre tm a n a o d n o si se n a k o d u n u ta r b lo k a finally koja sledi iza to g zatienog regiona. Taj k o d se uvek izvrava, bez o bzira na to kako se iz b loka try izlazi. (P ri o b ra d i izuzetaka, b lo k try m oe se n ap u stiti na vie n eu o bia jen ih naina.) U o vo m sluaju, b lo k finally kae: ,,Za x uvek pozovi m e to d u ciscenje(), bez o b zira n a to ta se d o g a a. U koliko jed an p o d o b je k a t zavisi o d d ru g o g , zap am tite da u svojoj m e to d i za ienje (u ovom sluaju, m e to d i ciscenje()) tak o e m o rate da v odite ra u n a o p o retk u ienja osnov n e klase i o b jekata lanova. Po prav ilu , treb a da p ra tite p rin c ip koji je uveo prevodilac jezika C + + za svoje d estru k to re: prv o o b av ite sve ienje k oje je specifino za vau klasu, i to red osled o m koji je o b rn u t o d redo sled a pravljenja. (U o p tem sluaju, za to je p o tre b n o d a elem en ti o sn o v n e kiase i dalje b u d u u ivotu.) Z atim pozovite m e to d u za ienje o sno v n e klase, kao to je u n aem p rim e ru p o k azan o . U m n o g im sluajevim a ienje nije p ro b le m i m o ete d a p u stite sakupljaa sm ea da obavi posao. Ali, kada je izriito ienje p o tre b n o , n je m u m o ra te da p ristu p ite m arljivo i paljivo, p o to n a sak u p ljan je sm ea ne m oete previe d a se oslo n ite. Sakupljanje sm ea u o pte n e m o ra da se d o go d i, a ako se d o g o d i, m oe d a isti objekte bilo kojiin redosled o m . N ajbolje je n e oslan jati se na sakuplja sm ea ni za ta o sim za o slobaanje m em o rije. Ako v am je ienje p o tre b n o , n a p rav ite svoje m eto d e za njega i ne oslanjajte se na m eto d u finalize().
V eba 12: (3) D odajte o d g o v araju u h ijerarh iju m eto d a ciscen je() svim klasam a iz veban ja 9.

Sakrivanje imena
Ako je u osnovnoj klasi u Javi defin isano im e m eto d e koje je kasnije nekoliko p u ta preklopljeno, redefinisanje tog im en a m eto d e u izvedenoj klnsi //ect sakriti n ijed n u verziju iz o sn o v n e klase (za razliku o d C + + -a ). Stoga prek lap an je fu n k cio n ie bez obzira na to da li je m eto d a definisana na to m nivou ili u o sn o v n o j klasi:
/ / : ponovnaupotreba/Sakrivanje.java // Preklapanje imena metode osnovne klase u izvedenoj klasi // ne sakriva v erz ije iz osnovne klase. import s ta tic net.mindview.u t i1. Print class Homer ( char doh(char c) { p rin t("d o h (ch a r)"); return 'd ';

}
flo a t doh(float f) { p rin t("d o h (flo a t)" ) ; return l.O f;

Poglavlje 7: Ponovno korienje klasa

195

class Milhaus {} class Bart extends Homer { void doh(Milhaus m ) {} print ("doh (Mi 1haus)1 1 );

} }
public class Sakrivanje { public s ta tic void m ain(String[] args) { Bart b = new B a r t ( ) ; b .d o h (l); b .d o h ('x '); b .d o h (l.O f); b.doh(new M ilh au sO );

}
} /* Is p is : doh(float) doh(char) doh(float) doh(Milhaus)

* ///:Viciite da su sve preklopljene m eto d e klase Homer d o stu p n e u klasi Bart, iako Bart uvodi novu p reklopljenu m e to d u (tim e bi se u C + + - U sakrile m etode osnovne klase). Kao to ete p roitati u n ared n o m poglavlju, m eto de istog im ena se m n o g o ee redefiniu p o m o u p o tp u n o istog potp isa i p o v ratn o g tipa kao i u osnovnoj klasi. Inae rezultati m o g u biti zbun juju i (zbog ega to u C++-U i nije dozvoljeno, da ne biste napravili greku). U Javi SE5 d o d a ta je an o tacija @Override, to nije rezervisana re, ali se m oe u p o trebljavati kao da jeste. Kada hoete da redefiniete m eto d u , m oete d o d ati tu an o tac iju i prevodilac e prijaviti greku ukoliko je n e n a m e rn o preklo p ite um esto da je redefiniete:
//: ponovnaupotreba/Lisa.java // {Compi1eTimeError} (Nee se prevesti) class Lisa extends Homer { OOverride void doh(Mi1haus m ) { System.out.println("doh(Mi 1haus) ) ;

III--

O znaka {C om pileT im eE rro r} uklanja ovu d ato te k u iz A nt skripta za prevoenje p rim era iz knjige, ali ako p ro b a te ru n o da prevedete taj p rim e r d obiete p o ru k u o greci:
method does not override a method from it s superclass

T im e e anotacija <Override spreiti da sluajno preklo p ite m e to d u koja nije bila pred v i en a za to.

196

Misliti na Javi

V eba 13: (2) N ap rav ite klasu s m e to d o m k oja je prek lo p lje n a trip u t. N eka je nasledi n ova klasa, dodajte jo je d n o p rek lap an je m eto d e i pok aite d a su sve etiri m eto d e d o stu p n e u izvedenoj klasi.

Izbor izmeu kompozicije i nasleivanja


I kom pozicija i nasleivanje dozvoljavaju d a u m e tn e te p o d o b jek te u n o v u klasu (u k o m p o z i ji to m o ra te da u ra d ite eksplicitn o , a u n asled iv an ju je to im p licitn o ). M oete d a se zapitate kakva je razlika izm e u n jih i k ad a se koja te h n ik a koristi. K om pozicija se p o p rav ilu k o risti k ad a u novoj klasi h o ete da im ate m o g u n o sti p o stojee klase, ali ne i n jen interfejs. O d n o sn o , vi u g ra d ite ob jek at tako da ga m o ete k o ristiti za realizovanje fu n k cio n aln o sti u n ov oj klasi, ali k o risn ik te nove klase vidi interfejs koji ste vi za n ju definisali, a ne interfejs u g ra e n o g objekta. D a biste to postigli, u novu klasu treb a d a u g rad ite p riv atn i o bjek at p o sto jee klase. Ponekad im a sm isla d a k o risn ik u klase dozv o lite d ire k ta n p ristu p k o m poziciji nove klase, o d n o sn o da objekte lanove proglasite jav n im . P oto o b jek ti ianovi i sam i koriste sakrivanje realizaciie, to m o ete b ez b o jazn i da u ra d ite. Kada k o risn ik zna da je posredi sastavljanje o d vie delova, lake e razu m eti interfejs. O b jek at k o la je d o b a r p rim er:
//: ponovnaupotreba/Kola.java // Kompozicija pomou javnih objekata. class Motor { public void s t a r t() {} public void rik v e rc () {} public void stop() {}

}
class Tocak { public void naduvaj(int psi) {}

}
class Prozor { public void podigni() {} public void spusti () {}

}
class Vrata { public Prozor prozor = new Prozor(); public void o tv o ri() {} public void z a tvo ri() {}

}
public class Kola { public Motor motor = new M otor(); public Tocak[] tockovi = new Tocak[4]; public Vrata leva = new V ra ta (), desna = new V ra ta (); // sa dvoja vrata

Poglavlje 7: Ponovno korienje klasa

197

public Kola() { fo r (in t i = 0; i < 4; i++) to ck o v i[i] = new Tocak();

)
public s ta tic void m ain(String[] args) { Kola kola = new K o la (); kola.leva.prozor.podigni( ) ; kola.tockovi [ 0 ].naduvaj (72);

} } ///= Poto je k o m p o z i ja kola u ovom sluaju deo analize p ro b le m a (a n e sam o d eo osnovno g p ro jek ta), proglaavan jem lanova jav n im p o m ae te p ro g ra m e ru klijen tu d a shvati kako d a k oristi klasu, a tim e se i sm an ju je slo en o st k o d a koji a u to r klase m o ra d a napie. Im ajte n a u m u da je ovo specijalan sluaj i d a polja u g lav n o m treb a da b u d u p riv atn a. Pri nasleivanju polazite o d postojee klase i p rav ite n je n u specijalnu verziju. U o ptem sluaju, to znai d a uzim ate klasu o p te n a m e n e i specijalizujete je za o d re e n e potrebe. A ko m alo razm islite, uvideete k ako n em a sm isla d a klasu a u to p rav ite k o m p o zicijom o d klase v o zilo - a u to n e sadri vozilo, o n jeste vozilo. Relacija jesteizraav a se nasleivanjem a relacija sadri izraava se k o m p o zicijo m . V eba 14: (1) U datoteci K ola.java klasi M o to r d o d ajte m e to d u serv is() i pozo v ite je iz m eto de m a in ().

Rezervisana re protected
Poto ste savladali nasleivanje, rezervisana re p ro te c te d n ap o k o n d o b ija znaenje. U id ealn om svetu, p riv a tn i lanovi bi uvek bili p o tp u n o p riv atn i, ali u stv arn im projektim a nekad ho ete da neto sakrijete o d ire jav n o sti, a da ipak izvedenim klasam a dozvolite p ristu p . Rezervisana re p ro te c te d d o p rin o si p ra g m atizm u . O n a kae: O vo je p riv a tn o to se tie korisnika klase, ali je d o stu p n o svakom ko ovu klasu nasledi ili bilo kom d ru g o m u istom p ak etu . O d n o sn o , u Javi zatieno (engl. protectcd) au to m a tsk i p o d ra z u m ev a i paketni p ristu p . lako m o ete prav iti zatiena polja, najbolje je d a p o lja b u d u p riv atn a - u v e k tre b a da zad rite pravo da m en jate o sn o v n u realizaciju. Tada p o m o u zatienih m eto d a m oete da dozvolite k o n tro lisan p ristu p n asled n icim a vae klase:
//: ponovnaupotreba/Ork.java // Rezervisana re protected. import s ta tic net.mindview.uti1. P r in t.*; class Nitkov { private String ime; protected void postavi(String im) { ime = im; } public N itkov(String ime) { this.ime = ime; } public String toStringO {

198

Misliti na Javi

return "Ja sam Nitkov i zovem se " + ime;

} }
public class Ork extends Nitkov { private in t orkBroj; public Ork(String ime, in t orkBroj) { super(ime); this.orkBroj = orkBroj;

}
public void promeni(String ime, in t orkBroj) { postavi(im e); // Dostupna zato to je zatiena this.orkBroj = orkBroj;

}
public String to Strin g () { return "Ork" + orkBroj + 1 1 + su p e r.to Strin g ();

}
public s ta tic void m ain(String[] args) { Ork ork = new OrkC'Limburger", 12); p rin t(o rk ); ork.promeni("Bob", 19); pri nt (o rk );

}
} /* Isp is: Ork 12: Ja sam Nitkov i zovem se Limburger Ork 19: Ja sam Nitkov i zovem se Bob

* ///:M oete v ideti da m eto d a prom eni() im a p ristu p m eto d i postavi() je r je o n a zatiena. O b ratite p a n ju i na to d a je m eto d a toStringO klase Ork definisana p o m o u o d govaraju e verzije isto im en e m eto d e natklase.

Veba 15: (2) N ap rav ite klasu sa zatien o m m e to d o m u n u ta r paketa. P okuajte da pozovete tu zatienu m e to d u van paketa i o bjasnite rezultate. Sada n asledite tu klasu i pozovite zatienu m e to d u iz neke d ru g e m eto d e te nasleene klase.

Svoenje navie
N ajbitniji vid nasleivanja nije o b ezbeivanje m eto d a n ovim klasam a. N asledivanje predstavlja relaciju izm ed u nove i osn o v n e klase. Relacija m oe da se sam e kao nova klasa je tipa postojee klase. P reth o d n i opis nije izm iljen sam o da bi se objasnilo nasledivanje - jezik ga d irek tn o podrava. Na p rim er, p o sm atrajte o sn o v n u klasu p o d im en o m Instrument, i izvedenu klasu po d im en o m Duvacki. Poto nasleivanje podra/.um eva da su sve m etode osnovne klase d o stu p n e i u izvedenoj klasi, svaku p o ru k u koju m oete da poaljete osnovnoj klasi, m oete da poaljete i izveenoj klasi. Ako klasa Instrument im a m eto d u sviraj(), im ae je i Duvacki in stru m en ti. To znai da, bez greke, m oem o rei kako je objek at tipa Duvacki takoe i tip a Instrument. N aredni p rim e r pokazuje kako prevodilac p odrava tu notaciju:

Poglav[je 7: Ponovno korienje klasa

199

//: ponovnaupotreba/Duvacki.java // Nasleivanje i svoenje navie. class Instrument { public voi s v ir a j() {} s ta tic void melodija(Instrument i) {

/ /
i .s v ira j ( ) ;

} }
// Objekti klase Duvacki.su instrumenti // je r imaju is t i in te rfe js : public class Duvacki extends Instrument { public s ta tic void main(String[] args) { Duvacki flauta = new Duvacki(); Instrum ent.m elodija(flauta); // Svoenje navie

} } ///:U o v om p rim e ru vana je m eto d a melodija(), iji arg u m e n t je referenca n a Instrument. M ed u tim , u m eto d i Duvacki.main(), m eto d a melodija() se poziva uz prosleivanje reference na objekat klase Duvacki. Z n aju i da Java strog o proverava tipove, u d n o je da m e to d a koja prihvata jed an tip sp rem n o p rih v ata neki d ru g i, sve d o tre n u tk a d o k ne shvatite da je objekat tip a Duvacki takoe i objekat tip a Instrument. Z bog toga n e po sto ji m e to d a m elodija() kojoj m oe da se prosledi objekat klase Instrument, ali ne i objek at klase Duvacki. K od u n u ta r m eto d e melodija() rad i za klasu Instrument i za sve to je iz nje izvedeno, a sam in konverzije iz reference klase Duvacki u referencu klase Instrum ent naziva se svoenje navie (engl. upcasting).

Zato svoenje navie'?


Ovaj naziv je zasnovan na trad icio n a ln o m n a in u crtan ja d ijag ram a nasledivanja: koren je u v rh u , a dijag ram se iri nadole. (N aravno, dijagram e m o ete d a crtate n a bilo koji n ain.) D ijagram nasleivanja za D u v ack i.ja v a je:

K onverzijom od izvedene ka osnovnoj klasi kreete se navie k roz dijagram nasleivanja, pa se to esto zove svoenje navie. Svoenje navie je uvek bezb ed n o , je r se sa o d reenijeg tip a vraate na optiji; o d n o sn o , izvedena klasa je natklasa osn o v n e klase. O n a m oe d a sadri vie m eto d a nego osn o v n a klasa, ali zato mora da sadri o n e m eto d e koje p o sto je u osno v noj klasi. Prilikom svoenja navie, je d in o se m oe d o g o d iti da interfejs

200

Misliti na Javi

klase izgubi neke m e to d e, a n e d a ih d obije. Z ato p revodilac dozvoljava svoenje navie bez eksplicitne konverzije ili neke d ru g e p o se b n e notacije. O sim svoenja navie, m o ete da u ra d ite i su p ro ta n proces, svoenje nanie, ali se p ri to m e javljaju o d re e n e dilem e, o em u e biti rei u n a re d n o m poglavlju i poglavlju Podaci o tipu.

Ponovo o biranju izmeu kompozicije i nasleivanja


N ajei nain za pravljenje i korienje o b jek tn o o rijen tisan ih p ro g ra m a jeste pakovanje p o d a ta k a i m eto d a u klase i u p o tre b a o b jek ata te klase. K oristiete i p ostojee klase za izgra iv an je no v ih klasa k o m p o zicijo m . N asleivanje ete ree k oristiti. Iako je p ri p o d u av an ju O O P -a p rilin o n aglaeno nasledivanje, to n e znai da ga treb a k o ristiti gde go d je m ogue. Ba su p ro tn o , treb a ga k o ristiti tedljivo, sam o kada je jasn o da je nasleivanje k orisn o . N ajsig u rn ije ete o d red iti d a li d a k o ristite k o m poziciju ili nasleivanie ako se zap itate h o e li v am ikada b iti p o tre b n o svoenje navie iz nove klase u o sn o v n u klasu. A ko m o ra te da k o ristite svoenje navie, nasleivanje je n e o p h o d n o , ali ako ne m o rate, paljivo p ro u ite d a li v am n asleivanje zaista treba. U poglavlju Polim orfizam opi suje se je d a n o d o sn o v n ih razloga za svoenje navie. A ko za p a m tite ove prin c ip e i u p itate se: ,,Da li m i je p o tre b n o svoenje navie?, u ru k a m a ete im ati d o b ru alatku za o dluivanje izm e u k om pozicije i nasleivanja.

Veba 16: (2) N ap rav ite klasu p o d im en o m Vodozemac. Iz nje nasledite klasu Zaba. Stavite odgovarajue m e to d e u o sn o v n u klasu. Iz m eto d e main() n ap rav ite objekat klase Zaba, svedite ga navie d o klase Vodozemac i p okaite da sve m eto d e i dalje rade. Veba 17: (1) Izm enite p re th o d n u vebu tako da u klasi Zaba redefiniete m e to d u iz
osn o v n e klase (n ap ra v ite nove definicije koristei iste p o tp ise m eto d a). Pogledajte ta se deava u m eto d i main().

Rezervisana re final
Z naen je Javine rezervisane rei finai im a nekoliko nijansi, u zavisnosti od ko n tek sta u kom je u p o treb ljen a, ali o p te je: Ovo ne m oe da se m enja. M oete poeleti da p ro m en e spreite zbog p ro jek ta ili efikasnosti. Poto su ti p rin cip i sasvirn razliiti, rezervisana re final m oe se i p o g ren o u p o tre b iti. U n a re d n o m o deljku razm atraju se tri sluaja u kojim a m oe da se u p o treb i rezervisana re final: za p o d atk e, m e to d e i klase.

Finalni podaci
M nogi p ro g ram sk i jezici im a ju n ain da p revodiocu naznae kako je neki p o d a ta k ,,konsta n ta n . K onstante su k o risn e iz dva razloga: 1. M ogu da zad aju k onstantne vrednosti p ri prevoenju koje se nikada nee p ro m en iti. 2. M ogu da predstavljaju v red n o sti koje inicijalizujem o to k o m izvravanja, a ne elim o da b u d u m enjane.

Poglavlje 7: Ponovno korienje klasa

201

K ada se k o risti k o n sta n ta p ri p rev o en ju, p revo dio cu je dozvoljeno d a k o n sta n tn u vred n o st ,,u k lo p i u sva izrau n av an ja u k ojim a se pojavljuje, to jest rau n an je m oe d a se obavi to k o m prevo en ja, im e se ubrzava izvravanje. O va v rsta k o n sta n ti u Javi m o ra da b u d e p ro sto g tip a i oznaava se p o m o u rezervisane rei final. N jena v re d n o st m o ra biti zad ata n a m estu definicije. Za polje koje je i statin o i finalno p ostoji sam o je d n o m esto za sldaditenje i o n o ne m oe da se m enja. K ada se rezervisana re final ko risti u z reference n a objekte u m esto uz p ro ste tipove, n jen o znaenje m oe da zbuni. Uz p ro s t tip, final oznaava d a je vrednost k o n sta n tn a , a uz referencu na objekat da je referenca k o n stan tn a. Kada se referenca je d n o m inicijalizuje i povee sa o b jek to m , o n a vie ne m oe da b u d e p ro m en je n a tak o d a po k azu je n a neki d ru gi objekat. Sam objekat, m e u tim , m oe d a se izm eni - Java ne o b ezb e u je n ain d a neki proizv o ljan o b jekat p retv o rite u k o n stan tan . (M oete, ipak, svoju Idasu d a napiete tako da se n jen i o b jek ti efektivno p o n aaju k ao d a su k o n stan tn i.) O vo og ran ien ie se o d n o si i na nizove, koji su tak o e objekti. Sledi p rim e r fin aln ih polja. O b ratite p anju na to da se statin a i finalna polja (tj. ko n stan te p ri p rev o en ju ) p o konvenciji piu velikim slovim a, s tim da se iz m e u rei pie p o tcrta.
// : ponovnaupotreba/Finalni Podaci.java // Efekat rezervisane rei fin al na polja. import s ta tic ja v a .u til import s ta tic net.mindview.util .P r in t.* ; class Vrednost { in t i ; // Paketni pristup public Vrednost(int i) { th is .i = i }

1
public class FinalniPodaci { private s ta tic Random slucajan = new Random(47); private String id; public F inalniPodaci(String id) { th is .id = id } // Mogu da budu konstante pri prevoenju private fin al int vrednostJedan = 9; private s ta tic fin al in t VREDNOST_DVA = 99; // Tipina javna konstanta: public s ta tic final in t VREDNOST_TRI = 39; // Ne m ogu da budu konstante pri prevoenju: private fin al int i4 = slucajan.nextlnt(20); s ta tic fin al in t INT_5 = slucajan.nextlnt(20); private Vrednost vl = new Vrednost(11); private fin al Vrednost v2 = new Vrednost(22); private s ta tic fin al Vrednost VRE_3 = new Vrednost(33); // N izovi: private fin al in t [] a = { 1, 2, 3, 4, 5, 6 }; public String toStringO {

202

Misliti na Javi

return id + ": " + "i4 = " + i4 + ", INT_5 = " + INT_5;

}
public s ta tic void main(String[] args) { FinalniPodaci fdl = new FinalniPodaci ( " f d l " ) ; //! fdl.vrednostJedan++; // Greka: vrednost ne moe da bude promenjena fdl.v2.i++; // Objekat n ije konstantan! fd l.v l = new Vrednost(9); // O K -- n ije finalna fo r(in t i = 0; i < fd l.a.le n g th ; i++) fdl.a[i]+ + ; // Objekat n ije konstantan! //! fdl.v2 = new Vrednost(O); // Greka: ne moete da //! fdl.VAL_3 = new Vrednost(l); // promenite referencu //! fd l.a = new in t [3 ] ; p r in t ( f d l) ; p rin t("P ra v lje n je novog objekta FinalniPodaci" ) ; FinalniPodaci fd2 = new Fin aln iPo d aci("fd 2 "); p r in t ( f d l) ; p rin t(fd 2 );

}
} /* Ispis: f d l: i4 = 15, INT5 = 18 Pravljenje novog objekta FinalniPodaci f d l: i4 = 15, INT5 = 18 f d l: i4 = 13, INT_5 = 18

* ///:Poto su prom enljive vrednostjedan i VREDNOST_DVA prosto g tipa, finalne su i dobijaju vrednost prilik o m prevodenja, obe m o gu da se u p o treb e kao k o nstan te p ri prevoenju i b itn o se ne razlikuju. VREDNOST_TRI je uobiajeniji nain za definisanje takvih konstanti: kao javne, zbog ega m o g u da se koriste i izvan paketa, statine, da bi se naglasilo kako postoji sam o je d n a kopija i finalne, da se naznai da su k on stantne. O b ratite panju n a to da su finalne i statine prom enljive pro stog tipa s k o n sta n tn im inicijalnim vred nostim a (tj. k o n stan te p ri p rev o enju ), po konvenciji napisane velikim slovim a, a rei su razdvojene p o tc rto m (kao i k on stan te u C -u, odakle i potie ova konvencija). Sam o zato to je neto finalno, ne znai da je njegova vredn ost p o z n a ta u tre n u tk u prevoenja. U p rim eru , to je po k azan o na prom enljivam a i4 i INT_5 koje su inicijalizovane sluajnim brojevim a to ko m izvravanja. Taj deo p rim era tak o e p okazuje u em u je razlika izm eu statin ih i nestatin ih finalnih vrednosti. Razlika se javlja sarno kada se vrednosti inicijalizuju prilikom izvravanja, jer sve k on stan te p ri prev o enju prevodilac isto tretira. (I verovatno o p tim izu je i elim inie, jer vie nisu p o treb n e.) Razlika se vidi iz rezultata izvravanja p rim era. V rednosti i4 za fd l i fd2 jedinstvene su, dok se v red no st INT _5 ne m enja n akon pravljenja dru g o g olijekta FinalniPodaci. O na je statina, pa se inicijalizuje sam o je d n o m pri uitavanju klase, a ne svaki pu t kada se pravi novi objekat. Prom enljive o d vl do VRK 3 pokazuju znaenje finalnih referenci. Kao to m oete videti u m etodi main(), to to je v2 finalna, ne znai da ne m oete m enjati vrednost objekta na koji ukazuje. Ali poto je v2 referenca, rezervisana re final znai da v2 ne m oete da poveete s nekim dru g im objektom . Isto vai i za niz, koji je u sutini sam o druga vrsta

Poglavlje 7: Ponovno korienje klasa

203

reference. (Koliko ja znam , ne postoji nain da sam e reference u nizu proglasite finalnim .) Proglaavanje referenci finalnim ini se m anje korisnim o d proglaavanja prom enljivih prostog tipa finalnim. V eba 18: (2) N apravite klasu koja im a statin o finalno polje i finalno polje i p okaite razlike izm e u njih.

Prazna finalna polja


Java dozvoljava pravljenje praznih fina ln ih polja (engl. blank finals), to oznaava polja koja su deklarisana kao finalna, ali im nije zadata inicijalizaciona v red n o st. U svim sluajevim a, prazna finalna polja moraju da b u d u inicijalizovana p re nego to se u p o treb e i prevodilac to proverava. Prazna finaina polja su, m e u tim , m n o g o fleksibilnija. N a p rimer, finalno polje u n u ta r klase u to m sluaju m oe d a b u d e razliito za svaki objekat, a da ipak pri to m zadri neprom enljivost. Evo p rim era:
// : ponovnaupotreba/PraznoFinalno.java // "Prazna" finalna polja. class Poppet { private int i ; Poppet(int i i ) { i = i i ; }

1
public class PraznoFinalno { private final int i = 0; // Inicijalizovana finalna promenljiva private final int j ; // prazna finalna promenljiva private final Poppet p; // prazna finalna referenca // Prazna finalna polja MORAJU da budu in ic i j a l izovana u konstruktoru: public PraznoFinalno () { j = 1; // In ic ij a l izovanje praznog finalnog polja p = new Poppet(l); // I n i c i j a l izovanje prazne finalne reference

}
public PraznoFinalno (in t x) { j = x; // In icija liz o v a n je praznog finalnog polja p = new Poppet(x); // In ic ijalizovanje prazne finalne reference

}
public sta tic void main(String[] args) { new PraznoFinalno ( ) ; new PraznoFinalno (47);

III--

Finalnim poljim a m o rate da dodeiite v rednosti p o m o u izraza na m estu definicije ili u svakom k o n stru k to ru . Na taj nain se g aran tu je da su finalna polja uvek in ijalizov ana pre upotrebe. Veba 19: (2) N apravite klasu s p razn o m finalnom referencom na objekat. U n u tar svih k o n stru k to ra inicijalizujte prazan finalni elem ent. D okaite da finalni elem ent m o ra biti inicijalizovan pre u p otreb e i da nakon toga vie ne m oe da se m enja.

204

Misliti na Javi

Finalni argumenti
A rgum ente m oete da pro g lasite fin aln im tak o to ih deklariete k ao finalne u listi argum enata. To znai da u n u ta r m e to d e n e m oete m en jati o n o na ta taj a rg u m en t kao referenca ukazuje:
//: ponovnaupotreba/FinalniArgumenti.java // Upotreba rezervisane rei " fin a l" uz argumente metode. class Stvar { public void z a v r ti() {}

}
public class FinalniArgumenti { void sa(final Stvar g) { //! g = new S tv a r (); // Nedozvoljeno -- g je finalno

}
void bez(Stvar g) { g = new S tv a r (); // OK -- g n ije finalno g .z a v rti();

}
// void f(fin a l int i) { i++; } // Ne moete da promenite // Finalnu promenljivu prostog tipa moete samo da ita te : int g(final in t i) { return i + 1; } public s ta tic void main(String[] args) { FinalniArgumenti bf = new FinalniArgumenti( ) ; bf,bez(nul1); b f,sa(nul1);

} } lll--~ M etode f() i g() pokazuju ta se deava kada su arg um enti prostog tipa finalni: argum ent m oete itati, ali ga ne m oete pro m en iti. To se koristi prvenstveno za prosleivanje podataka an o n im n im u n u tran jim klasam a koje ete u poznati u poglavlju Unutranje klase.

Finalne metode
Za u p o tre b u finalnih m eto d a posto je dva razloga. Prvi je da se m eto da ,,zakljua kako bi se spreilo da bilo koja klasa naslednica p ro m en i n jeno znaenje. To se radi zbog projektn ih razloga, kada elim o da b u d e m o sigurni kako e p on aan je m etod e biti neprom enjen o p rilikom nasleivanja, o d n o sn o kako m eto d a ne m oe biti redefinisana. Ranije se kao dru g i razlog za korienje finalnih m eto da navodila efikasnost. U preth o d n im realizacijam a Jave, kada biste m eto d u proglasili finalnom , dozvoljavali ste prevodiocu da sve pozive toj m eto d i u gradi d irek tn o u kod (engl. inline call). Kada bi prevodilac naiao na poziv finalnoj m etod i, m ogao je (p o svom n a h o e n ju ) da preskoi uobiajen poziv (stavi arg u m en te na stek, skoi na kod m eto de i izvri ga, vrati se, poisti arg u m en te sa steka i razm o tri p o v ra tn u v red n o st). U m esto toga, poziv m etode m ogao je da se zam eni k opijom stv arn o g k oda iz njenog tela. T im e su se elim inisali reijski trokovi sam og poziva. N aravno, ako je m etod a velika, kod poinje da se poveava i verovatno ne

Poglavlje 7: Ponovno koridenje klasa

205

biste d o b iti poboljanje p erfo rm an si, jer b i sva u n ap re en ja bila zanem ariva u o d n o su na vrem e izvravanja m etode. U novijim verzijam a Jave, v irtu e ln a m ain a (ko n k retn o , tehnologije v ru ih taaka) m oe da prep ozna takve situacije i p a m e tn o o d ab ere da li d a k oristi u m etan je k oda na m estu poziva za finalnu m eto d u , p a vie ne treb a da p o m aete o p tim iz ato ru tako to ete koristiti rezervisanu re fin al (p o p ravilu, to ne b i treb alo da rad ite). U Javi SE5/6, pustite da se prevodilac i izvrno o k ru en je (JVM ) staraju o efikasnosti, a m e to d u proglasite fin aln o m sam o ako elite izriito da spreite n jen o redefinisanje.1

Finalno i privatno
Svaka p rivatna m etod a u klasi im p licitn o je i finalna. Poto priv atn o j m eto d i ne m oete da pristu p ite iz nasleenih klasa, ne m oete n i da je redefiniete. P rivatnoj m eto d i m oete da dodelite specifikator final, ali njoj to ne daje nikakvo d o d a tn o znaenje. O vo stanje m oe da izazove zab u n u - ako pokuate da redefiniete p riv atn u m eto d u (koja je im plicitno finalna), to ete sam o naizgled uspeti, jer prevodilac nee prijaviti greku:
//: ponovnaupotreba/IluzijaRedefinisanjaFinalnih.java // Privatnu i l i privatnu finalnu metodu samo naizgled moete da redefiniete. import static net.mindview.uti1. P r in t.*; class SadrziFinalne { // Identino kao i kada sto ji samo "priva te": private final void f ( ) { p rin t("S a d rz iF in a ln e .f( ) " ) ; // Takoe automatski finalna: private void g() { p rin t("S a d rz iF in a ln e .g ()" ) ; }

class RedefinisanjePrivatnih extends SadrziFinalne { private final void f ( ) { pri nt("Redefini sanjePri v a t n ih .f ()" );

}
private void g() { pri nt("Redefi ni sanjePri v a tn ih .g ()" ) ;

class RedefinisanjePrivatnih2 extends RedefinisanjePrivatnih { public fin al void f () { print("R edefinisanjePrivatnih2 .f ( ) " ) ;

}
public void g() { p rin t("R ed efin isan jePrivatn ih 2 .g ()" ) ;

} }

N e m o jte p o d le i isk u e n ju d a p re ra n o o p tim iz u je te . A ko p o s tig n e te d a va sistem ra d i, ali je previe spo r, p ita n je je n io e te li to p o p ra v iti p o m o u re z erv isa n e rei fin a l. N a http://M indView.net/Hooks/ NBetterlava im a te p o d a tk e o p ro filisan ju - to zaista itioe d a u b rz a va p ro g ra m .

206

Misliti na Javi

public class IluzijaR edefinisanjaFin alnih { public s ta tic void m ain(String[] args) { RedefinisanjePrivatnih2 op2 = new RedefinisanjePrivatnih2 ( ) ; o p 2 .f(); op2.g(); // Moete da obavite svodenje navie: RedefinisanjePrivatnih op = op2; // A li ne moete da pozovete metode: //! o p .f( ) ; //' o p .g (); // Isto vai i ovde: SadrziFinalne sf = op2; //! s f . f ( ) ; //! s f .g () ;

}
} /* Isp is: Red efinisanjePrivatnih2 .f() Redefini sanjePri vatni h2.g()

* ///:R edefinisanje se javlja sam o ako se m enja deo interfejsa osnovne kJase. O d no sn o, m o rate biti u m o g uno sti da objekat svedete navie do njegovog prostog tipa i da pozovete istu m e to d u (p o en ta ovoga e postati jasna u n a red n o m poglavlju). U koliko je m etoda privatna, o n a nije deo interfejsa o snovne klase. O na je sam o neki kod koji je sakriven u n u ta r Idase i sam o sluajno im a isto im e kao m eto da u izveenoj klasi. Ako napravite javnu m eto du , zatienu m e to d u ili m eto d u s p ak etn im p ristu p o m u izveenoj klasi, ne postoji veza s priv a tn o m m eto d o m koja m oe im ati takvo isto im e u osnovnoj klasi. Niste redefinisali postojeu m e to d u , nego napravili novu. Poto je privatna m etoda n e d o stu p n a i efektivno nevidljiva, o n a ne utie ni na ta sem na organizaciju koda klase u kojoj je definisana. Veba 20: (1) Pokaite da anotacija <Override rea\'a problem razm otren u ovom odeljku. V eba 21: (1) N apravite klasu s fin aln o m m eto d o m . N asledite klasu i pokuajte da tu m eto d u redefiniete.

Finalne klase
Kada ozn aite da je cela klasa finalna (stavljajui rezervisanu re final pre njene definicije), saoptav ate da tu klasu ne elite da nasle u jete niti dozvoljavate bilo kom e da to uini. D ru g im reim a, iz nekog razloga p ro jekat vae klase nikad se nee m enjati ili iz bezbedn o sn ih razloga ne elite da o m o g u ite nasledivanje.
//: ponovnaupotreba/Preistorijski.java // Proglaavanje cele klase finalnom. class MaliMozak {} fin al class Dinosaurus { in t i = 7; in t j = 1;

Poglavlje 7: Ponovno korienje klasa

207

MaliMozak x = new MaliMozak(); void f ( ) {}

}
//! class Nastavak extends Dinosaurus {} // greka: Klasa 'Dinosaurus' ne moe da bude proirena public class P re is to rijs k i { public s ta tic void m ain(String[] args) { Dinosaurus n = new Dinosaurus( ) ; n. f 0 ; n .i =40; n.j++;

III--

Polja finalne klase m ogu da b u d u finalna, ali i ne m o raju , kako god izaberete. Za finalna polja vae ista pravila kao i ranije, bez o bzira na to da li je sam a klasa finalr.a. Kako je nasleivanje spreeno, sve tnetode u finalnoj klasi su im p licitn o (au to m atsk i) finalne, po to ne p o sto ji nain za njihovo redefinisanje. M eto d am a u finalnoj klasi m o ete da d o date specifikato r final, ali on ne dod aje nikakvo novo znaenje. V eba 22: (1) N apravite finalnu klasu i p okuajte da je nasledite.

Paljivo sa final
Kada p ro jek tu jete klasu, m oe vam se uiniti lcao d o b ra ieja da m e to d u proglasite finaln o m . M oda p om islite da ionako n em a anse da iko ikada reefinie vae m etode. Ponekad je to zaista tako. Ipak, paljivo razm islite. U glavnom je teko pred v id eti kako e se klasa p o n o v o koristiti, n aro ito klase opte naraene. Ako m eto d u proglasite fin aln o m , spreiete da vau klasu p u te m nasleivanja u p o treb i neki d ru g i p ro g ram er, i to sam o zato to niste m ogli zam isliti d a se o n a m oe koristiti na taj nain. S ta n d a rd n a Javina biblioteka je d o b a r p rim er. N aroito klasa V ector iz Jave 1.0/1.1 koja se esto koristi i koja je m ogla da b u d e jo korisnija da nisu, u im e efikasnosti (to je gotovo sig u rn o bila zabluda) sve m etode proglaene finalnim . Lako je p retp o stav iti kako elite da nasledite i redefiniete tako fu n d am en taln o k o risn u klasu, ali su p ro je k tan ti nekako odluili d a to nije u redu. Iro n in o - iz dva razloga. Prvo, klasa S tack je nasledena iz klase V ector, to govori da Stack icstc V ector, m ada to i nije ba tano s logike take gledita. U prkos to m e, sam i pro jek tan ti Jave izveli su tu klasu iz klasc V ector. im su n a taj nain napravili Stack, treb alo je da shvate kako su finalne m eto d e previe ograniene. D rugo, m noge najvanije m etode klase Vector, kao to su ad d E Ie m e n t() i eIem en tA t(), sin h ro n izo v an e su (rezervisana re sy n ch ro n ized ). Kao to ete videti u poglavlju Paralelno izvravanje, to p rav i znaajne reijske trokove koji verovatno ponitavaju svu korist o d toga to su inetode finalne. O va pria p o tv r u je teo riju da p ro g ram eri loe p ogaaju m esta na kojim a treb a o p tim izovati. Zaista je loe to je tako nesp retan projekat uao u sta n d ard nu bib lio teku pa svi m orarn o da se b o rim o s njim . (Na sreu, kontejnerska biblioteka

208

Misliti na Javi

savrem ene Jave zam enjuje ldasu Vector klasom ArrayList, koja se po n aa m n o g o p rim erenije. N aalost, i dalje do sta n o v ih p ro g ra m a k oriste sta ru k o n tejn ersk u bib lio tek u .) V ano je n a p o m e n u ti d a klasa Hashtable, jo je d n a v an a klasa iz sta n d a rd n e bib lio teke Jave 1.0/1.1, tiema n ije d n u fin aln u m e to d u . Kao to je ve reen o n a d ru g im m estim a u ovoj knjizi, prilin o je o igledno d a su neke klase p ro jek to v ali sasvim razliiti Ijudi. (Videete i da su im ena m eto d a u klasi Hashtable m n o g o k raa o d im en a u klasi Vector, to je jo je d a n dokaz.) Ba to k o risn icim a b ib lio teke klasa ne bi treb alo d a b u d e oigledno. Kada stvari nisu dosledne, k o risn ik sam o im a vie posla. To go v o ri d a p ro jek a t i k o d treb a pregledati vie p u ta. (U k o n tejn ersk o j b ib lio teci sav rem en e Jave, klasa Hashtable je zam en jen a klasom Hashmap.)

Inicijalizacija i uitavanje klasa


P ro g ra m i pisani n a veini jezika uitav aju se u celini u o k v iru p o k re ta n ja p ro g ra m a. N akon toga sledi inicijalizacija, a zatim o tp o in je p ro g ra m . P roces inicijalizacije u tak v im jezicim a m o ra da b u d e paljivo k o n tro lisan , kako p o re d a k inicijalizacije statin ih e lem en ata ne b i p ro u zro k o v ao p ro b lem e. P rim era rad i, u C++-U se javlja p ro b lem kada jed an statini elem en t oekuje da d ru g i b u d e isp rav an p re nego to je taj d ru g i statini elem ent inicijalizovan. U Javi se taj p ro b lem n e pojavljuje, je r o n a im a d ru g i p ristu p uitav an ju . O vo je je d n a od aktiv n osti koje su p ostale lake zato to je u favi sve objekat. Setite se da se prevedeni kod svake klase nalazi u zasebnoj dato teci. Ta d ato tek a se ne uitava sve d o k taj k o d ne b u d e p o treb an . U opteno, m o em o rei da se ,,kod klase uitava na inestu prve u p o tre b e . O b in o se klasa uitava p rilik o m k o n stru k cije prv o g o b jek ta te klase, ali se uitavanje obavlja i prilikom p ristu p a n ja statik o m po lju ili statikoj m e to d i.2 Prilikom prvog korienja klase, obavlja se i statik a inicijalizacija. Svi statin i objekti i statini delovi koda inicijalizuju se u tre n u tk u uitav an ja p o redosledu pisanja" (tj. u p o retk u kojim ste ih napisali u definiciji klase). S tatini ele m e n ti se, n arav n o , inicijalizuju sam o jed n o m .

Inicijalizacija i nasleivanje
Da bism o dobili celovitu sliku o svem u ovom e, p o g le d a jm o ceo proces inicijalizacije ukljuujui i nasleivanje. R azm o trite n are d n i p ro g ram :
//: ponovnaupotreba/Buba.java // Potpuni proces in ic ija liz a c ije . import s ta tic net.m indview .util.P r in t.* ; class Insekt { int i = 9; i nt j ; Insekt() {
' I k o n stru k to r je statina m etoda, m ada se rezervisana re s ta tic ne m ora eksplicitno navesti. Dakle, preciznije je rei da se klasa uitava o n d a kada se pristupi biio kojem njen o m statinom lanu.

Poglavlje 7: Ponovno korienje klasa

209

p r in t("i = + i + , j = " + j ) ; j = 39;

}
private s ta tic in t xl = p r in t ln it ( in ic ijaliz o v a n a statina promenljiva In s e c t.x l ) ; s ta tic in t p rin tIn it(S trin g s) ( p r in t (s ); return 47;

} }
public class Buba extends Insekt ( private in t k = p rin tIn it("Bu b a .k in ic ija liz o v a n a "); public Buba() ( p rin t("k = " + k); p r in t("j = " + j ) ;

}
private s ta tic in t x2 = p rin tIn it("in ic ija liz o v a n a statina promenljiva Buba.x2 " ) ; public s ta tic void m ain(String[] args) ( printf'konstruktor klase Buba"); Buba b = new Buba();

}
} /* Is p is : in icijaliz o va n a statina promenljiva Insekt.xl in icijaliz o va n a statina promenljiva Buba.x2 konstruktor klase Buba i = 9, j =0 Buba.k in icijaliz o va n a k = 47 j = 39

* ///= Kada p o k ren ete klasu Buba, n ajpre p okuavate d a p ristu p ite (statin o j) rnetodi Buba.main(), zbog ega prevodilac (u d atoteci Buba.class) trai p revedeni k o d klase Buba. P ro gram za uitavanje (engl. loader) p rim eu je d a o n a im a o sn o v n u klasu (to oznaava rezervisana re extends), pa zatim uita i nju. To e se d o g o d iti bez o b zira na to da li ete p raviti objekat te osn ov n e klase. (Kao dokaz, p ro b a jte da stavite u k o m e n ta r pravljenje objekta.) Ako o sn o v n a klasa im a so p stv en u o sn o v n u klasu, i o n a e biti u itan a i tako redom . Z atim se obavlja statika inicijalizacija u korenskoj o sn o v n o j klasi (u n aem sluaju klasi Insekt), zatim u sledeoj izvedenoj i tako redom . O vaj redosled je b itan , je r statika inicijalizacija izvedene klase m oe da zavisi o d p rav iln e inicijalizacije nekog statikog lana osn o v n e klase. U to m tre n u tk u , uitan e su sve n e o p h o d n e klase i objek at m oe da b u d e napravljen. Prvo, sve pro m en ljiv e p rostog tipa do b ijaju p o d ra zu m e v a n e v red n o sti, a reference na objekte d o bijaju v re d n o st n u l l - to se obavlja o d je d n o m , tako to se m em o rija u o b jek tu p o p u n i b in a rn im n u lam a. Z atim se poziva k o n stru k to r o sn o v n e klase. U n aem sluaju,

210

Misliti na Javi

poziv se obavlja au to m atsk i, ali tak o e m o ete d a o d red ite koji k o n s tru k to r o sn o v n e klase treb a da b u d e pozvan (prva o p eracija u k o n stru k to ru klase Buba) p o m o u rezervisane rei super. K o n stru k to r osn o v n e klase p ro lazi k roz isti p ro ces i istim red o sled o m kao k o n stru k to r izvedene klase. N akon obavljene k o n stru k cije o sn o v n e klase, pro m en ljiv e in stance inicijalizuju se p o p o retk u pisanja. K onano, izvrava se o statak tela k o n stru k to ra .

Veba 23: (2) D okaite da se klasa uitava sam o je d n o m . D o k aite d a uitavanje m oe da


b u d e p ro u zro k o v an o bilo p ravljenjem prve in stan ce te klase bilo p ris tu p o m statin o m lanu.

Veba 24: (2) U dato teci Buba.java, iz klase Buba n asledite specifinu v rstu b u b e, drei
se istog fo rm ata kao kod postojeih klasa. P ratite i objasn ite rezu ltat p ro g ra m a .

Saetak
I nasleivanje i kom pozicija o m o g u av a ju da o d po sto jeih tip o v a n a p ra v ite nove. K om pozicija upotrebljav a postojei tip kao d eo o sn o v n e realizacije n o v o g tip a, a nasleivanje pon o v o u potrebljava interfejs. U nasleivanju, izvedena klasa o b u h v a ta interfejs o sn o v n e klase, p a m oe da b u d e svedena navic ka osnovi, to je veom a b itn o za p o lim o rfizam (kao to ete videti u n ared n o m poglavlju). U prkos veom a n aglaenom n asledivanju u o b jek tn o o rije n tisa n o m p ro g ra m ira n ju , u p rvo m p ro lazu kroz p ro jek at treb alo b i d a se k o n cen trie te na k o m p o ziciju (ili na delegiranje), a nasleivanje koristite sam o kada je n eo p h o d n o . K om pozicija u m e da b u d e fleksibilnija; Iukavo p rim en ju ju i nasleivanje na objekte lanove, m o ete im p ro m en iti taan tip, a sam im tim i p o n aan je to k o m izvravanja. D ru g im reim a, p o n aan je objekta nastalog p o m o u kom pozicije m o ete da m en ja te to k o m izvravanja. Kada projektujete sistem , va cilj je d a p ro n a e te ili n ap rav ite sk u p klasa u k om e svaka klasa im a specifinu n a m en u . Uz to, n ijed n a klasa ne treb a da b u d e ni prevelika (s toliko m o guno sti da postaje n ezg rap n a da bi se p o n o v o koristila) ni p rem ala (toliko da ni vi ne m oete da je koristite takvu kakva je ve je m o rate p ro iriti). U koliko p ro jek at p o stan e previe sloen, esto p o m ae kada d o d a te nove objekte nastale cep an jem p ostojeih na m anje delove. Kada ponete da p ro jek tu jete sistem , vano je da shvatite kako je razvoj p ro g ram a p o stu p an proces, ba kao i ovekovo uenje. N jegova osnova je ek sp erim e n tisan je; m oete b eskrajno dug o da an alizirate stvari, ali i dalje neete znati sve o d g o v o re kada se u p u stite u projekat. Im aete m n o g o vie u sp eh a - i vie n ep o sred n ih p o d ata k a o rezu ltatim a - ako ,,uzgajate svoj projekat kao organsko bie koje se razvija, u m esto da ga o d je d n o m pravite kao stakleni soliter. N asleivanje i k o m pozicija sp ad aju m e u najvanije alatke u objektno o rijen tisan o m p ro g ra m ira n ju koje om o g u av aju takvo ek sp erim e n tisan je.
R eenja o d a b ra n ih vebi d a ta su u e le k tro n sk o m d o k u m e n tu Thinking in Jnva Annotated Solutiini

Guide, koji se m o e k u p iti n a VVeb lokaciji www.MindView.net.

Polimorfizam
Pitali su tne: M o lim vas, gospodine Bebide, recite hoe li iz vae m aine izai tani odgovori ak i ako u nju unesetepogrene brojeve?N e m ogu da p o jm im k a kva je zbrka u glavam a Ijudi koji p ita ju tako ta . arls Bebid (1791-1871)
PO L IM O R FIZA M PREDSTAVLJA TR EU SUSTINSKU O SO B IN U O B IEK TN O O R IJEN TISA N OG

pro g ram sk o g jezika, p o re d apstrakcije p o d atak a i nasleivanja. P olim orfizam o b e /b ed u je jo je d n u d im en ziju u razd vajan ju interfejsa i realizacije, tj. razdvaja ta i kako. Pored toga to o m o g u ava n a p re d n iju o rganizaciju k o da i poboljava itljivost, p o lim o rfizam om o g u av a i pisan je proirivih p ro g ra m a koji m o g u d a ,,rastu i to k o m p o e tn o g pravljenja p ro je k ta i kada zatreb aju nove m o gu no sti. K apsulacija daje nove tipove k o m b in o v an jem k arak teristik a i po n aan ja. Sakrivanje realizacije odvaja interfejs o d realizacije tako to detalje proglaava p riv a tn im . O vakva m eh an ika organizacija je p o tp u n o jasn a n ek o m e ko im a iskustva u p ro c e d u ra ln o m p ro gra m ira n ju . P olim orfizam , m e u tim , razdvaja klase p o tipovim a. U p re th o d n o m poglavlju ste videli kako nasledivanje o m og uava d a ob jek at tre tira te kao objekat sopstvenog tipa ili kao objekat natklase. To je veom a b itn o , je r o m o g u ava d a m n o g o tip ov a (izvedenih iz istog prosto g tip a) b u d e o b ra iv a n o kao a su svi jed n o g istog tip a i d a jed an isti kod radi jed n ak o sa svim tim razliitim tipo vim a. P olim orfni poziv m eto d e o m oguava da jed an tip izrazi svoju razliitost o d d ru g o g slinog tip a, dokle god su oba izvedena iz istog pro stog tipa. Ta razliitost se ispoljava u p o n a an ju m eto d a koje m oete d a pozovete preko o sno v ne klase. U ovom pogiavlju o p o lim o rfizm u (koji se tak o e naziva dinam iko vezivanje, kao i k a sno vezivanje ili vezivanje prilikom izvravanja) poi ete o d osnova, uz jed n o stav n e p rim ere koji ne sadre nita d ru g o sem p o lim o rfn o g p o n aan ja p ro gram a.

Ponovo o svoenju navie


U p re th o d n o m poglavlju videli ste kako objekat m oe da b u d e u p o tre b ljen kao objekat sopstvene klase ili kao objekat natklase. Kada referencu na objekat tre tira te kao referencu natklase, to se naziva svoenje navie (engl. upcasting) zbog naina crta n ja stabala nasleivanja sa o sn o v n o m klasom u vrh u . Videli ste tak oe da se pojavljuje i p ro blem p rikazan u sledeem p rim e ru s m u zikim in stru m e n tim a. Prvo, poto se u vie ovih p rim e ra sviraju n o te, tre b alo bi d a u n eko m p aketu n ap ravim o zaseban tip N o ta definisan nab rajan jem :
//: polimorfizam/muzika/Nota.java // Note koje e se s v ira ti na tnuzikim instrumentima. package polimorfizam.muzika publi c enum Nota ( SREDNJE_C, C_P0VISEN0,

B_SNIZEN0 ; // Itd .

> ///:-

212

Misliti na Javi

Nabrojane tipove prestavili smo u poglavlju Inicijalizacija i ienje. Ovde je Duvaki tip Instrumenta; zato je Duvaki nasleen od Instrumenta:
//: polimorfizam/muzika/Instrument.java package polimorfizam.muzika import s ta tic net.m indview .util.P r in t.* ; class Instrument { public void sviraj(N ota n) { print(''Instrum ent.sviraj ( ) " ) ;

} }

III-//: polimorfizam/muzika/Instrument.java package polimorfizam.muzika // Objekti klase Duvacki su instrumenti // je r imaju is t i in te rfe js : public class Duvacki extends Instrument { // Redefinisanje metode in te rfe js a : public void sviraj(N ota n) { System.out.printl n ("D u v a c k i.s vira j( ) " + n);

III--

//: polimorfizam/muzika/Instrument.java //: Nasleivanje i svoenje navie package polimorfizam.muzika public class Muzika { public s ta tic void melodija(Instrument i )

/ /
i.s v i raj(Nota.SREDNJE_C);

}
public s ta tic void m ain(String[] args) { Duvacki flauta = new Duvacki(); m e lo d ija(flau ta ); // Svoenje navie

}
} /* Isp is: D uvacki.sviraj() SREDNJE_C

* ///:M etoda Muzika.melodija() p rih v ata reference n a o b je k te klase Instrum ent.ali takode i na bilo ta to je iz te klase izvedeno. U to m o ete d a se uverite u m eto d i main(), u kojoj se m eto di melodija() p ro sle u je referenca na objek at klase Duvacki, a da nem a p o trebe za eksplicitnom ko n v erzijo m .T o je prihvatljivo, je r je klasa Duvacki nasle en a iz klase Instrument, pa u njoj m o ra da p o stoji interfejs klase Instrument. Svodenje navie iz klase Duvacki na klasu Instrument m oe da ,,suzi interfejs, ali o n nee biti m anji od p o tp u nog interfejsa klase Instrument.

Poglavlje 8: Polimorfizam

213

Zanemarivanje tipa objekta


M o da v am p ro g ra m M u zik a.ja v a izgleda n eobino. Z ato b i iko n a m e rn o zanem ario tip objekta? Ba to se deava p ri svo en ju navie. N aizgled je m n o g o loginije da arg u m e n t m eto d e m e Io d ija () b u d e referenca n a klasu D u v ack i. To nas d o v o d i d o kljunog dela: u to m sluaju, za svaki tip in s tru m e n ta m o rali biste da piete n o v u m e to d u m e lo d ija(). P retp o stav im o da tak av nain razm iljanja p rim e n ite i n a ostale in stru m en te:
//: polimorfizam/muzika/Muzika2.java // Preklapanje umesto svoenja navie package polimorfizam.muzika import s ta tic net.mindview.util .P r in t.* ; class Zicani extends Instrument { public void sviraj(N ota n) { p rin t("Z ica n i .s v ira j ( ) " + n);

} }
class LimeniDuvacki extends Instrument { public void sviraj(N ota n) { print("Lim eniD uvacki.sviraj( ) " + n);

} }
publi c class Muzi ka2 { public s ta tic void melodija(Duvacki i) { i . svi raj(Nota.SREDNJE_C);

}
public s ta tic void melodija(Zicani i) { i . svi raj(Nota.SREDNJE_C);

}
public s ta tic void melodija(LimeniDuvacki i) { i. s v i raj(Nota.SREDNJE_C);

}
public s ta tic void m ain(String[] args) { Duvacki flauta = new Duvackif); Zicani vio lin a = new Z ic a n i(); LimeniDuvacki francuskiRog = new LimeniDuvacki( ) ; m e lo d ija (fla u ta ); // Nem a svoenja navie m e lo d ija (v io lin a ); melodija(francuski Rog);

}
} /* Isp is: D u va ck i.svira j() SREDNJE_C Z ic a n i. s v ir a j () SREDNJEC Lim eniD uvacki.sviraj() SREDNJE_C

* ///:-

214

Misliti na Javi

Ovaj p ristu p dovodi d o eljenih rezu ltata, ali u z veliku m an u : za svaki novi Instrument koji dodate, m o ra te d a n ap iete p o se b n u m eto d u . To n a sa m o m p o etk u zahteva vie p ro g ram iran ja, ali tak o e znai da ete im ati i p u n o posla ako elite d a d o d a te n o v u m e to d u slinu m eto d i m elodija() ili n o v u p o tk lasu klase Instrument. D o d ajte to m e injenicu d a prevodilac nee p rijav iti p o ru k u o greci ako zab o rav ite da p rek lo p ite n eku m e to d u , p a ceo proces ra d a s tip o v im a p o staje v eo m a nep o d esan . Z ar n e bi bilo m n o g o lepe ako b iste m o g li d a napiete sam o je d n u m e to d u , iji b i arg u m en t bila osn ov n a klasa, a ne neka p o seb n o izvedena klasa? O d n o sn o , zar ne bi bilo fino ako biste m ogli z an em ariti in jen icu d a su to izvedene klase i m ogli da napiete p ro g ram tak o d a se ob raa sam o o sn o v n o j klasi? P olim orfizam v am om oguava ba to. M e u tim , veina p ro g ra m e ra sa iskustvom u p ro c e d u ra ln o m p ro g ra m ira n ju m a lo tee ra z u m e n ain n a koji p o lim o rfizam deluje.

Veba 1: (2) N apravite klasu Cikl s p o tk lasam a Unicikl, Bicikl i Tricikl. Pokaite da se p rim e ra k svakog o d tih tipova m e to d o m v o z iti( ) m oe svesti navie na Cikl.

Zakoljica
P roblem s p ro g ram o m Muzika.java m oe d a se u oi n ak o n p o k retan ja p ro g ram a. Rezultat e b iti poziv m eto d e Duvacki.sviraj(). D o b ijen je eljeni rezultat, ali ne izgleda login o zato to tak o radi. P o sm atrajte m e to d u melodija():
public s ta tic void melodija(Instrument i) {

/ /
i .sviraj(Nota.SREDNJE_C);

} O n a do b ija referencu na Instrument. Kako, o n d a, prevodilac uopte m oe znati da referenca na Instrument u stvari p o k azu je na objek at klase Duvacki, a ne na objekat klase LimeniDuvacki ili Zicani? Prevodilac i ne zna. Da biste ovo pitanje bolje razum eli, korisno je p ro u iti vezivanje.

Vezivanje poziva metoda


Povezivanje poziva m eto d e i tela m eto d e naziva se vezivanje (engl. biitciing). Kada se vezivanje obavi pre pok retan ja p ro g ram a (to rad e prevodilac i p ro g ram za povezivanje - engl. linker, ako postoji), to se naziva rano vezivanje (engl. early binding). M ogue je da za ovaj te rm in uopte niste uli zato to u p ro ce d u ra ln im jezicim a i nije bilo d ru g e m ogunosti. P rim era radi, C im a sam o jed an n ain za pozivanje m etoda, a to je ran o vezivanje. O n o to u p re th o d n o m p rim e ru zb u n ju je, o d n o si se na ran o vezivanje. Poto prevodilac im a sam o referencu na Instrument, ne m oe da zna koju m eto d u treb a da pozove. Reenje tog pro b lem a naziva se kasno vezivanje (engl. late binding), im e se oznaava da se vezivanje obavlja prilik o m izvravanja, u zavisnosti od tipa objekta. K asno vezivanje se jo naziva i dinam iko vezivanje (engl. d yn a m ic binding) ili vezivanje prilikom izvravanja (engl. runtim e binding). Kada je u n ekom jeziku o m o g u en o k asn o vezivanje, m ora

Poglavlje 8: Polimorfizam

215

p o sto jati m e h a n iz a m koji o d re u je ta a n tip o b jek ta p rilik o m izvravanja i koji e p o to m pozvati o d g o v araju u m e to d u . O d n o sn o , prev o d ilac i dalje ne zna tip objekta, ali m eh anizam za pozivanje to o tk riv a i poziva od g o v araju e telo m etode. M eh an izam kasnog vezivanja v arira o d jezika d o jezika, ali je jasn o d a u objekte m o ra da b u d e u g ra e n a nekakva in fo rm ac ija o tip u . U Javi se svi p ozivi m eto d a obavljaju p rek o k asn o g vezivanja, osim ako je m eto d a p ro glaena sta ti n o m ili finaln o m (p riv a tn e m eto d e su im p licitn o finalne). To znai d a o b ino n e m o ra te od lu iv ati o to m e da li e se k o ristiti kasno vezivanje - o n o se u p o treb ljav a au to m atsk i. Z bog ega biste n e k u m e to d u proglasili finalnom ? Kao to je u p re th o d n o m poglavlju n a p o m e n u to , tim e spreavate redefinisanje. V erovatno da je jo b itnije to tim e efektivno ,,isJjuujete d in am ik o vezivanje, o d n o sn o u k azu jete p rev o d io cu kako d in am ik o vezivanje nije n e o p h o d n o . To om o g u av a neto efikasnije pozivanje fin aln ih m etoda. U veini sluajeva, m e u tim , tako se nee o stv ariti opte pob o ljan je p erfo rm an si p ro g ra m a , pa je najbo lje da rezervisanu re fin a l k o ristite sam o kao posledicu projekta, a ne kao p o k u aj za poboljavanje p erfo rm an si.

Dobijanje pravilnog ponaanja


Im ajui na u m u da se svi pozivi in eto d a u Javi oliavljaju po lim o rfn o , preko kasnog vezivanja, p ro g ra m m o ete da piete tako da se o b raa osn o v n o j Jasi pa e p o u z d a n o ispravno rad iti i sa svim izveenim klasam a. D ru g im reim a,p o aljite p o ru k u o bjektu i p u stite ga da sam o tkrije ta treb a da u ra d i. Klasian p rim e r u O O P -u je onaj sa oblicim a. O n se esto koristi je r se lako p rikazuje ali, naalost, o n pro g ram ere poetn ik e m oe navesti da pogreno pom isle kako O O P slui sam o za p ro g ra m ira n je grafike, to, n arav n o , nije sluaj. P rim e r sa o b licim a im a o sn o v n u klasu p o d im e n o m O b lik i razne izvedene klase: K ru g , K v ad rat, T ro u g ao itd. O vaj p riin e r je veo m a p o g o d an , jer je lako pojm ljivo kada kaem o k ru g je oblik . M e u so b n i o d n o si su p o k azan i na d ijag ram u nasleivanja:

Svoenje navie kroz dijagram nasleivanja

K v a d ra t

Trougao
nacrtaj()

Identifikator objekta Krug

nacrtajd obrii

obrii

216

Misliti na Javi

Svoenje navie m oe d a se pojavi ak i u ovako jed n o stav n o j naredbi:


Oblik o = new Krug();

U ov o m sluaju, pravi se objekat klase Krug, a njegova referen ca se o d m a h dodeljuje p ro m enljivoj tip a Oblik, to je naizgled greka (jer jed an tip d o d elju jem o d ru g o m ), a op et to je u red u jer, zbog nasleivanja, Krug jeste Oblik. Stoga se p revodilac slae s p reth o n o m n a re d b o m i ne prijavljuje p o ru k u o greci. P retp o stav im o d a p o zovete n ek u m e to d u o sn o v n e klase (koja je red efin isan a u izveden im klasam a):
o .n a crta j( ) ;

M ogli biste, ponovo, oekivati kako e b iti p o zv an a m eto d a nacrtajO klase Oblik, p o to je to ipak referenca n a Oblik - kako bi, o n d a, prev o d ilac m o g ao bilo ta d ru g o da u ra di? A ipak, zbog kasnog povezivanja (p o lim o rfizm a) poziv a se o d g o v araju a m e to d a

Krug.nacrtajO.
N ared n i p rim e r p o kazuje taj p rin c ip n a neto d rugaiji nain . N ajpre em o n ap rav iti v iek ratn o u p otreb ljiv u b ib lio tek u tipo va Oblika:
//: polimorfizam/oblik/Oblik.java package polimorfizam.oblik; public class Oblik { public void n a crta j() {} public void o b ris i() { }

III--

//: polimorfizam/obl i k/Krug.java package polimorfizam.oblik; import s ta tic net.m indview.util.P r in t.* ; public class Krug extends Oblik { public void n a c rta j() { p rin t("K ru g .n acrta j( ) " ) ; } public void o b ris i() { print("K ru g.obrisi ( ) " ) ; }

} ///:// : polimorfizam/obli k/Kvadrat.java package polimorfizam.oblik; import s ta tic net,mindview.uti1. P rin t.* ; public class Kvadrat extends Oblik { public void n a crta j() { p rin t("K v a d ra t.n a crta j( ) " ) ; } public void o b ris i() { p rin t("K v a d ra t.o b risi( ) " ) ; }

III--

polimorfizam/oblik/Trougao.java package polimorfizam.oblik; import s ta tic net.m indview.util.P r in t.* ;


//:

Poglavlje 8: Polimorfizam

217

public class Trougao extends Oblik { public void n a crta j() { print{"Trougao.nacrtaj( ) " ) ; } public void o b ris i() { print("T ro ugao.obrisi( ) " ) ; }

III--

// : pol imorfizam/obli k/GeneratorSlucajnihOblika.java //: "Proizvodjac" koji nasumino proizvodi oblike. package polimorfizam.oblik; import ja v a .u til public class GeneratorSlucajnihOblika { private Random slucajan = new Random(47); public Oblik next() { sw itch(slucajan.nextlnt(3)) { default: case 0: return new Krug(); case 1: return new Kvadrat(); case 2: return new Trougao();

} }

1 III--//: polimorfizam/Oblici.java // Polimorfizam u Javi import polimorfizam.oblik.*; public class Oblici { private s ta tic GeneratorSlucajnihOblika gen = new GeneratorSlucajnihOblikaO; public s ta tic void main(String[] args) { 0 b lik [] o = new Obl i k [9]; // Popuni niz oblicima: fo r(in t i = 0; i < o.length; i++) o [i] = gen.next(); // Obavi polimorfne pozive metoda: fo r(0 b lik obl : o) obli k .n a crta j( ) ;

}
} /* Isp is: Trougao.nacrtajO Trougao.nacrtaj() Kvadrat.nacrtaj() Trougao.nacrtaj() Kvadrat.nacrtaj() Trougao.nacrtaj() Kvadrat.nacrtaj() Trougao.nacrtaj() Krug.nacrtaj()

* ///:-

218

Misliti na Javi

O sn o v n a klasa Oblik ustanovijava zajedniki interfejs za sve to je iz nje nasleeno - od n o sn o , svi oblici m o g u d a b u d u n a c rta n i i ob risani. Izvedene klase redefiniu ove m etod e da b i obezbedile od g o v araju e p o n aan je za specifian oblik. Klasa GeneratorSlucajnihOblika je neka vrsta ,,proizvoaa koja vraa referencu na sluajno o d a b ra n tip o blika, svaki p u t k ad a pozovete n jen u m e to d u next(). P ri svakoj nared bi return obavlja se svoenje navie, p ri em u se referenca n a Krug, Kvadrat ili Trougao alje iz m eto d e next() i p retv ara u p o v ra tn i tip Oblik. Z ato k ad g o d pozovete next(), nik ad a ne v id ite koji je tip o d a b ra n , je r kao p o v ra tn u v red n o st uvek d o b ijate referencu na

Oblik.
M eto d a m ain() sadri niz referenci n a objekte klase Oblik; taj niz se p o p u n ja v a poziv im a m e to d i GeneratorSlucajnihObIika.next(). U to m tre n u tk u znate d a im ate neke objekte klase Oblik ali n e zn ate n ita preciznije (n iti to zn a p revodilac). M e u tim , kada p ro ete kroz taj niz i za svaki elem en t pozovete m e to d u nacrtajO, zbog neke arolije, svaki tip p o in je d a se p ra v iln o p o n aa. U to se m oete u veriti iz rezu ltata p ro g ra m a kada ga p okren ete. N asu m in o pravljenje o blika treb a da vam dokae kako prev o d ilac n e m oe zn ati ita to bi m u p o m o g lo da p rav iln o poziva m eto d e p rilik o m prev o enja. Svi pozivi m eto d i nacrtajO m o ra ju biti d in am ik i vezani. V eba 2: (1) U p rim e r sa oblicim a d o d ajte ano taciju @ O verrie. V eba 3: ( I ) D o d ajte n o v u m e to d u osnovnoj klasi u dato teci O b lici.ja v a koja ispisuje p oru k u , ali n e m o jte d a je redefiniete u izvedenim klasam a. O b jasn ite ta se deava. Sada je redefiniite u sam o jed n o j izvedenoj klasi i v idite ta se deava. K onano, redefiniite je u svim izvedenim klasam a. V eba 4: (2) D o dajte n o v u p o tk lasu klase O b lik u d ato teci O b lic i.jav a i p o tv rd ite u m cto d i m a in () da p o lim o rfizam rad i za novi tip kao i za stare tipove. V eba 5: (1) U vebi 1, klasi C ikl dodajte m e to d u to ck o v i() koja vraa broj tokova. Izm enite m eto d u v o z iti() tako da poziva m eto d u to ck o v i() i uverite se da p olim orfizam radi.

Prosirivost
V ratim o se sada p rim e ru s m u zik im in stru m e n tim a . P olim orfizam o m o g u u je da to m sistem u d o d ate koliko god elite novih in s tru m en ata, bez p o treb e d a m en jate m eto d u m eIo d ija (). U d o b ro p ro jek to v an o m O O P p ro g ram u , veina m eto d a ili sve nae m eto d e p ratile bi m o d el p rim e n jen na m e to d u m eIo d ija() i k o m u n ik aciju obavljale sam o preko interfejsa o sno v n e klase. Takav p ro g ram je proiriv ; jer m oete da m u d o d ate novu fu nkc io n aln o st ako nove tipove p o d atak a nasledite iz zajednike o sn o v n e klase. M etode koje obavljaju neki p osao preko interfejsa o sn o v n e klase, u o p te ne treb a da m en jate da bi prihvatile i nove klase. R azm o trite ta se deava ako u p rim e ru sa in stru m e n tim a d o d a m o nove m eto d e u o sn o v n u klasu, kao i izvestan broj n o v ih klasa. D ijagram hijerarh ije nasleivanja d a t je na slici:

Poglavlje 8: Polimorfizam

219

Sve ove nove klase p rav iln o rade sa starom i n e izm en jen o m m e to d o m m elo d ija (). ak i ako se m eto d a m e lo d ija () nalazi u posebnoj d a to te i ako se interfejsu klase In s tru m e n t d o d a ju n o \re m etode, m e lo d ija () e, bez po no v n o g prev o en ja, p rav iln o raditi. Evo i realizacije dijagram a:
//: polimorfizam/muzika3/Muzika3.java // Pro iriv program package polimorfizam.muzika3; import polimorfizam.muzika.Nota; import s ta tic net,mindview.uti1 .P rin t.*; class Instrument { void sviraj(N ota n) { p rin t("In stru m en t.sviraj() " + n ); } String sta () { return "Instrument"; } void nastimuj() { p r in t( timovanje instrumenta"); }

}
class Duvacki extends Instrument { void sviraj(N ota n) { print("D uvacki. s v ir a j () " + n); } String sta () { return "Duvacki"; } void nastimujf) { print("timovanje duvakog"); }

220

Misliti na Javi

class Udaraljke extends Instrument { void sviraj(N ota n) { print("Udaral jk e .s v ira j () " + n ); } String s ta () { return "U daraljke"; } void nastimuj() { print("tim ovanje u d araljke"); }

}
class Zicani extends Instrument { void sviraj(N ota n) { p rin t("Z ic a n i .s v ira j () " + n ); } String s ta () { return "Z ic a n i"; } void nastimuj() { p r in t('timovanje ianog"); }

}
class LimeniDuvacki extends Duvacki { void sviraj(N ota n) { print("LimeniDuvacki . s v ir a j( ) + n ); } void nastimuj() { print("Lim eniD uvacki.nastim uj()"); }

}
class DrveniDuvacki extends Duvacki { void sviraj(N ota n) { print("DrveniDuvacki .s v ir a jO String s ta () { return "DrveniDuvacki"; }

" + n ); }

}
public class Muzika3 { // Ne vodi rauna o tipovima, pa e novi tipovi // koje dodamo sistemu i d alje pravilno r a d iti: public s ta tic void melodija(Instrument i) {

/ /
i .sviraj(Nota.SREDNJE_C);

}
public s ta tic void sveMelodije(Instrument[] e) { for(Instrument i : e) melodi j a ( i );

}
public s ta tic void m ain(String[] args) { // Svoenje navie prilikom dodavanja u niz: Instrument[] orkestar = { new Duvacki( ) ; new Udaral j k e ( ) ; new Z ic a n i( ) ; new LimeniDuvacki( ) ; new DrveniDuvacki( ) ;

};
sveM elodije(orkestar);

}
} /* Isp is: Duvacki ,s v ir a j( ) SREDNJE_C U d a raljk e .svira jO SREDNJE_C

Poglavlje 8: Polimorfizam

221

Z ic a n i.s v ir a j() SREDNJE_C Lim eniDuvacki.sviraj() SREDNJE_C DrveniDuvacki.svirajO SREDNJE_C * ///=-

N ove su m e to d a sta(), koja v ra a referen cu n a ob jek at klase String u kojem je o pis klase, i m e to d a nastimuj() koja o m o g u av a da se svaki in s tru m e n t n a neki nain natim uje. Svaki p u t k ada neto u b acite u niz orkestar u m eto d i m ain(), a u to m atsk i obavljate svoenje navie ka klasi Instrument. M eto da meIodija() je p o tp u n o im u n a na sve izm ene u p ro g ra m u i rad i p ravilno. Polim o rfizam ba to om oguava. Izm en e u k o d u n e rem ete delove p ro g ra m a n a koje i ne bi tre b alo da utiu. D ru g im reim a, p o lim o rfiza m je vana te h n ik a koja p ro g ra m e ru o m o guava da razdvoji o n o to se m e n ja o d o n o g to ostaje isto.

Veba 6: (1 ) P ro m e n ite d a to tek u Muzika3.java tak o d a m e to d a sta() p o stan e m e to d a toStringO iz korenske klase Object. P ro b ajte d a objekte klase Instrument ispisujete p o m o u m eto d e System.out.println() (bez ikakve konverzije). Veba 7: (2) U d a to te k u Muzika3.java d o d ajte n o v u p o tk lasu klase Instrument i uverite
se da p o lim o rfizam rad i i za n o v i tip.

Veba 8: (2) Izm enite d a to te k u Muzika3.java tako da objekte klase Instrument p rav i na sluajan nain kao u dato teci Oblici.java. Veba 9: (3) N apravite h ijerarh iju nasleivanja klase Glodar: Mis, Pacov, Hrcak itd.
U o sn o v n o j klasi obezb ed ite m e to d e koje su zajednike za sve glodare i redefiniite ih u izvedenim klasam a, tako da se razliito p o n aaju za razne tipove glodara. N apravite niz objek ata klase Glodar, p o p u n ite ga razn im o b jek tim a p otklasa klase Glodar, pozovite m eto d e definisane u o sn o v n o j klasi i v idite ta se dogaa.

Veba 10: (3) N apravite o sn o v n u klasu s dve m etode. Pozovite d ru g u m eto d u iz prve m etode. N asledite tu klasu i redefiniite d ru g u m eto d u . N apravite objekat izvedene klase, svedite ga navie d o njegovog pro sto g tip a i pozovite prv u m etod u. O bjasnite ta se deava.

Greka: redefinisanje" privatnih metoda


Evo neega to biste u blaen o m n ezn an ju m ogli pok u ati d a uradite:
//: pol imorfizam/RedefinisanjePrivatnih.java // Pokuaj redefinisanja privatne metode. package polimorfizam import s ta tic net.m indview .util.P r in t.* ; public class RedefinisanjePrivatnih { private void f ( ) { p rin tf'p riv a tn a f ( ) ) ; } public s ta tic void m ain(String[] args) { Redefin isa n je P riv a tn ih rp = new Izvedena(); r p .f();

222

Misliti na Javi

class Izvedena extends RedefinisanjePrivatnih { public void f ( ) { p rin t("ja vn a f ( ) " ) ; } } /* Isp is: privatna f ( )

* ///:M ogli biste razlono oekivati d a rezu ltat b u d e javna f(), ali je p riv atn a m e to d a a u to m atski finabia, a i sakrivena je o d izvedene klase. Z ato je u ovo m sluaju m eto d a f() klase Izvedena p o tp u n o nova m eto d a; o n a nije ak ni p reklop ljen a, p o to u klasi Izvedena nije vidljiva verzija f() iz o sn o v n e klase. R ezultat svega ovoga jeste d a sam o n ep riv atn e m etod e m o g u b iti redefinisane, ali se m o rate uvati i od redefinisanja p riv atn ih m e to d a prevodilac vas n iim nee upo zo riti na to, ali se pro gram verovatno nee po naati o nako kako ste hteli. D a b u d e m o p o tp u n o jasni, u izvedenoj klasi upotrebljavajte im ena drugaija od im ena p riv atn e m eto d e iz osnovne klase.

Greka: polja i statine metoda


Poto ste sada nauili ta je p o lim o rfizam , m o gli biste p o m isliti da se sve deava p o lim o rfno. M e u tim , p o lim o rfn i m o g u biti sam o o b in i pozivi m eto d a. N a p rim er, ako po lju p ristu p ite n ep o sred n o , taj p ristu p e b iti razreen u v rem e p rev o enja, k ao to pokazu je' sledei p rim er:
//: polimorfizam/PristupPolju.java // Neposredno pristupanje polju biva razreeno u trenutku prevoenja. class Nad { public int polje = 0; public int p rib aviPolje ()

{ return po lje; }

class Pod extends Nad { public in t polje = 1; public in t prib aviPolje () { return po lje; ) public in t pribaviNadPolje () { return nad.polje; }

}
public class PristupPolju { public s ta tic void m ain(String[] args) { Nad nad = new Pod(); // Svoenje navie System .out.println("nad.polje = " + nad.polje + ", nad.pribaviPolje() = " + nad. p r ib a v iP o lje ()) ; Pod pod = new Pod(); System.out.println("pod.pol je = " + pod.polje + ", pod.pribaviPolje() = + pod.pribaviPolje() + ", pod.pribaviNadPolje() = " + pod.pribaviNadPolje()) ;

}
1 Z a h v a lju je m R en d iju N ik o lsu koji se s e tio d a p o sta v i to p ita n je .

Poglavjje 8: Polimorfizam

223

( /* Is p is : nad.polje = 0, nad.pribaviPolje() = 1 pod.polje = 1, po d.pribaviPolje() = 1, pod.pribaviNadPolje = 0


* / / / :-

Kada se Pod objekat svede navie na Nad referencu, sva p ristu p an ja po ljim a razreava prevodilac, p a o na zato nisu polim o rfn a. U ovom p rim eru , Nad.polje i Pod.polje ne do b ijaju isti m em orijski prostor. Z ato Pod zapravo sadri dva polja Polje: sopstveno i o n o koje dobija o d klase Nad. M e u tim , Nad verzija nije o n a p o d razu m ev an a koja se d o b ija kada izraz u pu uje na polje u klasi Pod; ako hoete Nad polje, m o rate izriito napisati nad.polje. Iako m o d a izgleda da b i ovo m o g lo izazvati zab u n u , u p raksi se to gotovo n ik ad a n e deava. Prvo, p o p rav ilu sva p o lja tre b a d a b u d u p riv atn a, p a im neete p ristu p a ti n ep o sredn o , nego sam o kao sp o red n i efekat p o zivanja m eto d a. Uz to, v erovatno neete d ati isto im e p o lju osnov n e klase i p o lju izvedene klase, je r biste tako izazvali zbrku. Ako je m e to d a statina, o n a se n e p o n a a p o lim o rfn o :
//: polimorfizam/Stati cni Polimorfi zam.java // Staticne metode nisu polimorfne. class StaticnaNad { public s ta tic String s ta tic n a P rib a vi() return "Osnovna s ta tic n a P rib a vi( ) " ;

}
public String dinamicnaPribavi() { return "Osnovna dinamicnaPribavi( ) " ;

} }
class StaticnaPod extends StaticnaNad { public s ta tic String s ta tic n a P rib a vi() { return "Izvedena s ta tic n a P rib a vi( ) " ;

}
public String dinamicnaPribavi() { return "Izvedena dinamicnaPribavi( ) " ;

public class PolimorfizamStaticnih { public s ta tic void m ain(String[] args) { StaticnaNad nad = new StaticnaPod( ) ; // Svoenje navie System.out. println(nad .staticnaPribavi ( ) ) ; System.out.pri ntln(nad.dinami cnaPri b a v i( ) ) ;

}
} /* Isp is: Osnovna s ta tic n a P rib a vi() Izvedena dinamicnaPribavi()

* ///:Statine m e to d e su p rid ru e n e klasam a, a ne p o jed in an im o b jektim a.

224

Misliti na Javi

Konstruktori i polimorfizam
K o n struktori se, kao i obino, razlikuju o d ostalih m etoda. To vai i u sluaju p olim orfizm a. Iako k o n stru k to ri nisu po lim orfni (to su zapravo statine m etode, ali je njihova deklaracija statinosti im plicitna), b itn o je da shvatite nain na koji o n i rade u sloenim hijerarh ijam a i s polim orfizm om . To znanje e vam p o m o i da izbegnete n ep rijatn a zapetljavanja.

Redosled poziva konstruktora


Redosled poziva k o n stru k to ra je kratk o razm atran u poglavlju Inicijalizacija i ienje i p o novo u poglavlju Ponovno korienje klasa, ali je to bilo p re nego to sm o uveli p olim orfizam . U pro cesu konstru k cije izvedene klase uvek se poziva k o n stru k to r o sn o v n e klase. Taj poziv se au to m atsk i p o m e ra navie p o stablu hijerarhije, tako da b ivaju p o zv an i svi k o n stru k to ri o sn o v n ih klasa. To im a sm isla, jer k o n stru k to r im a p o seb a n zadatak: da se p o b rin e d a objekat b u d e p rav iln o n apravljen. Izvedena klasa im a p ristu p sam o svojim lan ovim a, ali ne i lan o v im a osn o v n e kiase (koji su o b in o p riv a tn i). S am o k o n stru k to r osno v ne klase im a o dgovarajue zn an je i p rav a p ristu p a za inicijalizaciju e lem en ata te klase. Stoga je n e o p h o d n o d a b u d u p o zvan i svi k o n stru k to ri, jer, u su p ro tn o m , nee b iti n apravljen ceo objekat. Z bog toga p revoilac insistira da b u d e pozv an k o n stru k to r za svaki d eo izvedene klase. U koliko u telu k o n stru k to ra izvedene klase izriito ne pozovete k o n stru k to r osnov n e klase, prevodilac e au to m atsk i da pozove njen p o d ra z u m ev a n i k o n stru k to r. Ako takav k o stru k to r ne postoji, prevodilac e prijav iti greku. (K ada klasa nem a n ijedan k o n stru k to r, prevodilac au to m atsk i pravi p o d razu m ev an i.) Pogledajm o p rim e r koji pokazuje efekte kom pozicije, nasleivanja i p o lim o rfizm a na p o re d a k konstrukcije:
//: polimorfizam/Sendvic.java // Poredak poziva konstruktora package polimorfizam import s ta tic net.m indview .util.P r in t.* ; class Obrok { Obrok() ( p rint("Obrok( ) " ) ; }

}
class Hleb { Hleb() { p r in t("H le b ()"); }

}
class S ir { S ir ( ) { p r in t ( " S ir ( ) " ) ; }

}
class Salata { SalataO { p r in t (" S a la t a ()" ) ; }

Poglav[je 8: Polimorfizam

225

class Rucak extends Obrok { Rucak() { p rin t("R u c a k ()");}

}
class RucakZaNosenje extends Rucak { RucakZaNosenje() { print("RucakZaNosenje() ) ;

}
public class Sendvic extends RucakZaNosenje { private Hleb h = new Hleb(); private S ir s = new S ir'O ; private Salata sl = new S a la ta (); public Sendvic () { p r in t(Sendvic ( ) " ) ; } public s ta tic void m ain(String[] args) { new Sendvic ( ) ;

}
} /* Is p is : Obrok() Rucak() RucakZaNosenjeO Hleb() S ir ( ) S a la ta () SendvicO

* ///:U ovom p rim e ru , sloena klasa se pravi o d d ru g ih klasa. Svaki k o n stru k to r ispisuje p oru k u kada je pozvan. N am a je b itn a klasa S en d v ic koja o b jed in ju je tri n ivoa nasleivanja (etiri, ako b ro jite i im p licitn o nasledivanje klase O b je ct) i tri objekta lana. R ezultat se vidi nakon pravljenja objekta klase S en d v ic u m eto d i m a in (). To znai d a je redosled poziva k o n stru k to ra sloenog objekta sledei: 1. Poziva se k o n stru k to r osn o v n e klase. O vaj k o rak se rekurzivno p onavlja tako d a se prvo k o n stru ie koren u hijerarhiji, n ak o n njega prva izvedena klasa itd., sve d o k se ne d o d e d o krajnje izvedene klase. 2 . Pozivaju se inicijalizatori lanova i to po red u njihove deklaracije. 3 . Poziva se telo k o n stru k to ra izvedene klase. R edosled poziva k o n stru k to ra je veom a b itan. P rilikom nasleivanja, o osno v n o j klasi znate sve i m oete da p ristu p ite svim n jen im jav n im i zatienim lanovim a. To znai da m oete p retp ostav iti kako su, kada se na ete u izvedenoj klasi, svi lanovi o sn o v n e klase ispravni. Kada se naete u obinoj m etodi, konstrukcija je ve obavljena, p a su svi lanovi svih delova objekta napravljeni. Ali kada ste u k o n stru k to ru , m o rate biti sig u rn i da su svi lanovi klase koju nasleujete ve napravljeni. Jedini nain da se to g aran tu je jeste da se prvo pozove k o n stru k to r osnovne Idase. N akon toga, kada se na ete u k o n stru k to ru izved en e klase, svi lanovi o snovne kiase kojim a m oete da p ristu p ite prav iln o su inicijalizovani. To to m o ra te z n a ti da su svi lanovi ispravni u n u ta r k o n stru k to ra tak o e je razlog da, kada god to m oete, inicijalizujete sve objekte lanove (tj. objekte koje u klasu um eete

226

Misliti na Javi

k om pozicijom ) na m estu njihove definicije u kiasi (npr. h, s i sl u p re th o d n o m p rim eru ). A ko ovaj p o stu p a k p otujete, biete sigurniji da su svi lanovi klase i objekti lanovi tekueg objekta inicijalizovani. N aalost, tim e ne p okrivate sve sluajeve, kao to ete videti u n are d n o m odeljku.

Veba 11: (1) D atoteci Sendvic.java d o d ajte klasu KiseliKrastavcic.

Nasleivanje i ienje
K ada n o v u klasu prav ite k o m p o zicijo m i n asledivanjem , uglav n o m neete m o ra ti da brin ete o ciscenju; p o d o b jek ti se o b in o m o g u p rep u stiti sakupljau sm ea. U koliko ienje ip ak treb a d a obavite, za svoju n o v u klasu m o rate sam i da n ap rav ite n eku m e to d u ciscenje() (ako im ate bolje im e, n azovite je drugaije). I p rilik o m nasleivanja, u izvedenoj klasi m o rate da redefiniete m e to d u ciscenje() ako je p rilik o m saku p ljanja sm ea p o tre b n o nekakvo p o seb n o ienje. Kada u izvedenoj klasi redefiniete m e to d u ciscenje(), b itno je d a se setite da pozovete verziju te m eto d e iz osn o v n e klase, inae o sn o v n a klasa nee biti oiena. To dokazuje n a red n i p rim er:
//: polimorfizam/Zaba.java // ienje i nasleivanje. package polimorfizam; import s ta tic net.mindview.uti1 .P rin t.*; class Karakteristika { private String s; Karakteristika(String s) { th is .s = s ; p rin t( "Pravljen je Karakteristike " + s );

}
protected void ciscen je() { p rin t(" i e n je Karakteristike " + s ) ; }

} }
class Opis { private String s; Opis(String s) { th is .s = s ; p rin t("P ra v lje n je Opisa " + s ) ;

}
prottected void ciscen je() { p rin t(" i e rje Opisa " + s );

} }
class ZivoBice { private Karakteristika p = new K a rak teristik a("ivo j e ); private Opis t =

Poglavlje 8: Polimorfizam

227

rew OpisC'Elementarno ivo b i e "); Zivo Bice() { p r in t("Z iv o B ic e ()");

}
protected void cisce n je() { prin t("Z ivo Bice - ie n je"); t.c is c e n je () p.ciscenjeO

class Z ivo tin ja extends ZivoBice { private Karakteristika p = new Karakteristika("im a src e "); private Opis t = new O pis("ivotinja je , n ije b iljk a " ); Z iv o tin ja f) { p r in t(" iv o tin ja ()" ) ; } protected void ciscenje() { p r in t("Z iv o tin ja - i e n je "); t .c is c e n je O ; p .c is c e n je O ; super.ciscenjeO ;

} }
class Vodozemac extends Zivotinja { private Karakteristika p = new Karakteristika("moe da ivi u vo d i"); private Opis t = new O p is("I u vodi i na z e m lji"); Vodozemac() { pri nt ("VodozemacO" ) ;

}
protected void ciscenjeO { p r in t("Vodozemac - ie n je "); t . ci sc e n je (); p .c is c e n je O ; su p e r.ciscen je f);

} }
public class Zaba extends Vodozemac { private Karakteristika p = new K arakteristika("K rekee"); private Opis t = new Opis("Jede bube"); public Zaba() { p rin t("Z a b a ()" ) ; } protected void ciscen je() { print("Zaba - i e n je "); t . c i sc e n je f); p.ciscenjeO ; su p e r.c isc e n je ();

228

Misliti na Javi

public s ta tic void m ain(String[] args) { Zaba zaba = new Zaba(); p rint("Z dravo!" ) ; Z aba.ciscenje();

}
} /* Isp is: Pravljenje Karakteristike ivo je Pravljenje Opisa Elementarno ivo bie ZivoBiceO Pravljenje Karakteristike ima srce Pravljenje Opisa Z ivo tin ja je , n ije b iljk a Z ivo tin ja O Pravljenje Karakteristike moe da iv i u vodi Pravljenje Opisa I u vodi i na zemlji VodozemacO Pravljenje Karakteristike Krekee Pravljenje Opisa Jede bube Zaba() Zdravo! Zaba - ienje ienje Opisa Jede bube ienje Karakteristike Krekee Vodozemac - ienje ienje Opisa I u vodi i na zemlji ienje Karakteristike moe da ivi u vodi Zivotinja - ienje ienje Opisa Z ivotinja je , n ije b iljk a ienje Karakteristike ima srce ZivoBice - ienje ienje Opisa Elementarno ivo bie ienje Karakteristike ivo je

* ///:Svaka klasa u h ijerarh iji sadri i objekte lanove tipova K a ra k te ristik a i O p is, koji takode m o raju biti oieni. R edosled ienja treb a da je su p ro ta n red o sled u inicijalizacije, za sluaj da je d an p o d o b jek at zavisi od dru g o g . Za polja to znai da redosled ienja treba da je su p ro tan redosledu d eklarisanja (p o to se p olja inicijalizuju o n im red o m kojim su deklarisana). Za o sn o v n e klase (p ratei fo rm u u p o tre b ljen u za d estru k to re u C + + -u ) prv o treba da oistite izvedene klase, a zatim i o sn o v n e klase. R azlog je to to p ri ienju izvedene klase m oete d a pozovete neke m eto d e iz o sn o v n e klase koje zahtevaju da kom p o n e n te osnovne klase jo b u d u ive, pa ne sm ete da ih u n itite pre vrem en a. Iz rezultata p re th o d n o g p ro g ra m a v idite da se delovi objekta Z ab a iste re d o m su p ro tn im redosledu kojim su pravljeni. Iz ovog p rim era vidite sledee: iako ne m o ra te uvek sam i da istite, o n d a kada m orate, taj proces zahteva p anju i znanje. V eba 12: (3) Izm enite vebu 9 tako da prikazuje reosled inicijalizacije u osnovnoj klasi i u izvedenim klasam a. Sada i osn o v n o j klasi i izvedenim klasam a d o d ajte objekte lanove i pokaite kojim redosledom se inicijalizuju p rilik o m konstru k cije.

Poglav[je 8: Polimorfizam

229

Povedite ra u n a i u to m e d a u g o rn jem p rim e ru ob jek at Z a b a p o se d u je svoje objekte lanove. O n ih p ravi i zna koliko d u g o treb a d a ive (koliko i Z a b a ), p a zn a kada da pozove ciscenje() za te objekte lanove. M e u tim , ukoliko je d a n ili vie d ru g ih objekata p o sed u ju neki o d tih objekata lanova, zad atak p ostaje sloeniji, p a vie n e m oete p ro sto d a p re tpo stav ite kako je d o v oljn o p o zv ati ciscen je(). U to m sluaju, m o d a e vam b iti p o tre b n o brojanje referenci d a biste znali koliko objek ata jo uvek p ristu p a d eljen o m (zajed n ik o m ) ob jek tu . Evo kako to izgleda:
//: polimorfizam/BrojanjeReferenci.java // ienje deljenih objekata lanova. import s ta tic net.m indview .util.P r in t.* ; class Deljena { private in t brojacref = 0; private s ta tic long brojac = 0; private fin al long id = brojac++; public DeljenaO { p rin t("P ra v l jenje 1 1 + th is );

}
public void dodajRef() { brojacref++; } protected void c isce n je() { if(- - b ro jacre f = = 0) p rint(" ienje " + th is );

}
public String toStringO { return "Deljena" + id ;}

class Kompozicija { private Deljena deljena; private s ta tic long brojac = 0; private fin al long id = brojac++; public Kompozicija(Deljena deljena) p rin t("P ra v lje n je " + th is ); this.d eljena = deljena; th is.d elje n a .d o d a jR ef();

}
protected void ciscen je() { p rin t(" i e n je " + th is ); del jena.ciscenjeO

}
public String toStringO { return "Kompozicija " + id ;}

public class BrojanjeReferenci { public s ta tic void m ain(String[] args) { Deljena deljena = new D eljena(); Kompozicija[] kompozicija = { new Kompozicija(deljena), new Kompozicija(deljena), new Kompozicija(deljena), new Kompozicija(deljena), new Kompozicija(deljena) } ;

230

Misliti na Javi

for(Kompozicija c : kompozicija) c .c is c e n je O ;

}
} /* Isp is: Pravljenje Deljena 0 Pravljenje Kompozicija Pravljenje Kompozicija Pravljenje Kompozicija Pravljenje Kompozicija Pravljenje Kompozicija ienje Kompozicija 0 ienje Kornpozicija 1 ienje Kompozicija 2 ienje Kompozicija 3 ienje Kompozicija 4 ienje Deljena 0

0 1 2 3 4

* ///.Statian brojac tip a long p ra ti bro j n ap rav ljen ih p rim e ra k a o b jek ta tip a Deljena i daje v re d n o st prom enljivoj id. T ip brojaca je long u m esto int da b i se izbeglo p rek o raen je (u ovom sluaju, to je sam o d o b ra praksa; v ero v atn o n ijed an p rim e r iz ove knjige nee p ro u zrok o vati prekoraenje takvog brojaa). P rom enljiva id je fin a ln a , zato to ne oekujem o da e m en jati v red n o st to k o in ivotnog veka ovog objekta. Kada svojoj klasi p rid ru ite deljeni objekat, ne sm ete zabo rav iti da pozovete dodajRef(), ali e m eto d a ciscenje() p ra titi broj referenci i odlu iti kada da obavi ienje. Ova teh n ik a zahteva o d a ta n tru d , ali ako su objekti koji zahtevaju ienje deljeni, nem ate m n o g o izbora.

Veba 13: (3) D atoteci BrojanjeReferenci.java d o d ajte m e to d u finalize() da biste p roverili postoji li stanja okonanja (videti poglavlje Inicijalizacija i ienje). Veba 14: (4) Izm enite vebu 12 tako da jed an o d objekata lanova b u d e eljen s b rojan jem referenci i pokaite da to ispravno radi.

Ponaanje polimorfnih metoda unutar konstruktora


H ijerarhija poziva k o n stru k to ra izaziva zanim ljivu n ed o u m icu . ta e se desiti ako se nalazite u n u ta r k o n stru k to ra i pozovete dinam ik i p o v ezan u m e to d u objekta ija je konstru k cija u toku? U n u tar obine m etode, dinam ik i povezan poziv bio bi razreen prilikom izvravanja, je r objekat ne m oe da zna da li on p rip ad a klasi u kojoj se m eto d a nalazi ili nekoj drugoj klasi koja je iz nje izvedena. Ako dinam iki povezanu m eto d u pozovete iz k o n stru k to ra , bie u p o treb ljen a redefinisana verzija te m etode. Ejekat m oe da b u d e p rilin o neoekivan, zato to se redefinisana verzija m etode poziva p re zavretka k o n stru isan ja objekta, a to m oe izazvati greke koje se veom a teko pronalaze. Po k o ncep tu, posao k o n stru k to ra je da objekat dovede u ivot (to je prilian podvig). U n u ta r svakog ko n stru k to ra, ceo objekat m oe biti sam o d elim in o fo rm iran - m oete da znate sam o da su inicijalizovani objekti o sn o v n e klase. Ako je izrada k o n stru k to ra sam o

Poglav|je 8: Polimorfizam

231

jed an k o rak u pravljenju objekta klase koja je izvedena iz klase to g k o n stru k to ra , u v rem e poziva tekueg k o n stru k to ra izvedeni delovi jo n isu inicijalizovani. M ed u tim , d in am ik i povezan poziv see ka spoljanjosti1 1h ijerarh ije nasleivanja. O n poziva m e to d u izvedene klase. Ako ga iskoristite u n u ta r k o n stru k to ra , pozvaete m e to d u koja m o d a rad i sa lan ov im a koji jo uvek nisu inicijalizovani, to je sig u ran recep t za k atastrofu. P rob lem m oete d a sagledate u n a re d n o m p rim e ru :
//: polimorfizam/PoliKonstruktori.java // Konstruktori i polimorfizam // ne pruaju ono to moda oekujete. import s ta tic net.m indview.util.P r in t.* ; class G lif { void c r t a j( ) {

print ("Gl i f . crta j ( ) " ) ;

GlifO {
p r in t { " G lif ( ) pre metode c r t a j ( ) " ) ; c rta j ( ) ; p r in t ( " G lif ( ) posle metode c r t a j ( ) " ) ;

} }
class O krugliG lif extends G lif { private in t poluprecnik = 1; Okrugl iGl if (in t r) { poluprecnik = r; p r int("O krugliGli f. Okrugli G l i f ( ) , poluprecnik = " + poluprecnik);

}
void c r t a j () { p r in t( OkrugliGl i f .c rta j ( ) , poluprecnik = 1 1 + poluprecni k );

public class PoliKonstruktori { public s ta tic void m ain(String[] args) { new OkrugliGli f (5 );

}
} /* Isp is: G l if ( ) pre metode c r t a j () O k ru g liG lif.c rta j( ) , poluprecnik = 0 G l if ( ) posle metode c r t a j () O krugliG lif. Okrugl iGl i f ( ) , poluprecnik = 5

* ///:U klasi Glif, m eto d a crtaj() m oe da b u d e redefinisana, to je u ra e n o u klasi OkrugliGIif. Tu m eto d u poziva k o n stru k to r klase Glif, te e biti n ap rav ljen skok u m eto d u OkrugliGlif.crtajO, to kao da nam je i bila n am era. Ali, p ogledajte rezu ltat i videete sledee: kada k o n stru k to r klase Glif pozove m e to d u crtaj(), pro m en ljiv a poluprecnik nije jo d obila ni p o d razu m ev an u p o etn u v red n o st 1. N jena v red n o st jeO .S to g a bi na e k ran u bila n ac rtan a taka ili m o d a ne bi bilo n ac rta n o nita, a vi biste ostali zb u n jen i po k u avajui da otkrijete zato p ro g ra m ne radi.

232

Misliti na Javi

P oredak inicijalizacije koji je opisan u ran ijem o deljku n ije ba k o m p le ta n i to je klju za reenje m isterije. Proces inicijalizacije zaista tee na sledei nain: 1. P ro sto r za sm etan je objekta p o p u n jav a se b in a rn im n u la m a p re nego to se bilo ta d ru g o odigra. 2 . Pozivaju se k o n stru k to ri o sn o v n ih klasa kao to je ran ije op isan o . N a to m m estu poziva se redefinisana m eto d a c rta j() (da, pre nego to se pozove k o n stru k to r klase O k ru g liG lif), koja zbog koraka 1 kao v re d n o st p o lu p re n ik a d o bija n u lu . 3. Pozivaju se inicijalizatori lanova p o red o sled u deklaracije. 4. Izvrava se telo k o n stru k to ra izvedene klase. Postoji je d n a d o b ra stran a svega ovoga - sve je b arem in icijalizovano n u la m a (ili ta god n u la znaila za o d re e n i tip) i nije ostavljeno sa slu ajn o zateen im v re d n o stim a koje su beskorisne. To ukljuuje i reference na objekte koje su u g ra e n e u klasu p rek o k o m p o zicije i do b ijaju v red n o st n u ll. Stoga, ako takvu referencu zab o rav ite d a inicijalizujete, javie se izuzetak p rilik o m izvravanja. Sve d ru g o do b ija v re d n o st nula, to je o b in o siguran znak da ete u re z u lta tu v ideti greku. S d ru g e stran e, treb alo bi da vas p rilin o uasne rezu ltat ovog p ro g ra m a . P o stu p ili ste logino, a ipak je njegovo p o n aan je tajan stveno pogreno, a p revodilac se nije b u n io . (U ovakvim situ a ja m a se C + + p o n aa razb o ritije.) O vakve greke lako m o g u da b u d u d u b o k o u k o p an e i njihovo pro n alaen je iziskuje p u n o v rem en a. Z bog toga, d o b a r savet za p isanje k o n stru k to ra giasi: U rad ite to m an je posla da objekat dovedete u isp rav n o stan je i ako ikako m oete, izbegavajte da pozivate bilo kakve d ru ge m etode u toj klasi. Sa sig u rn o u m o ete da pozivate sam o fin aln e m e to d e osn o v n e klase. (O vo se au to m atsk i o d n o si i na p riv atn e m eto d e, koje su au to m a tsk i i finalne.) O n e ne m o g u da b u d u redefinisane i stoga ne kriju ovakvu v rstu izn en a en ja. N eete uvek m oi d a se p rid rav ate ovog saveta, ali to je ideal kojem tre b a teiti. V eba 15: (2) D atoteci P o IiK o n stru k to ri.ja v a d o dajte P ra v o u g a o n iG lif i p okaite p ro blem opisan u ovom odeljku.

Kovarijantni povratni tipovi


Java SE5 je do n ela i kovarijantne povratne tipove, to znai da redefinisana m eto d a izvedene Idase m oe vraati tip izveden iz tip a koji vraa m eto d a osn o v n e klase:
//: polimorfizam/KovarijantnoVracanje.java class Zito { public String toStringO

{ return " ito "; }

}
class Psenica extends Zito public String toStringO { { return "Penica"; }

Poglavlje 8: Polimorfizam

233

class Mlin

{ { return new Zito(); }

Zito obradi()

}
class PsenicaMlin extends Mlin Psenica obradi() { {

return new Psenica(); }

}
public class KovarijantnoVracanje M1 in m = new M1 i n ( ) ; Zito z = m. o b r a d i (); Sy stem.out.println(z); i n = new PenicaMl i n ( ) ; z = m. o b r a d i (); System.out.println(z); { {

public static void main(String[] args)

}
} /* Ispis: i to Penica

* ///:K ljuna razlika izm eu Jave SE5 i ran ijih verzija Jave lei u sledeem : ranije verzije bi naterale redefinisanu verziju m eto d e obradi() da v rati objek at klase Zito, a ne Psenica, iako je Psenica izvedena iz klase Zito i stoga je i dalje legitim an p o v ra tn i tip. K ovarijantni p o v ra tn i tipovi o m oguavaiu specifiniji p o v ra tn i tip Psenica.

Projektovanje pomou nasleivanja


Kada shvatite polim o rfizam , m oe vam se u initi da bi sve treb alo da nasle u jete jer je polim orfizam tako p a m etn a alatka. To m oe da o p tereti projekat; n aim e, ako o d m a h u p o trebite nasleivanje kada o d postojee klase p ravite novu, stvari m o g u bez p o tre b e d a se kom p lik u ju . Bolji p ristu p je da najp re izaberete k o m poziciju, n aro ito ako nije o ig led n o ta treb a da u p o treb ite. K o m p ozi ja ne nam ee uvoenje h ijerarh ije nasledivanja u projekat. K om pozicija je tak oe fleksibilnija, je r ste u m o g u n o sti da tip o d ab erete d in am ik i (a tim e i po n aan je), do k p ri nasleivanju m o rate ve p rilik o m p rev o en ja d a zn ate taan tip. To ilustruje n ared n i p rim er:
//: polimorfizam/Kompozicija.java // Dinamika promena ponaanja objekta // pomou kompozicije (projektni obrazac "stanje") import static net.mindview.uti1.P r i n t .*;

class Glumac { publi c voi d glumi( ) ;

234

Misliti na Javi

class SrecanGlumac extends Glumac { public void glumi() { pr in t( "S re ca nG lu ma c"); }

}
class TuzanGlumac extends Glumac { public void glumi() { p r in t(T u za nG lu ma c" ); }

}
class Pozornica { private Glumac glumac = new Sr ec an Gl um ac (); public void p r om en i() { glumac = new T u za nG lu ma c( ); } public void kreni () { glumac.glumi (); }

}
public class Kompozicija { public static void main(String[] args) { Pozornica pozornica = new Pozornica(); pozornica.kreni(); po zornica.promeni(); pozornica.kreni();

}
} /* Ispis: SrecanGlumac TuzanGIumac

* ///:O b jek at Pozornica sadri referencu na klasu Glumac koja je inicijalizovana o bjektom klase SrecanGlumac. To o d re d u je p o n aa n je m eto d e kreni(). Ali p oto referenca prilikom izvravanja m oe da b u d e povezana s d ru g aijim ob jek to m , referenca glumac m oe d a b u d e izm enjena tako da po kazu je na objekat klase TuzanGlumac, im e se ponaanje m eto d e kreni() m enja. Z nai, do bili ste d in am ik u fleksibilnost pri izvravanju. (O vo se tak oe naziva i ob razac State (S tanje). P ogleajte knjigu T hinking iti Patterns (ivith Java) na aresi w w w .M indV icw .net.) N asu p ro t to m e, p riliko m izvravanja ne m oete da m enjate ta ete naslediti; to m o ra p o tp u n o da b u d e o d re e n o ve prilikom prevoenja. O p ta sm ern ica je upotrebljavajte nasleivanje da izrazite razlike u p o n aan ju , a polja d a izrazite p ro m e n e stan ja. U p re th o d n o m p rim e ru koristi se i je d n o i drugo: dve razliite klase su nasleene kako bi iskazale razliku u m etod i glum i(), a u klasi Pozornica upotrebljava se k om pozicija kako bi n jen o stan je m o g lo da se m enja. U ovom sluaju, p ro m e n a stanja povlai i p ro m e n u ponaanja.

Veba 16: (3) Pratei p rim e r iz d atotek e Kompozicija.java, nap rav ite klasu SvemirskiBrod u kojoj se nalazi referenca na klasu Uzbuna koja m oe da prikae tri razliita stanja
u zb u n e. N apravite i m eto d e koje m o g u d a m en jaju ta stanja.

Poglav[je 8: Poiimorfizam

235

Poreenje zamene i proirivanja


in i se d a je najbolji p ristu p za pravljenje h ijerarh ije nasleivanja u p o treb a ,,p o tp u n o g p ristu p a . O d n o sn o , u izvedenoj klasi bie redefinisane sam o m eto d e u stanovljene u o sn o v n o j klasi, kao to je p o k azan o n a n a red n o m dijagram u:
Oblik nacrtajO obrisi()

7V

Krug nacrtaj(] obrisi(]

Kvadrat nacrtajO obrisif

Trougao nacrtajf) obrisif)

O vo m oe da b u d e o zn aen o kao p o tp u n a relacija ,,je, jer interfejs klase o d red u je ta ova h ijerarh ija predstavlja. N asleivanje g a ran tu je da e bilo koja izvedena ldasa im ati interfejs osn ovn e klase i nita m anje. Ako p ratite ovaj dijagram , uoiete da izvedene ldase tak o e nee im ati nita vie osim interfejsa o snovne klase. To m oe da se p o sm a tra kao p o tp u n a za m en a , jer o bjekti izvedenih klasa m o g u savreno d a se zam ene o sn o v n o m klasom , pa kada ih koristite ne m o rate o n jim a im ati nikakve d o d a tn e inform acije:
Obrati se Obliku Poruka relacija ,.je" Krug, Kvadrat, Linija ili novi tip Oblika

O sn o v n a klasa m oe da p rim i svaku p o ru k u koju m oete da poaljete izvedenoj klasi, jer o n e im aju p o tp u n o isti interfejs. Treba sam o da u ra d ite svoenje navie iz izvedene klase pa nikada vie neete m o rati da razm iljate o to m e s ko jim tip o m radite. Polim orfizam se stara o svem u d ru g o m . Kada to na ovaj nain p o sm atrate, ini se da je p o tp u n a relacija ,,je jedini logini nain da takve stvari u rad ite i da je bilo koji dru g i nain projek to v an ja nejasan i sam im tim lo. To je tak o e zam ka. im p o n ete da razm iljate na taj nain , pogledaete oko sebe i u stan o v iti da je proirivan je interfejsa (to naalost rezervisana re extends p odstie) sav reno reenje o d re e n o g p ro b lem a. To se m oe nazvati relacijom ,,je kao, jer je izvedena

236

Misliti na Javi

klasa p o p u t o sn o v n e klase - im a isti o sn o v n i interfejs - ali im a i d ru g e m o g u n o sti koje zahtevaju realizaciju d o d a tn ih m eto d a:

Pretpostavimo da ovo predstavlja veliki interfejs

je kao"

Proirivanje interfejsa

Iako je ovo k o ristan i ra z u m a n p ristu p (u zavisnosti o d situacije), on im a svoje m ane. P roireni deo interfejsa izvedene klase nije d o stu p a n o sno vn oj klasi, stoga nakon svodenja navie ne m oete da p ozivate nove m etode:
Obrati se objektu klase Koristan

Korisni deo Poruka KoriSugfdeo

Ako u ovom sluaju ne obavljate svoenje navie, to vas nee optereivati, ali esto m oete da d o e te u situaciju kada tre b a p o n o v o o tk riti taan tip objekta kako biste mogli da p ristu p ite njegovim d o d a tn im m e to d am a. U n a re d n o m odeljku p ok azu je m o kako se to radi.

Svoenje navie i podaci o tipu prilikom izvravanja


Poto prilik o m svoenja navie (k reta n ja uz h ije rarh iju nasleivanja) gubite inform aciju o ta n o m tip u , im a sm isla da za p o n o v n o d o b ijan je in form acije o tip u - o d n o sn o za kretan je nazad niz h ijerarh iju n asleivanja - u p o treb ljav ate svodenje nanie (engl. downcast). N auili ste da je svoenje navie uvek sig u rn o , je r osn o v n a klasa ne m oe da im a iri interfejs o d izvedene Idase, pa e stoga svaka p o ru k a koju poaljete preko interfejsa osn ov ne klase biti prihvaena. Ali p rilik o m svoenja nanie ne zn ate da je neki oblik (na p rim er) zaista kru g - m ogao bi da b u d e tro u g ao , k vadrat iii neto drugo.

Poglavlje 8: Polimorfizam

237

D a b i se ovaj p ro b le m reio, m o ra d a p o sto ji n a in za g aran to v an je ispravnog svoenja nan ie, kako ne biste grekom u rad ili svoenje u p o g rean tip i zatim poslali p o ru k u koju objekat n e m oe da p rim i. To bi bilo veo m a opasno. U n ek im jezicim a (p o p u t C + + -a ), m o ra te da u p o tre b ite p o se b n u o p eraciju da biste dobili sig u rn o svoenje nanie. M e u tim , u Javi se p roveravaju sva svoenjal Iako naizgled obavljate o b i n u konverziju, p rilik o m izvravanja se u tv r u je da li je to zaista onaj tip koji vi m islite d a jeste. Ako to nije sluaj, d o i e d o izuzetka tip a C lassC astE x cep tio n . in p ro veravanja tip o v a p rilik o m izvravanja naziva se p o d a c i o tip o v im a p rilik o m izvravanja (engl. R T T I - r u n - ti m e typ e in fo rm a tio n ). N ared n i p rim e r pokazuje kako RTTI radi:
//: polimorf iz am /R TT I.java // Svoenje nanie i podaci o tipovima prilikom izvravanja (RTTI). // {ThrowsException}

class Koristan { public void f() {} public void g() {}

}
class Korisniji extends Koristan { public void f() public void g() public void u() public void w() {} {} {} {}

public void v () {}

}
public class RTTI Koristan[] { args) {

public static void main(String[] x = { new Ko r i s t a n (), new Korisniji ()

};
x[0].f(); x[l] -g(); // Prilikom prevoenja: metoda nije pronaena u klasi Koristan //! x [1] .u (); ( (Korisniji)x [1]).u (); // Svoenje nanie/RTTI ((Korisniji)x [0]).u (); // bie baen izuzetak

} } ///:Kao to se vii s p re th o d n o g dijagram a, klasa Korisniji proiruje interfejs klase Koristan. Poto je nasleena, o n a takoe m oe da b u d e svedena navie ka klasi Koristan. To m oete da viite u okviru inicijalizacije niza x u m etodi main(). Poto oba objekta u nizu pripad aju klasi Koristan, za oba m oete da pozovete m etode f() i g(), a ako pokuate da p o zovete m eto d u u() (koja postoji sam o u klasi Korisniji), dobiete greku p ri prevoenju.

238

Misliti na Javi

U koliko elite da p ristu p ite p ro iren o m interfejsu objekta klase K orisn iji, p rim en ite svodenje nanie. Ako je tip odgovarajui, o n o e b iti uspeno. U su p ro tn o m , dobiete izuzetak tipa C lassC astE xception. Za o b rad u ovog izuzetka nije n e o p h o d n o da piete poseban kod, jer o n naznaava p ro g ram ersk u greku koja je m ogla d a se desi bilo gde u p ro g ram u . K om en tar - oznaka {Throw sException} - kazuje sistem u za au to m atizo v an o prevoenje i pakovanje (engl. build) p rim era iz ove knjige kako oekuje da e ovaj p ro g ra m prilikom izvravanja generisati izuzetak. RTTI je vie o d o bine konverzije. Postoji, na p rim er, nain da u stanovite tip pre nego to po kuate da obavite njegovo svoenje nanie. K om pletno poglavlje Podaci o tipovim a p osveeno je prouav an ju razliitih vidova po d atak a o tip o v im a prilik o m izvravanja u Javi. V eba 17: (2) K oristei h ijerarh iju C ik l iz p rim e ra 1, m e to d u ra v n o te a () d o d ajte klasam a U n icik l i B icikl, ali ne i klasi T ricik l. N apravite p rim e rk e sva tri tip a i svedite ih navie n a niz tip a C ikl. Pozovite m e to d u ra v n o te a () za svaki elem en t to g niza i p o gledajte rezultate. Svedite nanie, pozovite m e to d u ra v n o te a () i p o g led ajte ta se deava.

Saetak
Po lim o rfizam znai razliiti o blici. U o b jek tn o o rije n tisa n o m p ro g ra m ira n ju im ate isti interfejs osn o v n e klase i razliite oblike koji u p o treb ljav aju taj interfejs: razliite verzije d in am ik i pov ezan ih m etoda. U ov o m poglavlju videli ste da nije m ogue ra zu m e ti niti p rim e n iti p o lim o rfiz a m bez u p o treb e ap strakcije p o d ata k a i nasleivanja. P o lim o rfizam je o so b in a koja ne m oe d a se p o sm a tra izolovano (kao to m oe, n a p rim er, n ared b a svvitch), ve sam o kao deo vee slike relacija izm e u klasa. D a biste u p ro g ra m im a efikasno koristili p o lim o rfizam - a sam im tim i o b jek tn o o rijen tisan e teh n ik e - m o ra te da p ro irite svoje p o im an je p ro g ra m ira n ja. P o red lanova i p o ru k a zasebne klase, u zm ite u o b z ir i zajednike o so b in e m e u klasam a kao i njihove m e u so b n e relacije. Iako to iziskuje znaajan n ap o r, o n se i te kako isplati. Postie se bri razvoj p ro g ra m a, bolja organizacija koda, pro iriv i p ro g ra m i i lake od rav an je koda. Reenja odabranih vebi data su u elektronskom dokum entu Thinking in Java Annotatcd Solution Guide, koji se moe kupiti na lokaciji www.MindView.net.

Interfejsi
Interfejsi i apstraktne klase pruaju strukturiraniji naiti razdvajanja interfejsa od realizacije. C + + IMA sam o p o sred n u p o d rk u za te koncepte. in jen ica da o n i p o sto je u Javi, govori d a su ih pro je k tan ti jezika sm atrali d ovoljno b itn im da se za n jih ob ezbed i d ire k tn a p o d rk a kroz rezervisane rei. N ajpre em o ra z m o triti apstraktne kla seko je su o tp rilik e na pola p u ta izm e u o b in e klase i interfejsa. Iako ete biti skloniji p ravljenju interfejsa, a p stra k tn a klasa je vana i n e o p h o d n a alatka za pravljenje klasa koje im aju neke nerealizovane m etode. N e m o ete uvek k o ristiti ist interfejs.
T a k v i m e h a n i z m i n i s u t a k o C e s t i u p r o g r a m s k i m j e z i c i m a . P r i m e r a RA DI,

Apstraktne klase i metode


U p rim e ru sa in stru m e n tim a u p re th o d n o m poglavlju, m eto d e u osn o v n o j klasi I n s tr u m e n t uvek su bile ,,lane m etode. Ako bi o n e ikad bile pozvane, p o sred i bi bila neka greka, zato to je nam en a klase I n s tr u m e n t da obezb ed i zajednicki interfejs za sve iz nje izvedene klase. U tim p rim e rim a , jedin i razlog z ap rav ljen je zajednikog interfejsa jeste da se o m o g u i razliito p onaanje svakog p o jed in an o g p o d tip a . O n u spostavlja o sn o v n i izgled, tak o da znate ta je zajedniko za sve izvedene klase. D ru g i nain da to iskaete jeste da p roglasite klasu In s tru m e n t apstraktnotn osnovnotn klasom (ili je d n o sta v n o apstraktnom klasom). Ako im ate a p stra k tn u klasu, p o p u t klase In s tru m e n t, n jen i o b jek ti gotovo n ik ad nem aju m kakvo znaenje. A p strak tn u klasu prav ite kada elite d a s g ru p o m klasa rad ite p reko zajednikog interfejsa. Tako In s tru m e n t slui sam o da se opie interfejs, a ne o d re e n a realizacija - stoga pravljenje objekata te klase n em a sm isla i v ero v atn o ete poeleti da spreite korisnika da to uradi. To m o ete da p o stig n ete tak o to ete svim m e to d a m a u klasi In s tru m e n t zadati da ispisuju p o ru k u o greci, ali tim e in fo rm ac iju odlaete sve do izvravanja i od korisnika zahtevate p rilin o tem eljn o testiran je. Uvek je bolje prijav iti p ro b lem e u vrem e prevoenja. Java za to obezbeuje m eh an izam koji se naziva apstraktna m etoda.' To je n e p o tp u n a m etoda, o n a im a sam o deklaraciju bez tela. S intaksa za deklaraciju a p strak tn e m eto d e je sledea:
abstract void f ();

Klasa koja sadri ap strak tn e m eto d e naziva se apstraktna klasa. Ako kJasa sadri je d n u ili vie ap strak tn ih m etoda, o n a m o ra biti kvalifikovana kao ap strak tn a . (U su p ro tn o m , prevodilac e prijaviti greku.) Poto je a p stra k tn a klasa n e p o tp u n a , ta bi p revodilac treb alo da u rad i kada n eko p okua da napravi objekat takve klase? Poto n e m oe da n ap rav i objek at a p strak tn e klase na siguran nain, prevodilac e prijaviti greku. Na ovaj nain se o b ezb e u je p o tp u n o s t apstra k tn e klase i vi ne m o rate da b rin ete o n jenoj ev en tu aln o j p o g ren o j u p o treb i.
Za program cre na C + f - u onc su iedn.ike potpuno virtuelnim funkcijanui.

240

Misliti na Javi

A ko a p stra k tn u klasu n asled ite i elite d a p rav ite objekte to g novog tipa, m o ra te da obezbedite definicije za sve a p stra k tn e m eto d e o sn o v n e klase. U koliko to ne u rad ite (a m o ete i tako d a o dlu ite), ta d a i izvedena klasa o staje a p stra k tn a , te e vas prevodilac p risiliti i da tu klasu oznaite rezerv isano m rei a b s tra c t. A p strak tn u klasu m o ete d a n ap ra v ite i tak o d a ne sadri n ijed n u a p stra k tn u m eto d u . To je k o risn o k ad a u klasi n e m a sm isla p ra v iti bilo kakve ap stra k tn e m etode, ali ipak elite da spreite pravljenje o b jek ata te klase. Klasa In s tr u m e n t iz p re th o d n o g poglavlja lako m oe da b u d e p retv o re n a u a p stra k tn u klasu. Sam o neke m e to d e e b iti a p stra k tn e - ne m o ra ju sve. Evo kako to izgleda:

Sledi izm enjeni p rim e r sa o rk e stro m uz u p o tre b u a p strak tn ih klasa i m etoda:


//: i nterfejsi/muzi ka4/Muzi ka4.java // Apstraktne klase i metode package interfejsi.muzika4; import polimorfizam.muzi ka.Nota; import static n e t . mi nd vi ew .u ti l.Print.*; abstract class Instrument { private int i; // Zauzima se memorija za svaki objekat klase Instrument public abstract void sviraj(Nota n); public String sta() { return " In st rument"; }

Poglavlje 9: Interfejsi

241

public abstract voi nastimuj();

}
class Duvacki extends Instrument { public void sviraj(Nota n) { print("D uv ac ki .s vi raj() " + n); {

}
public String sta() { return "Duvacki"; {} } public void nastimuj()

}
class Udaraljke extends Instrument { public void sviraj(Nota n) { print("U da ra lj ke .s vir aj() " + n);

}
public String sta() { return "Udaraljke"; {} } public void nastimuj()

}
class Zicani extends Instrument { public void sviraj(Nota n) { print( "Z ic an i. sv ir aj() " + n ) ;

}
public String sta() { return "Zicani"; } {} { public void nastimuj()

}
class LimeniDuvacki extends Duvacki public void sviraj(Nota n) { print( "L im en iD uv ac ki.s v i r a j () " + n ) ;

}
public void nastimuj() { print("LimeniDuvacki.nastimuj()"); }

}
class DrveniDuvacki extends Duvacki public void sviraj(Nota n) { pr in t( "D rv en iD uv ac ki.s v i r a j () " + n); {

}
public String sta() { return "DrveniDuvacki"; }

public class Muzika4 { // Ne vodi rauna o tipovima pa e novi tipovi // koje dodamo sistemu i dalje pravilno da rade: static void melodijaflnstrument i) {

/ /
i.svi ra j( No ta.SREDNJE_C);

}
static void sveMelodije(Instrument[] e) { for(Instrument i : e) melodi j a ( i ) ;

242

Misliti na Javi

public static void main(String[] args) { // Svoenje navie prilikom dodavanja u niz: Instrument[] orkestar = { new Du va ck i(); new Udaral j k e ( ) ; new Z i ca ni(); new LimeniDuvacki(); new Dr ve niDuvacki();

};
sveMelodije(orkestar);

}
} /* Ispis: D u v a c k i . s v i r a j O SREDNJE_C Ud ar aljke.sviraj() S R E D N J E C Zi ca ni.sviraj() SREDNJE_C LimeniDuvacki.sviraj() SREDNJE_C DrveniDuvacki.sviraj() SREDNJE_C

* ///:O b ra tite p a n ju n a to d a je izm e n jen a sam o o sn o v n a klasa. Pravljenje ap stra k tn ih m eto d a i klasa je k o risn o je r izriito o d re u je ap stra k tn o st klase i objanjava n je n u n a m e n u i k o risn ik u i p rev o d io cu . Takoe, a p stra k tn e klase su korisne alatke za p o n o v n u p o d elu na p ro ste fak to re, je r om o g u av aju lako p o m e ra n je zajednikih m eto d a navie u hijerarh iji nasleivanja.

Veba 1: (1) Izm enite vebu 9 iz p re th o n o g poglavlja tako da klasa Glodar p ostane ap stra k tn a klasa. G de god je m o g u e, p retv o rite m e to d e klase Glodar u apstraktne. Veba 2: (1) N ap ravite a p stra k tn u kiasu koja n em a n ijed n u a p stra k tn u m e to d u i uverite
se da ne nije m o g ue n ap rav iti in stan ce te klase.

Veba 3: (2) N apravite o sn o v n u k lasu s a p stra k tn o m m e to d o m ispisi() koju ete redefinisati u izvedenoj klasi. R eefinisana verzija te m eto d e treba da ispisuje v red n o st celob ro jn e prom enljive definisane u izvedenoj klasi. P rom enljivoj zad ajte n ek u vred n o st razliitu o d nule na m estu definicije. P ozovite tu m e to d u u k o n stru k to ru o sn o v n e klase. U m etodi main() n ap rav ite o b jek at izvedenog tip a i p o to m pozo v ite njegovu m etodu ispisi(). O b jasnite rezultate.

Veba4: (3) N apravite a p stra k tn u klasu koja n em a n ijed n u m eto d u . N asledite je i dodajte
jednu m eto d u . N aprav ite statin u m e to d u iji je a rg u m e n t referenca na o sn o v n u klasu. Svedite tu referencu n an ie ka izvedenoj klasi i pozo v ite m eto d u . U m eto d i main() pokaite da to radi. Sada ubacite a p stra k tn u deklaraciju u o sn o v n u klasu i elim iniite p o treb u za svoenjem nanie.

Interfejsi
Rezervisana re interface u n a p re u je k o n cep t apstrakcije. R ezervisana re abstract o m o guava da u klasi n ap rav ite je d n u ili vie m e to d a koje n em aju definicije - tim e obezbedujete deo interfejsa, ali izostavljate o d g o v araju u realizaciju, koju e n ap rav iti naslednici te klase. R ezervisana re interface o znaava p o tp u n o a p stra k tn u klasu, u kojoj uop te nem a

Poglavlje 9: Interfejsi

243

nikakve realizacije. Interfejs a u to ru o m o g u av a d a definie izgled klase: im en a m eto d a, liste arg u m e n a ta i p o v ra tn e tipove, ali n e i tela m etoda. O n obezbeuje sam o fo rm u , ali ne i realizaciju. Interfejs n a m govori: O vako e izgledati sve klase koje realizuju ovaj interfejs". Stoga svaki k o d koji upotrebljava odreeni interfejs zna koje m etode interfejsa m oe da koristi i to je sve. Interfejs se, znai, koristi d a ustan o v i k o m unikacioni p ro to k o l izm eu klasa. (Neki o bjektn o orijentisani program ski jezici za istu n a m en u koriste rezervisanu re protocol.) M e u tim , interface nije sam o a p stra k tn a klasa dovedena d o k rajn jih granica. O n vam om o g u av a da d obijete v arija n tu viestrukog nasleivanja C + + -a, je r p o m o u njega m oete d a n ap rav ite klasu koja se m o e svesti navie p rem a vie o sn o v n ih tipova. D a biste n ap ravili interfejs, u m esto rezervisane rei class u p o treb ite rezervisanu re interface. Kao i k od klasa, ispred rezervisane rei interface m o ete da d o d a te i rezervisan u re public (ali sam o ako je taj interfejs definisan u dato teci istog im en a). U koliko je izostavite, dobijate p ak e tn i p ristu p , tak o da taj interfejs m oete da upotrebljavate sam o u n u ta r istog paketa. Interfejs m oe d a sadri i polja, ali su o n a im p licitn o (auto m atski) defin isan a kao statin a i finalna. D a biste nap ravili klasu koja zadovoljava o d re en i interfejs (ili g ru p u interfejsa), u p o treb ite rezervisanu re im plem ents. T im e govorite: njen izgled je definisan d a tim interfejsom , a sada u d efinisati kako e o n a da radf'. Izuzev te izm ene, sve ostalo je kao kod nasleivanja. Izloeni k o n cep t je p rik aza n n a d ijag ram u p rim e ra s in stru m e n tim a:

244

Misliti na Javi

Iz klasa DrveniDuvacki i LimeniDuvacki m oete videti sledee: kada realizujete interfejs, ta realizacija postaje obin a klasa k oja m oe d a se p ro iru je na uo biajene naine. D eklaracije m eto d a u interfejsu m oete eksplicitno d a pro glasite jav n im . O n e su, m e u tim , javne ak i ako to ne uinite. Stoga p rilik o m realizacije interfejsa, sve m e to d e iz interfejsa m o ra ju da b u d u proglaene javn im . U s u p ro tn o m bi d ob ile p o d raz u m ev an i p ak etn i p ristu p , im e biste um an jili nivo p ristu p a m e to d am a p rilik o m nasleivanja, to Java prevodilac ne dozvoljava. To m oete d a vidite u izm enjenoj verziji p rim e ra sa in stru m e n tim a . Z a p a m tite da je svaka m eto d a u interfejsu sam o deklaracija i prevodilac nee d o p u stiti n ita d ru g o . Pored toga, iako n ijedn a m eto d a interfejsa Instrument nije deklarisana kao jav na, to se a u to m atsk i obavlja:
/ / : interfejsi/muzika5/Muzika5.java // Interfejsi. package interfejsi.muzika5; import polimorfizam.muzika.Nota; import static net.mindview.util.Print.*; interface Instrument { // Konstanta pri prevoenju: int IZNOS = 5; // statina i finalna // Ne moe da ima definicije metoda, ve samo deklaracije: void sviraj(Nota n); // Automatski javne void nastimuj();

}
class uvacki implements Instrument { public void sviraj(Nota n) { print(this + " . s v i r a j O " + n); { return "Duvacki"; } }

}
public String t o S t r i n g O public void nastimuj() { print(this + " .n a st im uj()");

}
class Udaraljke implements Instrument { public void sviraj(Nota n) { print(this + " . s v i r a j O " + n); { return "Udaraljke"; } }

}
public String t o S t r i n g O public void nastimuj() { print(this + ". nastimuj ()");

}
class Zicani implements Instrument { " + n); { return "Zicani"; } } public void sviraj(Nota n) { print(this + " . s v i r a j O

}
public String t o S t r i n g O public void nastimuj() { print(this + " .nastimuj()");

Poglavlje 9: Interfejsi

245

class LimeniDuvacki extends Duvacki public String t o S t r i n g O

{ return "LimeniDuvacki ; }

}
class DrveniDuvacki extends Duvacki public String toString() {

{ return "DrveniDuvacki"; }

}
public class Muzika5 { // Ne vodi rauna o tipovima pa e i novi tipovi // koje dodate u sistem ispravno raditi: static void melodija(Instrument i) {

/ /
i.sviraj(Nota.SREDNJE_C);

}
static void sveMelodije(Instrument[] e) { for(Instrument i : e) melodi ja ( i );

}
public static void main(String[] args) Instrument[] orkestar = new { new Duvacki (); new Udaralj k e ( ) ; new Zicani (); new LimeniDuvacki (); new DrveniDuvacki (); { // Svoenje navie prilikom dodavanja u niz:

};
sveMelodije(orkestar);

}
} /* Ispis: D u v a ck i. sv ir aj() SREDNJE_C Udar al jk e. sv ir aj() SREDNJE_C Z i c a n i .svir a j () SREDNJE_C Li me ni Duvacki.sviraj() SREDNJE_C DrveniDuvacki.svi r a j () SREDNJE_C

* ///:U ovoj verziji p rim era uinjena je jo jed n a izm ena: m eto d a sta() prep rav ljen a je u toStringO, jer se koristila u pravo na taj nain. Poto je toStringO d eo korenske klase Object, ne m o ra da se pojavi u ovom interfejsu. O statak p ro g ram a radi kao i ranije. Nije b itn o da li obavljate svoenje navie ka obinoj klasi Instrument, a p strak tn o j klasi Instrument ili interfejsu Instrument. P onaanje je id en tin o . U m eto di melodija(), u stvari, ne p ostoji nijedan trag o to m e da li je Instrum e n t o b i n a klasa, ap stra k tn a klasa ili interfejs. Veba 5: (2) U zasebn o m pak etu n ap rav ite interfejs koji sadri tri m etode. R ealizujte taj interfejs u nekom d ru g o m paketu.

Veba 6: (2) D okaite da su sve m eto d e interfejsa au to m a tsk i javne.

246

Misliti na Javi

Veba 7: ( 1) Izm enite vebu 9 iz pogiavlja Polim orfizam tak o da klasu Glodar p re tv o rite u
interfejs.

Veba 8: (2) U datoteci polimorfizam.Sendvic.java n a p ra v ite interfejs p o d im e n o m BrzaHrana (sa o d o gov araju im m eto d am a) i p ro m e n ite Sendvic tako d a realizuje i interfejs BrzaHrana. Veba9: (3) Izm enite p ro g ram Muzika5.java pravei a p stra k tn u klasu koja sadri zajednike m eto d e po tklasa Duvacki, Udaraljke i Zicani. Veba 10: (3) Izm enite d ato tek u Muzika5.java d o d aju i interfejs MozeDaSvira. D eklaraciju m eto d e sviraj( ) prebacite iz interfejsa Instrument u MozeDaSvira. Izvedenim klasam a dodajte i interfejs MozeDaSvira, u b acujui ga u listu iza rezervisane rei implements. Izm enite m eto d u m eiodija( ) tako da n jen a rg u m en t b u d e interfejs MozeDaSvira, um esto interfejsa Instrument.

Potpuno razdvajanje
Kad g od m e to d a rad i s klaso m u m esto sa interfejsom , o g ran ien i ste na u p o tre b u te klase i n jen ih podklasa. U koliko biste ba h teli da p rim e n ite tu m e to d u na n ek u klasu koja nije u toj hijerarh iji, nem ate sree. Interfejs z n a tn o slabi to ogranienje. Stoga m oete da piete kod koji je lake u p o tre b iti vie p uta. Na p rim er, p retp o stav im o da im ate klasu Procesor s m e to d a m a im e ( ) i o b rad i( ) koja p rim a ulazne podatk e, m odifikuje ih i alje na izlaz. O sn o v n a klasa se p ro iru je prilik o m pravljenja vie razliitih v rsta Procesora. U ovom sluaju, p o d tip o v i Procesora m odifikuju String objekte (vodite rau n a o to m e da k o v arijan tn i m o g u biti p o v ra tn i tipovi, ali ne i tip o v i arg um enata):
//: interfejsi/klasaprocesor/Primeni.java package interfejsi.klasaprocesor; import java.util import static net.mindview.uti1 .P r i n t .*; class Procesor { public String ime() { return g e t C l a s s O . g e t S i m p l e N a m e O ;

}
Object obradi(Object ulaz) { return ulaz; }

}
class VelikimSlovima extends Procesor { String obradi(Object ulaz) { // Kovarijantno vraanje return ((String)ul a z ) .t o U p p e r C a s e O ;

} }
class MalimSlovima extends Procesor { String obradi(Object ulaz) {

Poglavlje 9: Interfejsi

247

return ( (String)ulaz).toLowerCase();

} }
class Delilac extends Procesor { String obradi(Object ulaz) // // na sastavne delove: { Argument metode split() je graninik po kojem se znakovni niz deli

return Ar ra ys .t oS tr in g(((String)ulaz).s p l i t (" "));

} }
public class Primeni {

public static void process(Procesor p, Object s) { print("Ukljuen procesor " + p.ime()); p rint(p.obradi(s));

}
public static String s = "Disagreement with beliefs is by definition incorrect"; public static void main(String[] args) { obradi(new VelikimSlovima(), s ) ; obradi (new Mal i m S l o v i m a O , s ) ; obradi(new Delilac(), s ) ;

}
} /* Ispis: Ukljuen procesor VelikimSlovima DISAGREEMENT WITH BELIEFS IS BY DEFINITION INCORRECT Ukljuen procesor MalimSlovima disagreement with beliefs is by definition incorrect Ukljuen procesor Delilac [Disagreement, with, beliefs, is, by, definition, incorrect]

* ///:M etoda Primeni.obradi() p rim a Procesorbilo koje vrste i p rim e n ju je ga na Object, a zatim ispisuje rezultat. Pravljenje m etode koja se p o n aa razliito, u zavisnosti o d arg u m en ta objekta koji joj je p rosleen, naziva se p ro jektn i o b razac Strategy (strategija). Ta m eto d a sadri n ep ro m en ljiv deo a lg o ritm a koji treba izvriti, d o k p ro m en ljiv deo sadri S trategija. S trategija je objekat koji prosledujete; o n sadri kod koji treb a izvriti. O vde je S trategija objekat tipa Procesor, a u m etodi main() vidite tri razliite Strategije p riin e n je ne na objekte tip a Strings. M etoda split() je deo klase String. O na p rim a String objek at i deli ga koristei svoj arg u m e n t kao g ranin ik, a vraa String[], O vde je u p o treb ljen a za b re pravljenje niza objekata tipa String. P retp ostavim o da ste pronali skup elektronskih filtara koji su p riv id n o p rik lad n i za m e to d u Primeni.obradi():
//: interfejsi/filtri/Talasnioblik.java

package interfejsi.filtri;

248

Misliti na Javi

public class Talasnioblik { private static long brojac; private final long id = brojac++; public String t o S t r i n g O { return "Talasnioblik " + id; }

} III-//-. interfejsi/filtri/Filtar.java
package interfejsi.filtri; public class Filtar { public String ime() { return getC1ass().getSimpleName();

}
public Talasnioblik obradi(Talasnioblik ulaz) { return ulaz; }

} III-//: interfejsi/filtri /N is ko pr op us ni.java package interfejsi.filtri; public class Niskopropusni extends Filtar { double lom; public Niskopropusni(double lom) { this.lom = lom; } public Talasnioblik obradi(Talasnioblik ulaz) return ulaz; // Lana obrada {

III-interfejsi/filtri/Visokopropusni.java

//:

package interfejsi.filtri; public class Visokopropusni extends Filtar { double lom; public Visokopropusni(double lom) { this.lom = lom; } public Talasnioblik obradi(Talasnioblik ulaz) { return ulaz; }

III--

//: i nterfejsi/filtri/Pojasni Propust.java package in terfejsi.filtri; public class PojasniPropust extends Filtar { double donjiLom, gornjiLom; public PojasniPropust(double donjiL, double gornjiL) donjiLom = donjiL; gornjiLom = gornjiL; {

}
public Talasnioblik obradi(Talasnioblik ulaz) { return ulaz; }

III--

Filtar im a iste elem ente interfejsa kao Procesor, ali poto nije nasledio P rocesor-jer tvorac klase Filtar nije ni p o m islio kako biste vi heli da ga u p o treb ite kao P rocesor- Filtar

Poglavlje 9: Interfejsi

249

ne m oete u p o tre b iti s m e to d o m Primeni.obradi(), iako bi to bilo lepo. U sutini, veza izm ed u Primeni.obradi() i Procesora jaa je nego to je p o tre b n o , a to spreava k o d iz Primeni.obradi() d a se p o n o v o upotreb ljav a ta m o gde bi to trebalo. O b ratite p an ju na to da su i ulazi i izlazi tip a Talasnioblik. D a je Procesor interfejs, o g ra n ien ja b i bila to lik o oslabljena d a biste m ogli k o ristiti m eto d u Primeni.obradi() koja u zim a taj interfejs. Evo m o d ifik o v an ih verzija Procesora i Primeni:
//: interfejsi/interfejsprocesor/Procesor.java package interfejsi.interfejsprocesor; public interface Procesor { String ime(); Object obradi(Object ulaz);

} ///://: in te rf ej si/interfejsprocesor/Primeni.java package interfejsi.interfejsprocesor; import static ne t. mi nd vi ew .u ti l.Print.*; public class Primeni {

public static void o b r a d i (Procesor p, Object s) { print(''Ukljuen procesor " + p.ime()); print(p.obradi (s));

} } ///= Prvi nain na koji m o ete p o n o v o u p o treb iti k o d jeste o n aj kada p ro g ram e ri klijenti la p iu svoje klase tako d a b u d u usaglaene sa ov im interfejsom ; n a p rim er:
//: interfejsi/i nterfejsprocesor/ProcesorZnakovni hNi zova.java package interfejsi.interfejsprocesor; import j a v a . u t i l .*; public abstract class ProcesorZnakovnihNizova implements Procesor{ public String ime() { return g e t C l a s s O .getSimpleName();

}
public abstract String obradi(Object ulaz); public static String s = "If she weighs the same as a duck, she's made of wood"; public static void main(String[] args) { Primeni.obradi(new Veli ki mS lo vi ma (), s ) ; Primeni .obradi (new Mal i m S l o v i m a O , s ) ; Primeni.obradi(new Delilac (), s);

250

Misliti na Javi

class VelikimSlovima extends ProcesorZnakovnihNizova { public String obradi(Object ulaz) { // Kovarijantno vraanje return ((String)ulaz) .tollpperCase();

} }
class MalimSlovima extends ProcesorZnakovnihNizova { public String obradi(Object ulaz) { return ((String)ulaz).toLowerCase();

} }
class Delilac extends ProcesorZnakovnihNizova { public String obradi(Object ulaz) { return Arrays.toString(((String)ulaz).spl it(" "));

}
} /* Ispis: Ukljuen procesor VelikimSlovima IF SHE UEIGHS THE SAME AS A DUCK, SHE'S MADE OF WOOD Ukljuen procesor MalimSlovima if she weighs the same as a duck, she's made of wood Ukljuen procesor Delilac [If, she, weighs, the, same, as, a, duck,, she's, made, of, wood]

* ///:M e u tim , esto n e m oete da m o ifik u jete klase koje h o ete da u p o tre b ite . Za elektron ske filtre, na prim er, p ro n a e n a je gotova b iblioteka, a nije pravljena nova. U takvim sluajevim a m o ete u p o tre b iti p ro jek tn i o b razac A dapter (a d a p te r). N avodim o nain kako u A d ap teru piete k o d koji e preuzeti postojei interfejs i n ap rav iti interfejs koji vam je p o treb an :
//: i nterfejsi/interfejsprocesor/ProcesorFi 1 tar.java package interfejsi.interfejsprocesor; import interfejsi.fi1t r i .*; class Fi1tarAdapter implements Procesor { Filtar filtar; public FiltarAdapter(Filtar filtar) this.fi Itar = filtar; {

}
public String ime() { return f i l t a r . i m e O ; } { public Talasnioblik obradi(Object ulaz)

return fi1ter.obradi((Talasnioblik)ulaz);

} }
public class FiltarProcesor { public static void main(String[] args) { Talasnioblik w = new Talasnioblik(); Pr im en i.obradi(new FiltarAdapter(new Ni sk op ro pu sn i(1.0)), w ) ;

Poglav[je 9: Interfejsi

251

Primeni.obradi(new FiltarAdapter(new Vi so ko pr op us ni(2.0)), w ) ; P r imeni.obradi( new FiltarAdapter(new PojasniPropust(3.0, 4.0)), w ) ;

}
} /* Ispis: Ukljuen procesor Niskopropusni Talasnioblik 0 Ukljuen procesor Visokopropusni Talasnioblik 0 Ukljuen procesor PojasniPropust Talasnioblik 0

* ///:U o v om p ristu p u A d ap teru , k o n stru k to r klase FiltarAdapter uzim a interfejs koji im ate - Filtar - i p roizvodi objekat koji im a interfejs Procesor koji vam treba. M oda ste uoili i delegiranje u klasi FiltarAdapter. R azdvajanje interfejsa o d realizacije o m o gu av a d a isti interfejs b u d e p rim en jen na vie razliitih reaiizacija, im e k o d postaje lake p o n o v o upotrebljiv.

Veba 11: (4) N apravite klasu s m e to d o m koja u zim a a rg u m e n t tip a String i daje rezultat
u kojem su zam en jen a m esta svakog p ara znakova u to m a rg u m e n tu . Prilagodite klasu tako da radi s m e to d o m interfejsprocesor.Primeni.obradi().

Viestruko nasleivanje u Javi


Poto interfejs uopte nem a nikakvu realizaciju - o d n o sn o za njega se ne odvaja nikakvo m esto za skladitenje - nita nas ne spreava da k o m b in u je m o vie interfejsa. To je k o risno, jer p o n ek ad treba rei da je ,,x tip a i a i b i c . O vo k o m b in o v an je interfejsa vie klasa, u C + + -u se naziva viestruko nasleivatije i sa so b o m nosi p rilin o o b im a n prtljag, je r svaka klasa m oe da im a razliitu realizaciju neke m eto de. U Javi m oete da u rad ite isto, ali realizaciju m oe da im a sam o je d n a klasa, pa se u Javi p rilik o m k o m b in o v an ja vie interfejsa ne javljaju p ro b lem i koje sm o susretali u C + + -u :
Apstraktna ili konkretna osnovna klasa i Interfejs I A ! : Funkcije osnovne klase Interfejs 1 ..........A i : Interfejs 2

........
i Interfejs n i ............A .......

...

Interfejs n

O sno v na klasa neke izvedene klase ne m o ra da b u d e ni a p stra k tn a ni konk retna" (klasa koja n em a a p strak tn e m eto d e). Ako n asleujete neto to nije interfejs , m oete da nasledite sam o je d n u takvu klasu. Svi ostali nasledeni elem en ti m o raju da b u d u interfejsi. Sva im en a interfejsa m o ra ju biti razdvojena zarezim a posle rezervisane rei implements.

252

Misliti na Javi

M oete da n avedete koliko g o d elite interfejsa - svaki po staje nezavisan tip, ka kojem m o ete da svedete navie n o v u klasu. U n a re d n o m p rim e ru p rik a za n a je k o n k re tn a klasa k oja uz nekoliko interfejsa daje n o v u klasu:
//: interfejsi/Avantura.java // Vie interfejsa interface MozeDaSeBori void boriSe(); {

}
interface MozeDaPliva { void p l i v a j O ;

}
interface MozeDaLeti void leti (); {

}
class AkcioniJunak { public void boriSe() {}

}
class Heroj extends AkcioniJunak implements Mo ze D a S e B o r i , MozeDaPliva, MozeDaLeti public void plivaj() public void leti () {} {} {

}
public class Avantura { public static void t(MozeDaSeBori x) { x.boriSe(); public static void u(MozeDaPliva x) { x.plivaj(); public static void v(MozeDaLeti x) { x.leti(); public static void m a i n ( S t r i n g [] args) Heroj h = new H e r o j (); t ( h ) ; // Tretiraj ga kao tip MozeDaSeBori u(h); // Tretiraj ga kao tip MozeDaPliva v (h ) ; // Tretiraj ga kao tip MozeDaLeti w(h); // Tretiraj ga kao tip AkcioniJunak { } } public static void w(AkcioniJunak x) { x.boriSe(); } }

} } ///:U g orn jem p rim e ru v idite da klasa H ero j k o m b in u je klasu A k c io n iju n a k sa interfejsim a M o zeD aS eB o ri, M o zeD aP Iiv a i M ozeD aL eti. Kada na ovaj nain k o n k re tn u klasu k o m b in u je te sa interfejsim a, p rv o m o ra te da navedete tu k o n k re tn u klasu, a posle nje interfejse. (U su p ro tn o m , p revodilac prijavljuje greku.) Potpis m etode boriS e() isti je 11 interfejsu M ozeD aS eB ori i u kiasi A k cio n iju n a k , a m etoda b o riS e() ncpostoji u definiciji klase H eroj. Interfejs m oete da nasledite (kao to ete ubrzo videti), ali pri to m ete do b iti jo jedan interfejs. Ako elite da n apravite objekat

Poglavlje 9: Interfejsi

253

novog tip a, m orate da napravite klasu u kojoj postoje definicije svih m etoda. Iako m eto d a

boriSe() nije izriito definisana u klasi Heroj,o n a dolazi u z klasu Akcionijunak, im e au to m atski p ostaje i deo klase Heroj, p a je m ogue n ap rav iti objekat te klase. U klasi Avantura posto je etiri m eto d e iji su a rg u m en ti razn i interfejsi k o n k retn e klase. K ada se n aprav i objekat klase Heroj, o n m oe d a se p ro sledi bilo kojoj o d tih m eto d a,
to znai da se m oe svesti navie ka svakom o d tih interfejsa. To se obavlja b ez p rep rek a i bez p o se b n o g angaovanja p ro g ram era, zahvaljujui n ain u n a koji su interfejsi u Javi p ro jekto vani. Z a p a m tite da je u g o rn je m p rim e ru po k azan sutinski razlog p o sto jan ja interfejsa: m o g u n o st da se obavi svoenje navie ka vie o sn o v n ih tipova. M e u tim , d ru g i razlog za korienje interfeisa isti je kao i za korienje a p strak tn e osn ov ne klase: d a p ro g ra m e r klije n t ne m o e da n ap ravi objekat te klase i d a b u d e svestan kako je to sam o interfejs. Z bog toga se javlja pitanje: da li u p o tre b iti interfejs ili a p stra k tn u klasu? A ko o sn o v n u klasu m oete da naprav ite bez definicija m eto d a i bez p ro m en ljiv ih , uvek od a b e rite in te rfejse u m esto ap strak tn ih klasa. U sutini, ako znate da e neto b iti osn ov na klasa, p rv o p o kuajte d a to napravite kao interfejs. (O to m e em o jo govoriti u saetku ovog poglavlja.)

Veba 12: (2) U datoteci Avantura.java d o dajte interfejs p o d im e n o m MozeDaSePenje


p rate i o b lik ostalih interfejsa u njoj.

Veba 13: (2) N apravite interfejs koji nasleuju dva d ru g a interfejsa. N eka trei interfejs
nasledi ta dva.:

Proirivanje interfejsa nasleivanjem


P o m o u nasieivanja, interfejsu lako d odajete nove deklaracije m eto d a, i kako k o m b in u jete nekoliko interfejsa u jedan n o v interfejs. U oba sluaja do biete n o v interfejs, kao to se vidi iz n ared n o g prim era :
//: i nterfejsi/HororPredstava.java // Proirivanje interfejsa nasleivanjem. interface Cudoviste { void za p r e t i ();

}
interface OpasnoCudoviste extends Cudoviste { void unisti ();

}
interface Smrtonosno { void u b i j ();

To e p o k a z a ti kak o in te rfejsi sp re a v a ju P ro b le m ro m b a koji se javlja u C + + - u p rilik o m v i e s tru kog n a sle iv a n ja .

254

Misliti na Javi

class Zmaj implements OpasnoCuoviste { public void zapreti() {} public void u n i s t i () {}

}
interface Vampir extends OpasnoCudoviste, Smrtonosno { void pijeKrv();

}
class VeomaZaoVampir implements Vampir public void zapreti() public void ubij() {} {} {} public void u n is ti() {} public void pijeKrv() {

}
public class HororPredstava { static void u(Cudoviste b) { b.zapreti(); static void v(OpasnoCudoviste d) { d. z a p r e t i (); d.unisti (); }

}
static void w(Smrtonosno s) { s.ubij(); public static void main(String[] args) OpasnoCudoviste barni = new Zmaj(); u( b a r n i ) ; v( b a r n i ) ; Vampir vlad = new Ve om aZ ao Va mp ir (); u(vlad); v( v l a d ) ; w( v l a d ) ; { }

} } ///:Interfejs OpasnoCudoviste je je d n o sta v n o pro iren je interfejsa Cudoviste. O n je realizovan u klasi Zmaj. Sintaksa u p o tre b ljen a za interfejs Vampir m oe da se koristi sa n io kada nasledujete interfejse. Uz rezervisanu re extends m o ete da u p o treb ite sam o jed n u klasu, ali poto interfejs m o e da se sastoji od vie d ru g ih , p rilik o m pravljenja novog interfejsa rezervisana re extends m oe da se o d n o si na vie interfejsa. Kao to vidite, im ena interfejsa sam o su razdvojena zarezim a.

Veba 14: (2) N ap rav ite tri interfejsa o d kojih svaki im a po dve m etode. N asledite novi interfejs iz ta tri uz dod av an je jo je d n e nove m etode. N apravite klasu realizujui novoform ira n i interfejs i isto v rem en o nasleujui k o n k retn u klasu. Sada napiite etiri m etode o d kojih e svaka za arg u m en t im ati je d a n o d d atih interfejsa. U m eto d i m a in ( ) nap rav ite o bjek at te klase i prosled ite ga svakoj m eto d i. Veba 15: (2) Izm en ite p re th o d n u vebu pravei ap stra k tn u klasu i iz nje izvedite klasu.

Poglavlje 9: Interfejsi

255

Sukobljavanje imena prilikom kombinovanja interfejsa


P rilik o m k o m b in o v a n ja vie interfejsa m oete d a se suoite s m alo m zakoljicom . U p re th o d n o m p rim e ru , interfejs M o zeD aS eB o ri i klasa A k c io n iju n a k im aju m e to d u id en tin o g im en a - v o id b o riS e(). U n aem p rim e ru to nije p ro b lem , je r su m e to d e iste u obe klase, ali ta ako se m eto d e razlikuju p o p o tp isu ili p o v ra tn o m tipu? Sledi p rim e r:
//: interfejsi/Sukobljavanjelnterfejsa.java package interfejsi; interface II { void f(); ) interface 12 { int f (int i); ) interface 13 { int f(); } class C { public int f() { return 1; } } class C2 implements II, 12 { public void f() {} public int f (int i) { return 1; } // preklopljena

}
class C3 extends C implements 12 { public int f(int i) { return 1; } // preklopljena

}
class C4 extends C implements 13 { // Identina, nije problem: public int f() { return 1; }

}
// Metode se razlikuju samo po povratnom tipu: //! class C5 extends C implements II {} //! interface 14 extends II, 13 {} ///:-

P ro b lem se javlja je r su redefinisanje, realizacija i preklapanje n ezg o d n o izm eani, a p rek lo p ljen e funkcije ne m ogu da se razlikuju sam o p o p o v ra tn o m tip u . Kaa se u kloni k o m e n ta r ispred poslednja dva reda p rim e ra , p o ru k a o greci sam a sve govori: SukobljavanjeInterfejsa.java:23: f( ) in C cannot im p lem en t f ( ) in II; a ttem p tin g to use incom patible return type fo u n d : in t recjuired: void SukobljavanjeInterJejsa.java:24: interjaces 13 a n d II are incompatible; both define f( ) , b u t vvith different return type Ako u p o tre b ite ista im ena m eto d a u razliitim interfejsim a koje n am erav ate da kom b in ujete, uglavno m ete u neti konfuziju i sm an jiti itljivost p ro g ram a. T rud ite se da to izbegnete.

256

Misliti na Javi

Prilagoavanje interfejsu
Jedan o d najvanijih razloga za u p o tre b u in terfejsa jeste m o g u n o st p o sto jan ja vie realizacija istog interfejsa. U je d n o stav n im sluajevim a, to se ra d i u o b lik u m e to d e koja p rihvata interfejs, a vam a ostavlja d a ga realizujete i m eto d i p ro sled ite svoj objekat. Stoga se interfejsi o b in o u p o treb ljav aju u p re th o d n o s p o m e n u to m p ro jek tn o m o b rascu Strategy. N apiete m e to d u koja obavlja izvesne operacije, a ta m eto d a p rih v ata i interfejs koji vi zadate. U sutin i, vi kaete: M oe u p o tre b ljav a ti m o ju m e to d u s bilo kojim o b jek to m koji je usaglaen s m o jim interfejsom ". T im e vaa m e to d a po staje fleksibilnija, optija i upotrebljivija. N a prim er, k o n stru k to r Java SE5 klase Scanner (o kojoj ete vie sazn ati u poglavlju Z n akovn i nizovi) p rim a interfejs Readable. V ideete d a Readable nije a rg u m e n t n ijed n e d ru g e m eto d e u stan d a rd n o j b iblioteci Jave - n ap rav ljen je sam o za Scanner, tako da Scanner ne m o ra da ogranii svoj a rg u m e n t n a o d re e n u klasu. N a taj nain , Scanner m oe da radi s vie tipova. A ko prav ite n o v u klasu i h o ete d a o n a b u d e u p o treb ljiv a s klasom Scanner, neka b u d e Readable (itljiva):
//: interfejsi/SlucajneReci.java // Realizovanje interfejsa tako a bude usaglaen sa odreenom metodom. import java.nio.*; import ja va .u ti l.*; public class SlucajneReci implements Readable {

private static Random slucajno = new Random(47); private static final char[] velikaslova = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".t o C h a r A r r a y (); private static final char[] malas = "abcdefghijklmnopqrstuvwxyz".toCharArray(); private static final private int brojac; public SlucajneReci(int brojac) public int read(CharBuffer cb) { iffbrojac == 0) return -1; // Pokazuje kraj ulaza c b .append(velikaslova [s lucajno.nextInt(velikaslova.1en gt h) ]); for(int i = 0; i < 4; i++) { cb.append(samoglasnici[slucajno.nextlnt(samoglasnici.1ength)]); cb.append(mal as[sl ucajno.nextInt(malas.length)]); { this.brojac = brojac; } char[] samoglasnici = "aei o u " .to Ch arArray();

}
cb.append(" "); return 10; // Broj dodatih znakova

}
public static void m a i n ( S t r i n g [] args) w h i 1e ( s .ha sN ex t()) System .o ut.prin tl n( s.n e x t ()); { Scanner s = new Scanner(new S1 uc a j n e R e c i (10));

Poglavlje 9: Interfejsi

257

} /* Ispis: Yazeruyac Fowenucor Goeazimom Raeuuacio Nuoadesiw Hageai kux Ruqicibui Numasetih Kuuuuozog Waqizeyoy

* ///:Interfejs Readable zahteva sam o realizaciju m eto d e read(). U m eto d i read(), argum e n tu tipa CharBuffer dod ajete znakove (to se m o e u ra d iti n a nekoliko naina; v ideti d o k u m e n ta c iju klase CharBuffer) ili vraate -1 kada ulaza vie nem a. P retp o stav im o da im ate klasu koja nije realizovala Readable - kako ete postii da o n a radi s klasom Scanner? Evo jed n e klase koja pro izv o d i sluajne realne brojeve (s p o k retn im zarezom ):
//: interfejsi/SlucajniDouble.java

import ja v a . u t i l .*; public class SlucajniDouble { private static Random slucajan = new Random(47); public double next() { return sl uc aj an .n ex tD ou bl e(); } { public static void ma in ( S t r i n g [] args) for(int i = 0; i < 7 ; i ++)

S1ucajniDoubles sc = new S1uc aj ni Do ub le (); System.out.print(sc.next() + " ");

}
} /* Ispis: 0.7271157860730044 0.5309454508634242 0.16020656493302599 0.18847866977771732 0.5166020801268457 0.2678662084200585 0.2613610344283964

* ///:Fonovo sm o u p o treb ili p ro jek tn i o b razac A dapter , ali se u ovom sluaju prilag o en a klasa m oe nap rav iti nasleivanjem i realizacijom interfejsa R ead ab le. D akle, koristei pseu do viestruk o nasledivanje koje o m oguava rezervisana re in terfa ce, napravili sm o novu klasu koja je i tip a S lu c a jn iD o u b le i tipa R eadable:
//: interfejsi/PrilagodjeniSlucajniDouble.java

// Pravljenje adaptera s nasleivanjem. import java.nio.*; import j a va .u ti1 .*; public class Pri1agodjeniS1ucajniDouble extends S1ucajniDouble implements Readable { private int brojac;

258

Misliti na Javi

public PrilagodjeniSlucajniDouble (int brojac) this.brojac = brojac;

}
public int read(CharBuffer cb) { if(brojac-- == 0) return -1; String rezultat = Double.toString(next()) + 1 1 cb.append(rezultat); return rezultat.length();

}
public static void main(String[] args) while(s.hasNextDouble()) System.out.print(s.nextDouble() + " "); { Scanner s = new Scanner(new Pr il ag odjeniSlucajniDouble(7));

}
} /* Ispis: 0.7271157860730044 0.5309454508634242 0.16020656493302599 0.18847866977771732 0.5166020801268457 0.2678662084200585 0.2613610344283964

* ///:Poto na ovaj nain svakoj postojeoj klasi m o ete d o d a ti interfejs, znai da m etoda koja p rim a interfejs obezbeuje i n ain p rilag o en ja bilo koje klase za rad s to m m eto dom . U to m e lei m o korienja interfejsa u m esto klasa. V eba 16: (3) N apravite klasu koja proizvodi niz znakova (objekata tip a ch a r). Prilagodite tu klasu tako da se m oe u p o tre b iti za ulaz objekta tip a S can n er.

Polja u interfejsima
Poto su sva polja koja stavite u interfejs au to m atsk i statin a i finalna, interfejs je korisna alatka za pravljenje g ru p a k o n sta n tn ih v red n o sti. Pre Jave SE5, to je b io jed in i nain da se postig ne efekat koji daje rezervisana re e n u m u C -u ili C ++-U . D akle, naii ete na ovakav k o d n apisan p re Jave SE5:
//: interfejsi/Meseci.java // Upotreba interfejsa za pravljenje grupa konstanti. package interfejsi; public interface Meseci int JANUAR = 1, FEBRUAR = 2, MART = 3, APRIL = 4, MAJ = 5, JUNI = 6, JULI = 7, AVGUST = 8, SEPTEMBAR = 9, 0KT0BAR = 10, N0VEMBAR = 11, DECEMBAR = 12; {

} ll l - ~ O b ra tite p an ju na to da je i ovde u p o treb ljen uob iajen i sti! pisanja statin ih finalnih polja u Javi, ije su v red n o sti inicijalizovane k o n stan ta m a sve se pie velikim slovim a,

Poglavlje 9: Interfejsi

259

d o k se vie rei u je d n o m id en tifik ato ru razdvaja p o tc rto m . Polja u interfejsu su a u to m a tski javna, pa to nije p o tre b n o e k sp li tn o zadati. O d pojave Jave SE5, n a rasp o lag an ju v am je m n o g o m o n ija i fleksibilnija rezervisana re e n u m , p a vie n e m a sm isla u p o treb ljav ati interfejse za k o n stan te. M e u tim , p riliko m itan ja starog koda v erov atn o ete esto nailaziti n a taj s ta ri id io m (d o d aci ove laijige na w w w .M indV iew .net sadre p o tp u n o pis starog n a in a p ravljenja n a b ro jan ih tipova p o m o u interfejsa). Vie p o jed in o sti o u p o tre b i rezervisane rei e n u m nai ete u poglavlju N abrojani tipovi. V eba 17: (2) D okaite da su p olja interfejsa au to m atsk i statin a i finalna.

Inicijalizacija polja u interfejsima


Polja definisana u interfejsim a ne m o g u b iti p ra zn a finaln a polja, ali m og u da b u d u inicijalizovana n ek o n sta n tn im izrazom . N a p rim er:
//: interfejsi/SlucajnePromenljive.java // Inicijalizacija polja u interfejsu pomou // nekonstantnih inicijalizacionih vrednosti import java.util public interface SlucajnePromenljive { Random SLUCAJAN = new Random(47); int SLUCAJAN_INT = SLUCAJAN.nextInt(10); long SLUCAJAN_LONG = SLUCAJAN.nextLong() * 10; float SLUCAJAN_FLOAT = SLUCAJAN.nextLong() * 10; double SLUCAJAN_DOUBLE = SLUCAJAN.nextDouble() * 10;

) ///:Poto su polja statin a, bie inicijalizovana kada klasa b u d e prvi p u t u itana, to e se desiti kada prvi p u t Jiudete p ristu p ili bilo koni polju. Evo i jed n o stav n o g testa:
//: interfejsi/TestirajSlucajnePromenljive.java import static n e t .mindview.uti1 .Print.*; public class TestirajSlucajnePromenljive { public static void main(String[J args) { pri n t (S1ucajnePromenl j i v e .SLUCAJAN_INT); pri nt(S1ucajnePromenlj iv e .SL UC AJ AN _L ON G); pri n t (S1ucajnePromenlji ve .S LUCAJAN_FLOAT); print(S1ucajnePromenlji ve .SLUCAJAN_DOUBLE);

)
} /* Ispis:

8
-32032247016559954 -8.5939291E18 5.779976127815049

* ///:O va polja nisu d eo interfejsa, ve se uvaju u sta tin o m skladitu to g interfejsa.

260

Misliti na Javi

Ugneivanje interfejsa
Interfejsi3 m o g u da b u d u u g n eden i u n u ta r d ru g ih klasa ili d ru g ih interfejsa. T im e se o tk riv a nekoliko veom a k o risn ih osobina:
//: interfejsi/ugnezdjivanje/Ugnezdjivanjelnterfejsa.java package interfejsi.ugnezdjivanje; class A { interface B { void f ();

}
public class Blmp implements B { public void f() {}

}
private class BImp2 implements B { public void f() {}

}
public interface C { void f();

}
class Clmp implements C { public void f() {}

}
private class CImp2 implements C { public void f() {}

}
private interface D { void f();

}
private class Dlmp implements D { public void f() {}

}
public class DImp2 implements D { public void f() {}

}
public D uzmiD() private D dRef; public void primiD(D d) { dRef = d; dRef.f(); { return new DImp2(); }

interface E { interface G { void f ();

}
Z a h v a lju je m M a r tin u D a n e ru to je p o s ta v io o v o p ita n je to k o m se m in a ra .

Poglavlje 9: Interfejsi

261

// Suvian public": public interface H { void f();

}
void g ( ) ; // Ne moe da bude privatan unutar interfejsa: //! private interface I {}

}
public class Ugnezdjivanjelnterfejsa { public class Blmp implements A.B { public void f() {}

}
class Clmp implements A.C { public void f() {}

}
// Privatni interfejs moe da se realizuje samo u klasi koja ga je definisala. //! class Dlmp implements A.D { //! public void f() {}

//! }
class Elmp implements E { public void g() {}

}
class EGImp implements E.G { public void f() {}

}
class EImp2 implements E { public void g() {} class EG implements E.G { public void f() {}

} }
public static void main(String[] A a = new A ( ) ; // Nije mogu pristup A.D: //! A.D ad = a . u z mi D( ); // Vraa samo A.D: //! A.DImp2 d i2 = a.uzmiD(); // Ne moe da se pristupi lanu interfejsa: //! a. u z m i D ( ) .f (); // Samo drugi A moe neto da uradi sa uzmiD(): A a2 = new A ( ) ; a2 .p ri mi D( a. uz mi D( )); args) {

} } ///:S intaksa ugneivanja interfejsa u n u ta r klase p rilin o je oig led n a i, p o p u t n eugne en ih interfejsa, on i tak o e m ogu im ati javni ili paketni p ristu p . Postoji nova zakoljica: interfejsi takoe m ogu da b u d u p rivatn i, kao to je sluaj s A.D (za ugneene interfejse koristi se ista sintaksa kao i za u gneene klase). em u slui p ri-

262

Misliti na Javi

vatni ugneeni interfejs? M ogli biste p retp o stav iti kako o n m oe sam o da b u d e realizovan kao priv atn a ugneena klasa u klasi D lm p, ali n am klasa A.DImp2 pokazuje d a o n takoe m oe biti realizovan i kao jav n a klasa. Klasa A.DImp2, m ed u tim , m oe da se up otrebljava sam o kao klasa tog o d re en o g tipa. Poto ne p ostoji nain da se spolja vidi kako ona, u stvari, realizuje privatn i interfejs D, to je realizovanje p riv atn og interfejsa nain da n am etnete p rim e n u definicija nekog interfejsa, a da p ri to m e ne do d ate i inform aciju o tip u (odnosno , tim e neete dozvoliti svoenje navie). M etoda uzm iD() u n o si d o d a tn u d ile m u po p ita n ju p riv atn o g interfejsa: o na je javna m eto d a koja vraa referencu n a p riv a tn i interfejs. ta m oete da u rad ite s p o v ra tn o m v redno u ove m etode? U m e to d i m ain() m o ete da uoite nekoliko po ku aja da se ta p o v ra tn a v red n o st u p o treb i, ali se svi o n i zavravaju n eu sp eh o m . P o v ratn u v red n o st jed in o m oete d a p red ate d ru g o m o b jek tu koji im a ovlaenja da je koristi - u ovom sluaju, d ru g o m o b jek tu klase A p rek o m e to d e p rim iD ( ). Interfejs E po kazuje d a interfejsi m o g u d a b u d u ugn een i jed an u n u ta r drugog. Pravila o interfejsim a - p o seb n o o n o da svi elem en ti interfejsa m o ra ju da b u d u javni - ovde se strogo p o tu ju , pa je interfejs u g n e en u n u ta r d ru g o g interfejsa au to m atsk i javan i ne m oe b iti proglaen p riv atn im . Klasa Ugnezdjivanjelnterfejsa p o k azu je razliite naine na koje ugneeni interfejsi m o gu da b u d u realizovani. P rilikom realizovanja interfejsa n e m o rate da realizujete i interfejse koji su u njega u gneeni. Takoe, p riv atn i interfejsi ne m o gu da b u d u realizovani van klase u kojoj su definisani. U poetk u vam se m oe u in iti da ove m o g u n o sti postoje sam o zbog k on sistentno sti sintakse. U p rin c ip u , kada zn ate da neka m o g u n o st postoji, sm a tram da m oete naii na m esto na kom e e k o risn o d a vam poslui.

Interfejsi i Proizvoai
Interfejs bi trebalo da poslui kao p u t do vie realizacija, a tipian nain pravljenja objekata usklaenih sa interfejsom jeste p ro jek tn i obrazac Factory M ethod (P roizvodna m etoda). U m esto da k o n stru k to r pozivate n e p o sred n o , vi pozivate m eto d u za pravljenje proizvodn og objekta koji proizvodi realizaciju tog interfejsa - tim e bi, teorijski, va kod bio p o tp u no izolovan o d realizacije interfejsa, to bi om oguilo n e p rim etn u zam enu jed n e realizacije d ru g om . Evo jed n o g p rim era koji e pokazati stru k tu ru obrasca Factory Method:
/ / : interfejsi/Proizvodjaci.java
import static ne t.mindview.util.Print.*; interface Usluga { void me t o d a l ( ) ; void me t o d a 2 ( ) ;

}
interface ProizvodjacUsluga { Usluga uzmiUslugu ();

Poglavlje 9: Interfejsi

263

class Realizacijal implements Usluga { Realizacijal() {} // Paketni pristup {print("Realizacijal me to da l1 ');} {print("Realizacijal metoda2");} public void metodal() public void metoda2()

}
class RealizacijalProizvodjac implements ProizvodjacUsluga { public Usluga uzmiUslugu() { return new Realizacijal();

} }
class Rea1izacija2 implements Usluga { Realizacija2() {} // Paketni pristup {print("Realizacija2 metodal");} {print("Realizacija2 metoda2");} public void metodal() public void metoda2()

}
class Realizacija2Proizvodjac implements ProizvodjacUsluga { public Usluga uzmiUslugu() { return new Re al izacija2();

} }
public class Proizvodjaci { {

public static void korisnikUsluge(ProizvodjacUsluga fabr) Usluga s = fa br .u zmiUslugu(); s.metodal (); S ,metoda2();

}
public static void main(String[] args) // Realizacije su potpuno zamenljive: korisnikUsluge(new Realizacija2Proizvodjac ()); { korisnikUsluge(new RealizacijalProizvodjac ());

}
} /* Ispis: Realizacijal metodal Realizacijal metoda2 Realizacija2 metodal Realizacija2 metoda2

* ///:Da n e m a obrasca Factory M ethod , negde u k odu m orali biste da navedete taan tip interfejsa U slu g a koji se prav i, da bi on m ogao da pozove odgovarajui k on stru ktor. Z ato da ovek do d aje jo jed an nivo posredovanja? Jedan od uobiajenih razloga je pravljenje zajednikog kostura. P retp o stav im o da p ravite sistem za igranje igara; na prim er, za igranje aha i dam e na istoj tabli:
//: interfejsi/Igre.java

// Kostur za Igre napravljen pomou Proizvodnih metoda. import static net.mindview.uti1.Print.*;

264

Misliti na Javi

interface Igra { boolean potez(); } interface Proizvodjaclgara { Igra uzmilgru(); } class Dama implements Igra { private int potezi = 0; private static final int POTEZI = 3; public boolean potez() return ++potezi { print("Dama potez " + p o t e z ) ; != POTEZI;

} }
class ProizvodjacDama implements Proizvodjaclgara { public Igra uzmilgru() { return new Dama(); }

}
class sah implements Igra { private int potezi = 0; private static final int POTEZI = 4; { public boolean potez() return ++potezi

print("ah potez " + potezi); != POTEZI;

} }
class Proizvodjacsah implements Proizvodjaclgara { public Igra uzmilgru() { return new sah(); }

}
public class Igre { public static void igrajIgru(ProizvodjacIgara proizvodjac) Igra s = pr oizvodjac.uzmiIgru(); whi1e(s.potez()) {

}
public static void main(String[] args) { igraj Igru(new P r o i z v o d j a c D a m a O ) ; igrajlgru(new Proizvodjacsah());

}
} /* Ispis: Dama potez 0 Dama potez 1 Dama potez 2 ah potez 0 ah potez 1 ah potez 2 ah potez 3

* ///:-

Poglavlje 9: Interfejsi

265

U koliko klasa Igre predstavlja sloen deo k o d a, ovaj p ristu p om og uav a p o n o v n u u p o tre b u tog koda s razliitim v rstam a igara. P ostoje i kom plik ov anije igre za koje bi se m ogao iskoristiti taj obrazac. U sledeem poglavlju videete elegan tniji n ain za realizovanje Proizvoaa p o m o u a n o n im n ih u n u tra n jih klasa.

Veba 18: (2) N apravite interfejs Cikl s realizacijam a Unicikl, Bicikl i Tricikl. N apravite Proizvoae za svaki tip Cikla i k o d koji u p o treb ljav a te Proizvoae. Veba 19: (3) P o m o u p ro izv o d n ih m eto d a n ap rav ite zajedniki k o stu r za bacanje n o via i bacanje kocke.

Saetak
Lako je pasti u zam k u i sve interfejse p roglasiti d o b rim pa ih zbog toga uvek treb a p retp ostav iti k o n k re tn im klasam a. N aravno, u m esto svake klase g otovo uvek se m oe n a p ra viti interfejs i Proizvoa. M no gi su podlegli to m iskuenju i n apravili interfejse i Proizvoae gde go d su m ogli. Razlog: m o d a e zatrebati dru g aija realizacija, p a tu ap strak ciju treb a d o d a ti uvek. To se pretv orilo u svojevrsnu p re ra n u o p tim izaciju p ro jek ta. Svaku apstrakciju treba da m otivie o d re en a stv arn a p o treb a. Ako vam zatreb aju , interfejse treba da d o d ate tek p rilik o m p o n o v n e p o d ele na proste faktore. N em ojte taj d od a tn i nivo indirekcije stavljati o d m a h i svugde, je r tim e poveavate sloenost. D o d atn a sloenost je bitna, i ako m e neko n atera da p ro e m kroz n ju sam o da b ih na kraju shvatio kako je interfejs d o d a t ,,za svaki slu aj, a bez prav o g razloga p o su m n ja u u sve p ro jek te te osobe. U m esna sm ern ica je radije koristite klase nego interfejse. Ponite o d klasa, pa ako se ispostavi da su interfejsi n e o p h o d n i, ponovo sve p o d elite na p roste faktore. Interfejsi su od line alatke, ali je lako p reterati s njih o v o m u p o tre b o m .
R e e n ja o d a b r a n ih v e b i d a ta su u e le k tr o n s k o m d o k u m e n t u T liitikin g in Java A n n o ta te d S olution

G uide , ko ji sc m o e k u p iti n a lo k a c iji w w w .M in d V iew .n et.

Unutranje kfase
D efiniciju klase m oete da stavite u n u ta r definicije neke druge klase. Takva klasa se naziva u n u tra n ja klasa (engl. inner class).
U n u t r a Sn j a k l a s a j e v r e d n a , z a t o St o o m o g u u j e d a k l a s e l o g i k i G R U PlSETE I DA

k o n tro liete n jih o v u m e u so b n u viljivost. Vano je shvatiti da su u n u tra n je klase b itn o razliite o d kom pozicije. N a p rv i pogled, u n u tra n je klase izgledaju kao jed n o stav an m eh an iz am za sakrivanje koda: p rek o n jih u gra u jete klasu u n u ta r drug e klase. N auiete d a u n u tra n ja klasa p ru a i vie o d toga - o n a je svesna p risustv a klase koja je o k ru u je i m oe s n jo m d a k o m u n icira. Iako su za vein u u n u tra n je klase sasvim n o v k on cep t, p o m o u n jih m oete da piete finiji i jasn iji ko d , m a d a niko n e jam i d a e takav i biti. D o k uite o u n u tra n jim klasam a, n jihova sv rh a esto nije oigledna, pa navikavanje n a njih m oe d a p o traje. N akon opisa o sn ov ne sintakse i sem an tike u n u tra n jih klasa, u o d eljk u Z ato u n u tra n je klase? nai ete p rim ere koji e o b jasniti p re d n o sti u n u tra njih klasa. N akon to g odeljka, u o statk u poglavlja detaljnije je raz m o tren a sintaksa u n u tra n jih klasa. Te m o g u n o sti su d o d ate rad i p o tp u n o sti jezika, ali vam v ero vatno nee treb ati, naro ito ne isprva. Z ato e vam p o etn i delovi poglavlja zasa verovatno biti dovoljni, a detaljnije istraivanje m oete sm a tra ti referen tnim m aterijalo m .

Pravljenje unutranjih klasa


U n u tra n ju klasu prav ite ba kako biste oekivali - tako to definiciju klase u m etn ete u sp o ljn u klasu:
//: unutrasnjeklase/Posi1j k a l .java // Pravljenje unutranjih klasa. public class Posiljkal { class Sadrzina { private int i = 11; public int value() { return i; }

}
class Odrediste { private String oznaka; Odrediste(String gde) oznaka = gde; {

}
String pr oc it aj Oz na ku() { return oznaka; }

}
// Upotreba unutranjih klasa izgleda kao i upotreba // bilo koje druge klase unutar klase Posiljkal: public void posalji(String odr) Sadrzina c = new Sadrzina(); {

Poglavlje 10: Unutranje klase

267

Odrediste d = new Odrediste(odr); System.out.println(d.procitajOznaku());

}
public static void main(String[] args) Posiljkal p = new Posiljkal(); p.posal ji (''Tanzani j a " ) ; {

}
} /* Ispis: Tanzanija

* ///:U p o treb a u n u tra n je klase u n u ta r m eto d e p o sa lji() izgleda kao i u p o tre b a bilo koje d ru g e klase. U ovom sluaju, jed in a vana razlika je to to su im en a u g n e en a u n u ta r klase P o s iljk a l. U skoro ete videti da to nije jed in a razlika. Spoljna klasa e ee im ati m e to d u koja v raa referencu n a u n u tra n ju klasu, kao to se vidi u m e to d a m a za() i sad r():
//: unutrasnjeklase/Posiljka2.java // Vraanje reference na unutranju klasu public class Posiljka2 { class Sadrzina { private int i = 11; public int vrednost() { return i; }

}
class Odrediste { private String oznaka; Odrediste(String gde) { oznaka = gde;

}
String procitajOznaku() { return oznaka; }

}
public Odrediste za(String s) { return new Od rediste(s);

}
public Sadrzina s a d r () { return new Sadrzina();

}
public void p o sa lji (String odr) Sadrzina c = sadr(); Odredi ste d = za (o dr ); Sy st em .o ut.pri ntln(d.proci tajOznaku()); {

}
public static void m a in (S tr in g[] args) Posiljka2 p = new Posi1j k a 2 (); p.posalji("Tanzanija"); Posiljka2 q = new Posiljka2(); // Definisanje referenci na unutranje klase: Po si1jka2.Sadrzina c = q.sadr(); Posi1jka2.0drediste d = q. za("Borneo"); {

268

Misliti na Javi

} /* Ispis: Tanzanija

* ///:Ako elite da o bjek at u n u tra n je klase n ap rav ite izvan neke n estatin e m eto d e spoljne klase, kao tip to g objekta m o rate d a navedete Im eSpoljneKlase.ImeU nutranjeKlase, kao to se vid i u m e to d i m a in ().

Veba I: (1) N apiite klasu Spoljna koja sadri u n u tra n ju klasu Unutrasnja. Klasi Spoljna d o d ajte m e to d u koja vraa o b jek at tip a Unutrasnja. U m e to d i m a in ( ) n ap rav ite i inicijalizujte referencu n a o b jek at klase Unutrasnja.

Veza ka spoljnoj klasi


D o sada izgleda d a su u n u tra n je klase sam o teh n ik a za sakrivanje im en a i za organizaciju koda, koja je k o risn a, ali n e i n e o p h o d n a . Postoji, m e u tim , jo je d n a zakoljica. Kada n apravite u n u tra n ju klasu, n jen o b jek at im a vezu sa objektom okolne klase k o ja g a je i napravila, p a o b jek at u n u tra n je klase m o e d a p ristu p a lan o v im a objekta koji ga o k ru u je - bez n ekih p o seb n ih kvalifikatora. U n u tran je klase, p o re d toga, im aju prava da p ristu p aju svim e lem en tim a o k ru u ju e klase.1 To pokazuje n a re d n i p rim er:
//: unutrasnjeklase/Sekvenca.java // uva niz objekata interface Selektor { boolean kraj(); Object t e k u c i (); void sledeci ();

}
public class Sekvenca { private Object[] obs; private int sledeci = 0; {obs = new O b j e ct [v el ic in a]; } public Sekvenca(int velicina) public void dodaj(0bject x) { if(sledeci < obs.length) obs[sledeci++] = x; {

}
private class SelektorSekvence implements Selektor { private int i = 0; public boolean kraj() public void sledeci() {return i == obs.length;} {i f (i < obs.length) { i++; } public Object tekuci () {return obs[i];}

}
public Selektor selektor() return new S e le kt or Se kv en ce ();

}
' U ovom pogledu, ugneene khise u C + + -u veom a se razlikuju, jer su o n e sam o m ehanizam za sakrivanje im ena. U C++-U ne postoji veza sa okolnim objektom niti podrazum evana prava pristupa.

Poglavlje 10: Unutranje klase

269

public static void main(String[] args) Sekvenca sekvenca = new Sekven ca (l O); for(int i = 0; i < 10; i++)

se kvenca.dodaj(Integer.toString(i)); Selektor selektor = se kvenca.selektor(); while(!selektor.kraj()) sele kt or .s le de ci(); { Sy st em.out.println(selektor.tekuci() + " ");

} }
} /* Ispis: 0 1 2 3 4 5 6 7 8 9

* ///:Klasa Sekvenca ob u h v ata sam o n iz fiksne d u in e elem enata tip a Object. M oete da pozovete m e to d u dodaj() kako biste d o d ali n o v elem en t tip a Object na kraj niza (ako u n je m u im a m esta). Za traen je elem en ata niza p o sto ji interfejs Selektor. To je p rim e r p ro jek tn o g o brasca Iterator (ite ra to r), o kojem ete vie saznati u nastavku knjige. Selektor om og u av a d a p rov erite d a li ste na k raju niza (m eto d a kraj()), da pogledate tekui objek at (m e to d a tekuci()) i da se p o m erite na sledei elem en t u n izu (m eto d a sledeci()). Poto je Selektor interfejs, njega m ogu da realizuju i m noge d ru g e klase na neki svoj nain, a taj interfejs m oe biti arg u m en t m n o g ih m eto d a, im e se prav i generiki kod. U naem p rim e ru , p riv a tn a klasa SelektorSekvence prua fu n k cio n aln o st interfejsa Selektor. U m eto d i main() se, n ak o n pravljenja objekta klase Sekvenca, u taj objekat d o daje nekoliko objek ata tipa String. Z atim se poziv an jem m eto d e selektor pravi Selektor koji se k oristi za p o m e ra n je p o o b jek tu klase Sekvenca i za p ristu p svakom elem en tu. Na prvi pogled, pravljenje klase SelektorSekvence izgleda p o p u t jo jedne u n u tra n je klase. P ro u im o je paljivije. O b ratite p an ju na to da se sve m eto d e - kraj(), tekuci() i sledeci() o b raaju referenci obs koja nije deo klase SelektorSekvence, ve je p riv atn o polje o k oln e sp oljn e klase. U n u tran ja klasa, m e u tim , m oe da p ristu p a m eto d am a i p oIjima spo ljne klase kao da p rip ad a ju njoj. Ta o so b in a m oe da b u d e veom a p o g o d n a, kao to se vidi u p re th o d n o m p rim e ru . Z nai, u n u tra n ja klasa im a au to m atsk i p ristu p lanovim a klase koja je okruuje. Kako se to deava? U n u tran ja klasa skriveno uva referencu na onaj objekat spoljne klase koji je tu u n u tra n ju klasu n apravio. P rilikom p ristu p a n ja lanovim a spoljne klase up o trebljava se ta (skrivena) referenca. Prevodilac se, na sreu, b rin e o svim d etaljim a um esto vas. Sada k o n an o m oete da shvatite zato u n u tra n ja klasa m oe da se n apravi sam o zaje d n o sa o b jek to m spo ljne klase (kada je, kao to ete videti, u n u tra n ja klasa n estatin a). Za k o n stru k ciju objekta u n u tra n je klase p o tre b n a je referenca na objekat spoljne klase i prevodilac e se p o b u n iti ako toj referenci ne m oe da pristu p i. U veini sluajeva, ovaj scenario se odigrava bez intervencije p ro g ram era.

Veba 2: (1) N apravite klasu koja uva jedan String i im a m eto d u toStringf) koja taj String prikazuje. D od ajte nekoliko p rim erak a te klase o b jek tu tipa Sekvenca i prikaite ih. Veba 3: (1) Izm en ite vebu 1 tako da Spoljna im a p riv atn o polje tipa String (koje inicijalizuje k o n stru k to r), a Unutrasnja m eto d u toStringO koja to polje prikazuje. N apravite objekat tipa Unutrasnja i p rik aite ga.

270

Misliti na Javi

Upotreba sintaksi .this i .new


Ako v am u u n u tran jo j klasi treb a referenca n a objek at spoljne klase, n avedite im e spoljne klase i iza nje tak u i rezervisanu re th is. D o b ijen a referenca e au to m atsk i biti ispravnog tipa, to se zna i proverava p rilik o m p rev o en ja, a ne p rilik o m izvravanja, p a nem a reijskih trokova za to. Evo p rim e ra koji po k azu je u p o tre b u .th is:
//: unutrasnjeklase/TackaThis.java // Kvalifikovanje pristupa objektu spoljne klase. public class TackaThis { void f() { System.out.println("TackaThis.f()"); public class Unutrasnja { public TackaThis spoljna() return TackaThis.this; // Golo "this" bi bilo "this" od klase Unutrasnja { }

} }
public Unutrasnja unutrasnja() { return new U n u t ra sn ja (); } args) { public static void main(String[] TackaThis dt = new TackaThis(); TackaThis.Unutrasnja dti = d t . u n u tr as nj a( ); dt i. sp oljna().f();

}
} /* Ispis: TackaThi s .f ()

* ///:P onekad n ek om d ru g o m o b jek tu tre b a n are d iti da n ap rav i o b jek at neke o d svojih u n u tran jih klasa. Da biste to postigli, sin tak so m ,n ew pro sled ite referencu na taj drugi objekat spoljn e klase, kao u n a re d n o m p rim e ru :
//: unutrasnjeklase/TackaNova.java // Neposredno pravljenje unutranje klase operatorom .new. public class TackaNova { public class Unutrasnja {} public static void main(String[] TackaNova dn = new TackaNova(); TackaNova.Unutrasnja dni = dn.new Unutrasnja(); args) {

} } ///= N asup ro t oekivanju, da biste d ire k tn o n apravili objekat u n u tra n je klase ne treba da p ratite uob iajenu m eto d u i da se o b ra tite im e n u spoljne klase T ackaN ova - u m esto toga m o rate da u p o treb ite objekat spoljne klase, kao to v idite u p re th o d n o m p rim e ru . T im e se reavaju i p itan ja oblasti vaenja za u n u tra n ju klasu, pa se ne kae dn.nevv TackaN ov a .U n u tr a s n ja ( ). (To se ak ne m oe rei.)

Poglavlje 10: Unutranje klase

271

Z nai, ako n em ate o b jek at spo ljn e klase, ne m oete d a n ap rav ite n i objekat u n u tra n je klase, zbog toga to objekat u n u tra n je klase m o ra biti povezan sa sp o ljn o m klasom . M e u tim , u koliko prav ite ugtieenu klasu (sta ti n u u n u tra n ju klasu), njoj referenca na ob jek at spoljne klase nije p o tre b n a . Evo kako bi se o p e ra to r ,n ew p rim e n io u p rim e ru Poiljka:
//: unutrasnjeklase/Posiljka3.java // Pravljenje instanci unutranje klase operatorom .new public class Posiljka3 { class Sadrzina { private int i = 11; public int vrednost() { return i; )

)
class Odrediste { private String oznaka; Odrediste(String gde) { oznaka = gde; ) String procitajOznaku() { return oznaka; } {

}
public static void main(String[] args) Posiljka3 p = new Posiljka3(); // Morate da upotrebite instancu spoljne klase // da biste napravili instancu unutranje klase: Posiljka3.Sadrzina c = p.new Sadrzina(); Posiljka3.0drediste d = p.new Odrediste("Tanzanija) ;

} } ///:V eb a4 : (2) Klasi S ek ven ca.S elek torS ek v en ce d o d ajte m eto d u koja proizvodi referencu na sp o ljn u klasu Sekvenca. Veba 5: (1) N apravite klasu koja im a u n u tra n ju klasu. U o dvojenoj klasi n ap ravite instan cu u n u tra n je klase.

Unutranje klase i svoenje navie


U n utran je klase dolaze d o izraaja kada ponete da prim enjujete svoenje navie ka osnovnoj klasi, a posebno ka interfejsu. (Efekat dobijan ja reference na interfejs o d objekta koji ga realizuje, u sutini je isti kao i svoenje navie ka osnovnoj klasi.) O vo biva zato to u n u tranja klasa koja realizuje interfejs m oe biti p o tp u n o nevidljiva i n edo stup na, to je pogodno za sakrivanje realizacije. Vraa vam se sam o referenca na osn o vn u klasu ili interfejs. Prvo em o definisati zajednike interfejse za p re th o d n e p rim ere:
//: unutrasnjeklase/Odrediste.java public interface Odrediste { String pr oc it aj Oz na ku();

} ///= -

272

Misliti na Javi

//: unutrasnjeklase/Sadrzina.java public interface Sadrzina ( int vrednost();

1III-Sada S a d rz in a i O d re d is te p red stav ljaju interfejse d o stu p n e p ro g ra m e ru klijen tu koji koristi nau biblio tek u klasa. (Setite se d a su svi lano vi interfejsa au to m atsk i javni.) Kada d obijete referencu n a o sn o v n u klasu ili n a interfejs, m o g u e je da u o p te ne m o ete d a otk rijete stvarni tip objekta, kao to je p rik aza n o u sledeem p rim eru :
//: unutrasnjeklase/ProbnaPosiljka.java public class Posiljka4 { private class PSadrzina implements Sadrzina { private int i = 11; public int vrednost() { return i; }

}
protected class POdrediste implements Odrediste { private String oznaka; private POdrediste(String gde) { oznaka = gde;

}
public String procitajOznaku() { return oznaka; }

}
public Odrediste odrediste(String s) { return new POdrediste(s);

}
public Sadrzina sadr() { return new PSadrzina();

} }
public class ProbnaPosiljka { public static void main(String[] Posiljka4 p = new P o s i 1j k a 4 (); Sadrzina c = p.sadr(); Odrediste d = p. od re di st e( "T an za nij a" ); // Nije dozvoljeno -- ne moete da pristupite privatnoj klasi: //! Posiljka4.PSadrzina pc = p.new PSadrzina(); args) {

III---

U klasi Posiljka4 d o d ato je neto novo: u n u tran ja klasa P S ad rzln a je privatna tako da niko osim klase P osiljka4 ne m oe da joj pristupi. O bin e klase (koje nisu u n u tran je) ne m ogu da b u d u privatne niti zatiene - m ogu im ati sam o javni ili paketni pristup. Klasa P O d re d iste je zatiena, pa ne m oe da joj p ristu p a niko osim klase P osiljka4, klasa koje su u istom paketu kao i P osiljka4 (poto rezervisana re p ro te c te d takoe dozvoljava paketni pristu p ) i naslednika klase P osiljka4. To znai da p ro g ra m e r klijent ne m oe p o tp u n o da

Poglavlje 10: Unutranje klase

273

poznaje te lanove, niti m oe d a im p ristu p a bez ogranienja. Ka priv atn oj u n u tran jo j klasi (ili ka zatienoj u n u tra n jo j klasi ako niste neki o d naslednika), u stvari, ne m oete d a obavite n i svoenje nanie, je r ne m oete da p ristu p ite n jen o m im enu , kao to se vidi u klasi P ro b n aP o siljk a. Privatne u n u tran je klase stoga o m oguavaju p ro jek tan tu klase da p o tp u no sprei zavisnost o d tip a i da p o tp u n o sakrije p o d atk e o realizaciji. Pored toga, proirivanje interfejsa je beskorisno, iz perspektive p ro g ra m e ra klijenta, je r o n ne m oe da p ristu p i ostalim m eto d am a koje nisu deo javnog interfejsa. T im e se takoe Javinom prevodiocu om oguava da pravi efikasniji kod. V eba 6: (2) N apravite interfejs koji sadri b arem je d n u m e to d u u so pstv eno m paketu. D o dajte zatienu u n u tra n ju Jdasu koja realizuje taj interfejs. U treem p ak etu nasledite svoju kJasu i, u n u ta r m eto d e, v ratite o bjek at zatiene u n u tra n je kJase, svodei navie na interfejs to k o m vraanja. V eba 7: (2) N aprav ite Idasu koja im a p riv a tn o p o lje i p riv a tn u m eto d u . N ap rav ite u n u tra n ju klasu ija m eto d a m en ja polje spo ljne kJase i poziva m e to d u spo ljne kJase. U d ru goj m e to d i spoljne kJase n a p ra v ite objek at u n u tra n je ldase i pozo vite n je n u m e to d u , a zatim pok aite dejstvo na objekat spo ljn e kJase. V eba 8: (2) U tv rd ite d a li spo ljn a ldasa im a p ris tu p p riv a tn im elem en tim a svoje u n u tra nje klase.

Unutranje klase u metodama i oblastima vaenja


Na osnovu o noga to ste dosad videli, stekli ste p red stav u o uobiajenoj u po treb i u n u tra njih klasa. P rogram i koje ete pisati i itati, a u k ojim a se pojavljuju u n u tran je klase, po pravilu bie sa obinim " u n u tra n jim Jdasama koje su jednostavne i lako se razum eju. K oncept u n u tra n jih klasa o buhvata jo dosta toga i postoji vie d rug ih , skrivenih naina kako da ih, ako elite, koristite: u n u tran je kJase m o g u da b u d u napravljene u n u ta r m etoda ili ak u proizvoljnoj oblasti vaenja. Tako neto m oete da u rad ite iz dva razloga, ukoliko: 1. Kao to je ranije p okazano, realizujete interfejs tak o da m oete da n ap rav ite i v ratite referencu. 2. Reavate sloen p ro b lem i elite da n ap rav ite klasu koja e vam p o m o i u njegovom reavanju, ali ne elite da o n a b u d e jav n o d o stu p n a. U n are d n im p rim e rim a , p re th o d n i p ro g ra m e biti izm enjen tako da koristi: 1. Klasu defin isanu u n u ta r m etode. 2. Klasu d efin isanu u n u ta r neke oblasti vaenja u m etodi. 3. A nonirniiu klasu koja realizuje interfejs. 4. A n o n im n u klasu koja p ro iru je klasu koja n e m a p o d razu m ev an i kon struk to r. 5. A n o n im n u klasu koja inicijalizuje polja. 6. A n o n im n u klasu koja obavlja k o n stru k ciju p o m o u inicijalizacije instance (an on im n e u n u tra n je klase ne m o g u da im aju k o n stru k to re ).

274

Misliti na Javi

Prvi p rim e r prikazu je pravljenje cele klase ija je oblast vaenja je d n a m eto d a (um esto da je oblast vaenja d ru g a klasa). To je lokalna unutranja klasa:
//: unutrasnjeklase/Posiljka5.java // Ugneivanje klase unutar metode. public class Posiljka5 { public Odrediste odr(String s) { class POdrediste implements Odrediste { private String oznaka; private POdrediste(String gde) { oznaka = gde;

}
public String procitajOznaku() { return oznaka; }

}
return new POdrediste(s);

}
public static void main(String[] args) { Posiljka5 p = new Posiljka5(); Odrediste d = p.odr("Tanzanija");

} } ///= Klasa POdrediste je deo m eto d e odr(), a ne deo klase Posiljka5. Z bog toga klasi POdrediste ne m o ete da p ristu p ite izvan m eto d e odr(). O b ra tite p a n ju na svoenje navie u n ared b i return - m eto d a odr() vraa sam o referencu na o sn o v n u klasu, klasu Odrediste. N aravno, to to je im e klase POdrediste navedeno u n u ta r m eto d e odr() ne znai da o bjekti klase POdrediste n akon p o v ratk a iz te m eto d e nee biti ispravni. O b ratite p anju na to da ste za im ena u n u tran jih klasa u svim klasam a u istom poddirek to riju m u m ogli da koristite identifik ato r POdrediste, a da ne o e d o sukoba im ena. N ared n i p rim e r p o kazuje kako u n u tra n ju klasu m oete da u g nezdite u n u ta r proizvoljne oblasti vaenja:
//: unutrasnjeklase/Posi1jka6.java // Ugneivanje klase unutar oblasti vaenja public class Posiljka6 { private void unutrasnjePracenje(boolean b) { if(b) { class KarticaZaPracenje { private String id; KarticaZaPracenje(String s) {

id = s; }
String uzmiKarticu() { return id; }

}
KarticaZaPracenje ts = new KarticaZaPracenje("slip"); String s = ts .uzmiKarticu();

Poglavfje 10: Unutranje klase

275

// Ovde ne moete da je koristite!

Izvan je oblasti vaenja:

//! KarticaZaPracenje ts = new KarticaZaPracenje("x");

}
public void p r a t i () ( un utrasnjePracenje(true); } public static void main(String[] args) Posiljka6 p = new PosiljkaG(); p.prati (); {

} } /// = Klasa KarticaZaPracenje je u g n e en a u n u ta r oblasti vaenja n a re d b e if. To n e znai da se klasa uslovno p rav i - o n a e b iti prevedena zajedno sa svim o stalim . O n a , m e u tim , nije d o stu p n a izvan oblasti vaenja u n u ta r koje je definisana. O sim toga, ista je kao svaka d ru g a o b in a klasa.

Veba 9: (1) N ap rav ite interfejs s najm an je je d n o m m e to d o m i realizujte ga definiui


u n u tra n ju klasu u n u ta r neke m eto d e koja vraa referencu n a va interfejs.

Veba 10: (1) P onovite p re th o d n u vebu, ali definiite u n u tra n ju klasu u nekoj oblasti
vaenja u o k v iru m etode.

Veba 11: (2) N ap rav ite p riv atn u u n u tra n ju klasu koja realizuje jav n i interfejs. N apiite
m e to d u koja vraa referencu n a in stan cu p riv atn e u n u tra n je klase, svedenu navie ka interfejsu. Pokaite da je u n u tra n ja klasa p o tp u n o sakrivena tako to ete p o k u a ti d a svedete nan ie ka njoj.

Anonimne unutranje klase


N are d n i p rim e r izgleda p o m alo u d n o :
/ / : unutrasnjeklase/Posiljka7.java // Metoda koja vraa anonimnu unutranju klasu. public class Posiljka7 { public Sadrzina sadr() return new Sadrzina() private int i = 11; public int vrednost() { return i; } }; // U ovom sluaju je potrebna taka i zarez. { { // Umetanje definicije klase

}
public static void main(String[] args) Posiljka7 p = new Posiljka7(); Sadrzina c = p . sa dr (); {

} ///:-

M etoda sad r() k o m b in u je generisanje p o v ratn e v red n o sti i d e fin i ju klase koja p red stavlja tu p o v ra tn u vrednost! Pored toga, klasa je anotiim na n em a im e. D a stvari b u d u jo gore, izgleda kao da poin jete da pravite n o v objek at klase Sadrzina, ali tad a, p re nego to d o d e te do take i zareza, kaete: Sam o tren u ta k , sada u da u b acim definiciju klase.

276

Misliti na Javi

O va u d n a sintaksa znai: N ap rav i o b jek at a n o n im n e klase koja nasleuje ldasu Sadrzina R eferenca koju vraa o p e ra to r new a u to m a tsk i se svodi navie na referen cu klase Sadrzina. Sintaksa za a n o n im n u u n u tra n ju klasu je sk raeni oblik za:
//: unutrasnjeklase/Posiljka7b.java // Proirena verzija programa Posiljka7.java public class Posiljka7b { class MojaSadrzina implements Sadrzina { private int i = 11; public int vrednost() { return i; } { return new Mo ja Sa dr zi na (); } {

}
public Sadrzina sadrzina() public static void main(String[] args) Posi1jka7b p = new Po si lj ka 7b (); Sadrzina c = p.sadrzina();

} } ///= U a n o n im n o j u n u tran jo j klasi, klasa Sadrzina je napravljen a p o m o u p o d razu m ev an o g k o n stru k to ra. N aredni p ro g ra m pokazuje ta da rad ite ako vaa osn o v n a klasa zahteva k o n stru k ciju sa arg u m en to m :
//: unutrasnjeklase/Posi1jka8.java // Anonimna unutranja klasa koja poziva konstruktor osnovne klase. public class Po si1j ka8 { public Omotac omot(int x) { // Poziv konstruktoru osnovne klase: return new Omotac(x) { // Prosleivanje argumenta konstruktoru. { public int vrednost()

return super.vrednost() * 47;

}
}; // Potrebna je taka i zarez

}
public static void main(String[] Posiljka8 p = new Posiljka8(); Omotac w = p . om ot (l O); args) {

} } ///:O dg o varaju i a rg u m en t sam o p ro sled ite k o n stru k to ru osno v ne klase, kao to je u gornjem p rim e ru p a ra m e ta r x pro sle en n a re d b o m n ew O m o ta c (x ). Iako je O m o ta c obina klasa s realizacijom , o n a se u p o treb ljav a i kao zajedniki interfejs za svoje izvedene klase:
//: unutrasnjeklase/Omotac.java public class Omotac { private int i;

Poglavlje 10: Unutranje klase

277

public 0motac(int x) { i = x; public int vrednost{)

{ return i; }

} ///:P rim etili ste kako O m o ta c im a k o n stru k to r koji zahteva a rg u m en t, da bi bilo m alo zanim ljivije. Z n ak taka i zarez n a k raju a n o n im n e u n u tra n je klase n e oznaava kraj tela klase. O n oznaava kraj izraza koji u d ato m sluaju sadri i a n o n im n u klasu. D akle, u p o tre b a take i zareza p o tp u n o je ista kao i bilo gde d ru g d e u p ro g ram u . Inicijalizaciju o b jek ta a n o n im n e klase m o ete d a obavite n a m e stu n a k o m e definiete polja:
//: unutrasnjeklase/Posi1jka9.java // Anonimna unutranja klasa koja obavlja inicijalizaciju. // Kraa verzija datoteke Posiljka5.java. public class Posiljka9 { // Argument koji koristite u anonimnoj unutranjoj // klasi mora da bude finalan: public Odrediste odr(final String odr) { return new Odrediste() { { return oznaka; } private String oznaka = odr; public String procitajOznaku()

}; }
public static void main(String[] args) Posiljka9 p = new Posiljka9(); Odrediste d = p.odr("Tanzanij a " ) ; {

III--

Ako definiete a n o n im n u u n u tra n ju klasu u kojoj elite da ko ristite objekat definisan izvan nje, prevodilac zahteva da taj objekat b u d e finalan. Z bog toga je a rg u m e n t m etode o d r() finalan. U koliko to zaboravite, d obiete greku p rilikom prevoenja. D okle god sam o dodeljujete vrednosti poljim a, gornji p ristu p je u redu. Ali ta ako treba da obavite neku aktivnost slinu k o n struktoru? Poto je klasa an o n im n a, k o n stru k to r u njoj ne m oe da im a im e (jer o n o ne postoji!). Na sledei nain m oete efektivno da napravite k o n stru k to r za a n o n im n u u n u tran ju klasu p o m o u bloka za itiicijalizaciju instancv.
I I : unutrasnjeklase/KonstruktorAnonimne.java
// Pravljenje konstruktora za anonimnu unutranju klasu. import static n e t .mindview.uti 1 .P r in t.*; abstract class Osnovna { public Osnovna(int i) { print("Konstruktor klase Osnovna, i = " + i);

}
public abstract void f ();

278

Misliti na Javi

pufalic class KonstruktorAnonimne { public static Osnovna uzmiOsnovnu(int i) { return new Osnovna(i) publ ic void f () { print("U anonimnoj f()"); { { print("Unutar inicij a l izatora instance"); }

} }; }
public static void main(String[] args) Osnovna osnovna = uz mi Os no vn u( 47 ); o s n o vn a. f(); {

}
} /* Ispis: Konstruktor klase Osnovna, i = 47 Unutar inicijalizatora instance U anonimnoj f()

*///U ovom sluaju, p ro m en ljiv a i nije m o rala d a b u d e finalna. lako se i p rosleuje osnovn o m k o n stru k to ru a n o n im n e klase, n ik a d a se ne upotrebljav a n ep o sre d n o u n u ta r te an o n im n e Idase. Evo kako izgleda naa Poiljka sa inicijalizacijom instance. Im ajte u v id u da arg u m en ti m eto d e o d r() m o ra ju b iti finalni, p o to se u p o treb ljav aju u n u ta r a n o n im n e klase.
//: unutrasnjeklase/PosiljkalO.java // Upotreba "inicijalizacije instanci" za konstrukciju // anonimne unutranje klase. public class PosiljkalO { public Odrediste odr(final String odr, final float cena) return new Odrediste() private int tr o s k o v i ; // Inicijalizacija instanci za svaki objekat: { {

{
troskovi = M a t h .r ou nd (c en a); if(troskovi > 100) System.out.println("Prekoraen b u d e t !");

}
private String oznaka = odr; public String p r o c it aj Oz na ku() { return oznaka; }

}; }
public static void main(String[] args) PosiljkalO p = new Posiljkal0(); Odrediste d = p. od r( "T an za ni ja ", 101.395F); {

}
} /* Ispis: Prekoraen budet!

* ///:-

Poglavjje 10: Unutranje klase

27 9

U n u ta r b loka za inicijalizaciju in s ta n uoavate kod koji ne m oe da bu d e u p o treb ljen za inicijalizaciju polja (preciznije, n are d b u if). Stoga blo k za inicijalizaciju in stan ci efektiv n o predstavlja k o n stru k to r a n o n im n e u n u tra n je klase. O vakav p ristu p , naravno, im a o gran ien ja; b lo k za inicijalizaciju in stan ci ne m oete da preklopite pa ete im ati sam o jed an takav k o n stru k to r. A n o n im n e u n u tra n je klase su p o neto o g ranienije nego o bino nasleivanje, p oto o ne m o g u da p ro ire klasu ili d a realizuju interfejs, ali ne oboje. A ako realizujete interfejs, m oete da realizujete sam o jed an .

Veba 12: (1) P onovite vebu 7 k oristei a n o n im n u u n u tra n ju klasu. Veba 13: (1) P onovite vebu 9 koristei a n o n im n u u n u tra n ju klasu. Veba 14: (1) P repravite p ro g ram interfejsi/HororPredstava.java tako da interfejse OpasnoCuoviste i Vampir realizujete p o m o u a n o n im n ih klasa. Veba 15: (2) N apravite klasu koja im a n ep o d razum evan i konstruktor, ali ne i p od razum evani. N apravite d ru g u klasu koja im a m e to d u koja vraa referencu na prv u klasu. N apravite objekat koji ete v ratiti koristei an o n im n u u n u tra n ju klasu koja nasleuje p rv u klasu.

Ponovo o proizvodnim metodama


P ogledajte koliko lepe izgleda p rim e r interfejsi/Proizvodjaci.java kada se u p o tre b e a n o n im n e u n u tra n je klase:
/ / : unutrasnjeklase/Proizvodjaci.java import static net.mindview.uti1 .Prin t .*; interface Usluga { void m e t o d a l (); void m e t o d a 2 ( ) ;

}
interface ProizvodjacUsluga { Usluga uzmiUslugu ();

}
class Realizacijal implements Usluga { private Realizacijal() public void metodal() public void metoda2() {} {print("Realizacijal metodal");} {print("Realizacijal me to da 2);} { {

public static ProizvodjacUsluga proizvodjac = new ProizvodjacUsluga() public Usluga uzmiUslugu () return new Re al iz ac ij al ();

280

Misliti na Javi

class Realizacija2 implements Usluga { private Realizacija2() public void metodal() public void metoda2() {) {print("Realizacija2 metodal");} {print("Realizacija2 metoda2");} { {

public static ProizvodjacUsluga proizvodjac = new Pr oi zv od j a c U s l u g a O public Usluga uzmiUslugu () return new Realizacij a 2 ();

} }; }
public class Proizvodjaci {

public static void korisnikUsluge(ProizvodjacUsluga fabr) { Usluga s = fa br .u zmiUslugu(); s.me to da l( ); s. me to da 2( );

}
public static void main(String[] args) { korisnikUsluge(new Realizacijal.proizvodjac ()); // Realizacije su meusobno potpuno zamenljive: korisnikUsluge(new Realizacija2.proizvodjac ());

}
} /* Ispis: Realizacijal metodal Realizacijal metoda2 Realizacija2 metodal Realizacija2 metoda2

* ///:Sada k o n stru k to ri za klase Realizacijal i Realizacija2 m o g u b iti p riv a tn i, i n em a p o treb e da se im e n o v an a klasa p rav i kao Proizvoda. Pored toga, esto je p o tre b a n sam o jed a n p ro izv o d n i objekat, pa je ovde n ap rav ljen kao statin o polje u realizaciji interfejsa Usluga. D obija se i sm islenija sintaksa. I p rim e r interfejsi/Igre.java m oe biti poboljan p o m o u a n o n im n ih u n u tra n jih klasa:
//: unutrasnjeklase/Igre.java // Kostur za Igre napravljen pomou unutranjih klasa. import static net.mi nd vi ew .u ti l.Print.*; interface Igra { boolean potez(); }

interface Proizvodjaclgara { Igra uzmilgru(); } class Dama implements Igra { private Dama() {} private int potezi = 0; private static final int POTEZI = 3; public boolean potez() {

Pc>t.jHVli'

l(

n u tra n je klase

281

print("Dama potez " + potez); return ++potezi != POTEZI; ; < I

}
public static Proizvodjaclgara proizvodjac public Igra uzmilgru() { return new Danu: (

n d jacIg araO {

}; }
class Sah implements Igra { private Sah() {} int POTEZI = 4; { private int potezi = 0; private static final public boolean potez() return ++potezi

print("Sah potez " + potezi); != POTEZI; /(djacIgaraO {

1
public static Proizvodjaclgara proizvodjac = nt'V I public Igra uzmilgru() { return new Sah(); }

}; }
public class Igre { public static void igrajIgru(ProizvodjacIgai ; Igra s = proizv od ja c. uz mi lg ruO ; while(s.potez())

:) {

public static void ma in (String[] args) igrajlgru(Dama.proizvodjac); igrajIgru(Sah.proi zvodjac);

}
} /* Ispis: Dama potez 0 Dama potez 1 Dama potez 2 ah potez 0 ah potez 1 ah potez 2 ah potez 3

* ///:N e zab orav ite savet s k raja p re tb o d n o g poglnv! U k oliko projekat zahteva interfejs, to e v am biti m o rate. V eba 16: (1) P repravite reenje vebe 18 iz po^lavlj; tra n ju klasu. V eba 17: (1) P repravite reenje vebe tra n ju klasu.
i 'j

pr

r"ite klase, a ne interfejse. i.jte interfejse ako ba ne ristei a n o n im n u u n u -

iz poj-ia

;oristei a n o n im n u u n u -

282

Misliti na Javi

Ugneene klase
A ko vam n e treb a veza izm eu n jen ih o b jek ata i o b jek ata sp oljn e klase, u n u tra n ju klasu m oete da proglasite statin o m . O n a se o b in o naziva ugneena klasa2. D a biste shvatili znaenje kvalifikatora sta tic kada se p rim e n i na u n u tra n je klase, setite se da objekat obine u n u tra n je klase im p licitn o uva referencu n a o b jek at spoljn e klase koja ga je napravila. Kada kaete da je u n u tra n ja klasa statin a, ovo ne vai. U gneena klasa znai da: 1. Za pravljenje o b jek ta u g n e en e klase nije p o tre b a n o b jek at spo ljne klase. 2 . Iz objekta u g n e en e klase ne m o ete d a p ristu p ite o b jek tu sp o ljn e klase. U gneene klase se jo p o n e em u razlik u ju o d n e stati n ih , tj. o b i n ih u n u tra n jih klasa. Polja i m e to d e nestatin e u n u tra n je klase m o g u d a b u d u sam o n a sp o ljn o m n iv ou klase, o d n o sn o n estatin a u n u tra n ja klasa ne m o e d a im a stati n e p o d atk e , statina polja niti statin e u n u tra n je klase. M e u tim , u g n e e n e (statin e u n u tra n je ) klase m o g u sve to da sadre:
//: unutrasnjeklase/Posiljkall.java // Ugneene klase (statine unutranje klase) public class Posiljkall { private static class PSadrzina implements Sadrzina { private int i = 11; public int vrednost() { return i; }

}
protected static class POdrediste implements Odrediste { private String oznaka; private POdrediste(String gde) oznaka = gde; {

}
public String procitajOznaku() public static void f() {} static int x = 10; static class DrugiNivo { public static void f() static int x = 10; {} { return oznaka; } // Statina unutranja klasa moe da sadri druge statine elemente:

} I
public static Odrediste odr(String s) { return new POdred is te (s );

}
public static Sadrzina sadr() return new PSadrzina(); {

O tprilike slino u gne enim klasam a u C + + -u , sem to te klase ne m o gu p ristu p a ti priv atn im lan ov im a kao u Javi.

Poglavjje 10: Unutranje klase

283

public static void main(String[] Sadrzina c = s a d r ( ) ; Odrediste d = od r( "T an za ni ja ");

args)

1 ///=U m e to d i main() nije p o tre b a n o b jek at klase P osiljk all; u m esto toga u p otreb ljavate n o rm a ln u sin tak su za p ristu p sta ti n o m lanu d a biste pozvali m eto d e koje v raaju reference na objekte klase Sadrzina i Odrediste. Kao to ste videli u p re th o d n o m delu poglavlja, u o b in o j (n estatin o j) u n u tra n jo j klasi veza ka spoljn o j klasi o stv aru je se p rek o p o se b n e reference th is. S tatin a u n u tra n ja klasa nem a tu p o se b n u referen cu , zbog ega je a n alo g n a sta ti n im m eto d am a.

Veba 18: (1) N ap rav ite k lasu koja sari u g n e en u klasu. U m eto d i m a in ( ) n ap rav ite
in sta n c u te u g n e en e klase.

Veba 19: (2) N ap rav ite klasu koja sari u n u tra n ju klasu koja i sam a sadri u n u tra n ju klasu. N astavite dalje ovako, ko ristei u g n e en e klase. Pogledajte im en a d ato tek a .class
koje prev o dilac pravi.

Klase unutar interfejsa


U n u ta r interfejsa ne m oete d a piete bilo kakav ko d , ali statin a u n u tra n ja klasa m oe da b u d e deo nekog interfejsa. Svaka klasa k o ju stavite u n u ta r interfejsa a u to m atsk i postaje jav n a i statina. Poto je klasa statin a, o n a ne n aru av a p rav ila o interfejsim a - statina u n u tra n ja klasa se sam o u m ee u im en sk i p ro s to r interfejsa. U u n u tra n jo j klasi ak m o ete d a realizujete o k ru u ju i interfejs, i to n a ovaj nain:
//: unutrasnjeklase/KlasaLIInterfejsu.java // {main: K1asaUInterfejsu$Test} public interface K1asaUInterfejsu { void z d r a v o ( ) ; class Test implements K1asaUInterfejsu public void zdravo() { Sy st em .o ut .p ri nt ln ("Z dr av o!"); {

}
public static void main(String[] new Te st () . z d r a v o ( ) ; args) {

} }
} /* Ispis: Zdravo!

* ///:K lasu je u m esn o staviti u interfejs o n d a kad a h o e te da n ap rav ite zajedniki k o d koji e b iti korien sa svim razliitim realizacijam a to g interfejsa.

284

Misliti na Javi

N a p o etk u ove knjige savetovao sam vam da u svaku klasu u b acite m e to d u m a in () za n jen o testiranje. M an a takvog p ristu p a je d o d a tn a koliina p re v ed en o g koda koji m o ra te d a vuete n aokolo. Ako to pred stav lja p ro b lem , za uvanje koda za testiran je k o ristite stati n u u n u tra n ju klasu:
//: unutrasnjeklase/ ZaTestiranje.java // Stavljanje koda za testiranje u statinu unutranju klasu // (main: ZaTestiranje$Tester} public class ZaTestiranje { public void f() { System.out.println("f()"); public static class Tester { public static void main(String[] t.f 0 ; args) { ZaTestiranje t = new Z a Te st ir an je (); }

} }
} /* Ispis: f()

* ///:O v im p ra v ite zase b n u klasu p o d im en o m ZaTestiranje$Tester (da b iste p o k re n u li p ro g ra m , n apiite java ZaTestiranje$Tester, ali u U n ix u /L in u x u p re tv o rite $ u izlaznu sekvencu). O vu klasu m o ete d a k o ristite za testiran je, ali ne m o ra te d a je u k lju ite u gotov pro izvod ; p re nego to sve u p ak u jete, sam o u k lo n ite ZaTestiranje$Tester.class.

Veba20: (1) N ap rav ite interfejs koji sadri u g n e en u klasu. R ealizujte interfejs i n a p ra vite in sta n c u te u g n e d en e klase.

Veba 21: (2) N ap rav ite interfejs koji sadri u n u tra n ju klasu sa sta ti n o m m e to d o m to
poziva m e to d e to g interfejsa i p rik azu je rezu ltate. R ealizujte interfejs i m e to d i p ro sled ite in sta n c u te realizacije.

Pristupanje spoljanjosti iz viestruko ugneenih klasa


Bez o b zira n a to koliko d u b o k o je u n u tra n ja klasa u g n e en a, o n a m oe tra n sp a re n tn o da p ristu p i svim lan o v im a svih klasa u k o jim a je u g n e en a, kao to se vidi iz n a re d n o g p rim e ra :3
//: unutrasnjeklase/PristupPriVisestrukomllgnezdjivanju.java // Ugneene klase mogu da pristupe svim lanovima // na svim nivoima klasa u koje su ugneene. class PVU { private void f() class A { private void g() {} {}

Jo je d n o m z a h v a lju je m M a r tin u D a n e ru .

Poglavlje 10: Unutranje klase

285

public class B { void h() {

g();
f0;

} } } }
public class PristupPriVisestrukomUgnezdjivanju { public static void m a i n (String[] args) PVU pvu = new P V U () ; PVU.A pvua = pvu.new A(); PVU.A.B pvuab = pvua.new B(); pvuab.h(); {

} } ///= Iz p rim e ra vid ite da u klasi PV U.A .B m e to d e g() i m o g u d a b u d u po zv an e b ez ikakvih kvalifikatora (i p o re d injenice d a su p riv atn e). O vaj p rim e r tak o d e p o k azu je sin ta k su koja je p o tre b n a za g ra en je o bjekata v iestru k o u g n e en ih u n u tra n jih klasa p ri p ra v ljen ju o b jek ata razliitih klasa. S intaksa .n e w daje o d g o v araju u o b last vaenja, p a p ri p ozivu k o n stru k to ra ne m o ra te da n azn aite im e klase.

f()

Zato unutranje klase?


D o sad je bilo p u n o sintakse i sem an tik e koje o p isu ju nain na koji u n u tra n je klase rade, ali to i dalje nije o d g o v o r n a p itanje: zbog ega o n e postoje? Z bog ega su se p ro je k ta n ti Jave toliko p o m u ili d a jeziku d o d a ju ovu su tin sk u m ogunost? U n u tra n ja klasa o b i n o n asle u je klasu ili realizuje interfejs i ra d i sa o b je k to m sp o ljn e klase u n u ta r koje je nap rav ljen a. M oglo bi se rei d a je u n u tra n ja klasa n alik p ro z o ru u sp o ljan ju klasu. P itanje koje zadire u sr u n u tra n jih klasa glasi: ako m i je p o tre b n a sam o referenca na interfejs, zato n e n a p rav im spoljanju klasu tako da realizuje taj interfejs? O d g o v o r je: ,A ko vam je sam o to p o treb n o , o n d a tako i u ra d ite . Po e m u se, o n d a , u n u tra n ja klasa koja realizuje interfejs razlikuje o d spoljne klase koja realizuje taj isti interfejs? O d g o v o r glasi: ne m o ete uvek da k o ristite p o g o d n o sti interfejsa - p o n ek ad m o ra te da rad ite s realizacijam a. D akle, najznaajniji razlog za p o sto jan je u n u tra n jih klasa m o e d a se fo rm u lie na sledei nain: Svaka u n utranja klasa tnoe nezavisno od drugih da nasledi realizaciju. Stoga, u n u tra nja kiasa nije ograniena tim e to je spoljna klasa ve nasledila neku realizaciju. Bez m o g u n o sti koje p ru a ju u n u tra n je klase - da se isto v rem en o n asledi vie o d jed ne k o n k re tn e ili a p s tr a k tn e klase - neki p ro b le m i p ri p ro jek to v a n ju i p ro g ra m ira n ju bili bi nereivi. led an nain za p o sm a tra n je u n u tra n jih klasa je u p o tp u n ja v a n je reen ja za p ro b le m v iestru k o g nasleivanja. D eo p ro b lem a reavaju interfejsi, d o k u n u tra n je klase efektivno d ozvoljavaju viestruko nasleivanje realizacije. U n u tra n je klase, dakle, efektiv n o o m o g u av a ju da nasledite vie klasa koje n isu interfejsi.

286

Misliti na Javi

D a b ism o ovo d etaljnije p ro u ili, ra z m o trim o situ aciju u kojoj im a m o dva interfejsa koja nekako m o ra m o d a realizu jem o u jed n o j klasi. Z b o g fleksibilnosti interfejsa m o ete da b irate je d n u o d dve m o g u n o sti: je d n a klasa ili u n u tra n je klase:
//: un ut ra sn je kl as e/ Vi ses tr uk iI nt er fe js i.java // Ova naina na koje klasa mo e da realizuje vie interfejsa. interface A {} interface B {} class X implements A, B {} class Y implements A { B napraviB() { // Anonimna unutranja klasa: return new B() {};

} }
public class Visestrukilnterfejsi static void uzimaA(A a) {} static void uzimaB(B b) {} public static void main(String[] args) X x = new X (); Y y = new Y ( ) ; u z i m aA (x ); uz i m a A ( y ) ; uz i m a B ( x ) ; uz im aB (y .n ap ra vi B( )); { {

} } lll--~ P ri to m e se, n arav no , p o d ra z u m e v a d a je s tru k tu ra vaeg p ro g ra m a takva da o b a nain a b u d u logina. O b i n o sam a p riro d a p ro b le m a u p u u je na p rav u o d lu k u : da li da u p o tre b ite je d n u klasu ili u n u tra n je klase. U g o rn je m p rim e ru , s take gledita realizacije, goto v o d a i n e m a nikakve razlike iz m e u ta dva p ristu p a . I jed a n i d ru g i e raditi. M e u tim , ako u m esto interfejsa im ate a p stra k tn e ili k o n k re tn e klase i ako vaa klasa m o ra nek ak o da realizuje i je d n e i d ru g e, m o ra te u p o tre b iti u n u tra n je klase:
//: unutrasnjeklase/VisestrukaRealizacija.java // Ako su u pitanju konkretne ili apstraktne klase unutranje klase su // jedini nain za dobijanje efekta "viestrukog nasleivanja realizacij e " . package unutrasnjeklase; class D {} abstract class E {} class Z extends 0 { E napraviE() { return new E() {}; }

Poglavlje 10: Unutranje klase

287

public class VisestrukaRealizacija { static void uzimaD(D d) {} static void uzimaE(E e) {} public static void main(String[] Z z = new Z ( ) ; uz i m a D ( z ) ; uzimaE(z.napraviE()); args) {

} } ///:A ko se n iste susreli s p ro b k m o m viestrukog n asleivanja realizacije, sve ostalo ste sasvim lo g in o m ogli da p ro g ra m ira te i b ez u p o tre b e u n u tra n jih klasa. U koliko ko ristite u n u tra n je klase, d o b ijate sledee d o d a tn e m oguno sti: 1. U je d n o m o b jek tu spoljne klase m oete d a im ate vie in stan ci u n u tra n je klase, od kojih svaka uva svoje inform acije koje zavise od in fo rm ac ija u o b jek tu spo ljn e klase. 2 . U je d n o j spoljnoj klasi m o ete d a im ate vie u n u tra n jih klasa, o d ko jih svaka realizuje isti interfejs ili nasleuje istu klasu na razliit nain . U b rzo sledi p rim er. 3 . T ren u ta k p rav ljen ja o b jek ta u n u tra n je klase nije vezan za pravljenje o b jek ta spoljanje klase. 4 . P ri ra d u sa u n u tra n jo m klaso m n em a p o ten cijaln o zb u n ju ju e relacije ,,je; u n u tra n ja klasa je zaseban en titet. P rim e ra radi, da u p ro g ra m u Sekvenca.java nism o ko ristili u n u tra n je klase, m o rali biste d a kaete Sekvenca je Selektor i za o re e n u sekvencu bi m o g ao d a p o sto ji sam o je d a n Selektor. M ogli biste, takode, da im ate jo je d n u m e to d u , obrnutiSelektor( ); o n a bi n a p ra v ila Selektor koji se kree u n a zad kroz sekvencu. O va v rsta fleksibilnosti je m o gua sa m o p ri u p o tre b i u n u tra n jih klasa.

Veba 22: (2) U d a to teci Sekvenca.java realizujte m e to d u obrnutiSelektor( ).


V eba 23: (4) N apravite interfejs U koji im a tri m etode. N apravite klasu A s m e to d o m koja v raa referencu na U tako to prav i a n o n im n u u n u tra n ju klasu. N ap rav ite d ru g u klasu, B, u kojoj se nalazi niz iji su elem en ti reference na objekte klase U. Klasa B bi treb alo d a im a je d n u m e to d u koja p rih v ata i sm eta u niz reference na U, d ru g u m e to d u koja postavlja na v re d n o st null o d re e n u referencu u n izu (o d re en u a rg u m e n to m m eto d e) i treu m e to d u koja se kree kroz niz i poziva m eto d e iz U. U m eto d i m a in ( ) n ap rav ite g ru p u o bjekata klase A i jed a n objek at klase B. P o p u n ite B referencam a n a U koje su n ap rav ili o bjekti klase A. U p o tre b ite B za p o v ra tn e pozive u sve objekte klase A. Izbacite neke reference n a U iz objekta B.

Zakljuci i povratni pozivi


Z n klju a k (engl. closure) jeste o b jek at k om e m o ete da p ristu p a te i koji uva in fo rm acije iz ob lasti vaenja u kojoj je b io nap rav ljen . Iz ove definicije v id ite da je u n u tra n ja klasa o b je k tn o o rijen tisa n i zakljuak, p o to o n a sadri delove in fo rm a c ija iz o b jek ta spoljne klase (oblast vaenja u kojoj je bio n ap rav ljen ) i au to m a tsk i uva i referencu na ceo o b jek at sp o ljn e klase u k om e im a dozvolu da radi sa svim lan o v im a, ak i p riv atn im .

288

Misliti na Javi

Povratni p o ziv i (engl. callbacks) bili su je d a n o d n ajo zb iljn ijih a rg u m e n a ta u p rilog u k ljuivanja neke vrste m e h a n izm a pokaziv aa u Javi. P o m o u p o v ra tn ih poziva, nekom se o b jek tu daje in fo rm acija koja m u o m o g u av a d a n eto kasn ije pozove n azad objekat koji m u je u p u tio p o v ra tn i poziv. Kao to ete v ideti u n astav k u knjige, to je veo m a m o an kon cep t. Ako se p o v ra tn i p ozivi realizu ju p o m o u p okazivaa, m o ra te se o slo n iti na to da e se p ro g ra m e r lepo p o n aati i d a nee zlo u p o tre b iti pokaziva. K ao to ste d o sada videli, u Javi se tei ka jo veoj o p rezn o sti, p a pokazivai n isu u ld ju en i u jezik. Z akljuak koji o bezb e u je u n u tra n ja klasa sav reno je reenje za p o v ra tn i poziv; m n o g o fleksibilnije i daleko sig u rn ije nego pokaziva. Evo je d n o sta v n o g p rim e ra :
//: un utrasnjeklase/PovratniPozivi.java // Upotreba unutranjih klasa za povratne pozive package unutrasnjeklase; import static n e t . mi nd vi ew .u ti l.Print.*; interface MozeDaSePoveca { void povecaj();

}
// Veoma jednostavna, samo realizuje interfejs: class Pozvanal implements Mo zeDaSePoveca { private int. i = 0; public void povecaj() {

i++;
pr i n t ( i ) ;

} }
class MojaPovecaj { { print("Druga operacija"); { m p . p o v e c a j (); } }

public void povecaj()

static void f(MojaPovecaj mp)

}
// Ako vaa klasa mora da realizuje metodu p o v e c a j () // na neki drugi nain, morate da upotrebite unutranju klasu: class Pozvana2 extends MojaPovecaj private int i = 0; private void povecaj() super.povecajO; { {

i++;
print(i);

}
private class Zakljucak implements MozeDaSePoveca { p u b l ic void povecaj () { // Specificirajte metodu iz spoljne klase, // beskonanu rekurziju: Pozv an a2 .t hi s. po ve caj(); inae ete dobiti

Poglavlje 10: Unutranje klase

289

MozeDaSePoveca uzmiReferencuZaPovratm'Poziv() return new Z a k l j u c a k O ;

class Poziva { private MozeDaSePoveca referencaZaPovratniPoziv; Poziva(MozeDaSePoveca cbh) void kreni() { referencaZaPovratniPoziv = cbh; } { referencaZaPovratniPoziv.povecaj (); }

}
public class PovratniPozivi { args) {

public static void main(String[] Pozvanal cl = new Pozvanal(); Pozvana2 c2 = new Pozvana2(); M o j a Po ve ca j. f( c2 );

Poziva pozival = new Poziva(cl); Poziva poziva2 = new Poziva(c2.uzmiReferencuZaPovratniPoziv()); po zi va l. kr en i(); po zi va l. kr en i(); po zi va 2. kr en i(); poziva2.kreni ();

}
} /* Ispis: Druga operacija

1 1
2 Druga operacija

2
Druga operacija 3

* ///:Ovaj p rim e r tak oe po k azu je d o d a tn e razlike izm e u realizovanja interfejsa u spoljnoj i u n u tra n jo j klasi. U pogled u pisanja p ro g ra m a , klasa Pozvanal je o igledno jed n o stav nije reenje. Klasa Pozvana2 nasleuje klasu MojaPovecaj u o k v iru koje ve p o sto ji m eto da povecaj( ) iji zadatak n em a nikakve veze sa o n im koji o ek u je m o o d interfejsa Povecaj. Kada klasu Pozvana2 nasled im o iz klase MojaPovecaj, n jen u m e to d u povecaj( ) ne m oem o da redefiniem o tako da m oe a se k oristi s in terfe jso m MozeDaSePoveca, pa m o rate da ob ezbed ite p o seb n u realizaciju p o m o u u n u tra n je ldase. O b ra tite p a n ju na to da p ravljenjem u n u tra n je klase n iti p ro iru jete niti m en jate interfejs spo ljn e klase. U klasi Pozvana2 sve je p riv a tn o osim m e to d e uzm iReferencuZaPovratniPoziv( ). Da biste om o guili bilo kakvu vezu sa sp o ljan jim sv eto m , n e o p h o d a n vam je interfejs MozeDaSePoveca. U ovom p rim e ru v id ite kako v am Javini interfejsi p o m a u da p o tp u n o razdvojite interfejs i realizaciju. U n u tran ja klasa Zakljucak sam o realizuje interfejs MozeDaSePoveca da bi obezbedila p o v ratn u vezu ka klasi Pozvana2 - ali sig u rn u p o v ra tn u vezu. Svako ko d o b ije referencu na

290

Misliti na Javi

interfejs MozeDaSePoveca m oe, narav n o , sam o d a pozove m e to d u p ovecaj( ) i to je sve (za razliku o d pokazivaa koji bi om o g u io p o tp u n u slo b o d u ). K o n stru k to r klase Poziva d o b ija referen cu n a interfejs M ozeDaSePoveca (p re m d a referencu za p o v ra tn i poziv m o ete d a p rih v atite u bilo k o m tre n u tk u ) i p o to m , neto kasnije, u p o treb ljav a tu referencu za p o v ra tn i p oziv u klasu Pozvana. Z naaj p o v ra tn ih poziva lei u njihovoj fleksibilnosti, o n o s n o u m o g u n o sti d a p rilikom izvravanja d in am ik i o d lu ite koje ete funkcije pozvati. V red n o st ove teh n ik e p o stae jasnija u poglavlju Grafika korisnika okruenja, u k o m e se za p rav ljenje grafikog k o risn ik o g o k ru en ja (G U I) svuda ko riste p o v ra tn i pozivi.

Unutranje klase i kosturi upravljanja


Kao k o n k re tn iji p rim e r u p o tre b e u n u tra n jih klasa m o e d a se p rim e n i p o stu p a k koji u nazvati kostur upravljanja (engl. control fram ew ork). K osturprogram a (engl. application fram ew ork) jeste klasa ili sk u p klasa koje su projekto v an e da ree o d re e n i p ro b lem . K ostur p ro g ra m a p rim e n ju ie te tak o to nasled ite jed n u ili vie klasa i redefiniete neke m eto d e. K od koji n ap iete u red efin isan im m e to d a m a p rilagoava o p te reenje koje p ru a k o stu r aplikacije d a bi reio specifian p ro b le m . To je p rim e r p ro je k tn o g ob rasca Tem plate M eth o d (ab lo nska m eto d a ) (videti T h in kin g in Patterns (w ith Java) n a adresi w w w .M indV iew .net). ablonska m eto d a sadri o sn o v n u stru k tu r u a lg o ritm a; d a b i dovrila akciju to g alg o ritm a, o n a poziva je d n u ili vie m e to d a koje se m o g u redefinisati. P ro jek tn i o b razac razdvaja o n o to se m en ja o d o n o g a to se ne m enja; u o v o m sluaju, o n o to se n e m en ja je ablonska m e to d a , a m en ja ju se m eto d e koje se m o g u redefinisati. K ostur u p rav ljan ja je p o seb an tip k o stu ra p ro g ra m a , a o sn o v n a n a m e n a m u je da se o d g o v o ri na d ogaaje; sistem ija je o sn o v n a funkcija d a reaguje n a d o ga aje naziva se sistem kojim upravljaju dogaaji (engl. event-driven system ). Jean o d n ajvanijih p ro b le m a p rilik o m pravljen ja p ro g ra m a je grafiko k o risn iko o k ru en je (G U I), kojim skoro p o tp u n o u prav ljaju dogaaji. Kao to ete v ideti u poglavlju Grafika korisnika okruenja, Javina bib lio tek a Swing je k o stu r u p rav ljanja koji efikasno reava p ro b lem grafikog korisn ik og o k ru e n ja , p ri em u esto ko risti u n u tra n je klase. D a biste razu m eli na koji nain u n u tra n je klase o m o g u av aju je d n o sta v n o pravljenje i u p o tre b u k o stu ra upravljanja, ra z m o trim o k o stu r u p rav ljan ja iji je zad atak da izvri dog aaje svaki p u t kada su ,,sp rem n i. P rem d a te rm in ,,sprem an m oe da o znaava bilo ta, u n aem sluaju on e biti zasnovan na asovniku. N ared n i k o stu r up ravljan ja ne sadri k o n k re tn e in fo rm acije o to m e im e upravlja. Te in fo rm acije se daju to k o m nasleivanja, p rilik o m realizacije o n o g dela alg o ritm a koji je n azvan a k cija(). Prvo em o definisati interfejs koji opisuje bilo kakav upravljaki dogaaj. O n je napisan u oblik u a p stra k tn e klase,a ne kao pravi interfejs, je r je njegovo p o d razu m ev an o ponaanje da upravlja oslanjajui se na vrem e, tako da u n jem u m o ra d a b u d e deo realizacije:
//: unutrasnjeklas e/ up rav lj ac :D og ad ja j.java // Zajednike metode za bilo koji upravljaki dogaaj. package unutrasnjeklase.upravljac;

Poglavlje 10: Unutranje klase

291

public abstract class Dogadjaj private long dogVreme;

protected final long vremeKasnjenja; public D o ga dj aj(1ong vremeKasnjenja) st a r t ( ) ; { this.vremeKasnjenja = vremeKasnjenja;

}
public void start() { // Omoguava ponovno pokretanje dogVreme = System.nanoTime() + vremeKasnjenja;

}
public boolean spreman( ) { return System.nanoTime() >= dogVreme;

}
public abstract void akcija();

III--

K o n stru k to r sam o zapisuje tre n u ta k (m e re n o o d tre n u tk a n a sta n k a o b jek ta) k ad a elite da b u d e p o k re n u t Dogadjaj, i zatim poziva m e to d u sta r t( ) koja tek u e m v re m e n u d o d aje v rem e kanjenja i tako se d obija v rem e k ad a e se dog a aj o d ig rati. U m esto da b u d e u k lju en a u k o n stru k to r, sta r t( ) je zasebna m eto d a. Stoga m era v rem e n a m oete p o n o v o d a p o k ren ete n ak o n o d ig rav an ja d o g a aja , p a se o b jek at Dogadjaj in o e p o n o v o u p o tre b iti. N a prim er, da biste dobili dogaaj koji se p onavlja, d o v o ljn o je da iz m e to d e ak cija( ) pozovete sta rt( ). M eto d a sp rem an ( ) govori da li je dolo v rem e da se p o k re n e akcija( ). M eto d a spreman( ), n arav n o , m oe da b u d e redefinisana u izvedenoj klasi, kako bi izvravanje d ogaaja bilo zasnov ano na neem d ru g o m . N aredna d ato tek a sadri stvarni k o stu r u p ra v lja n ja koji u p rav lja d o g a ajim a i poziva ih. O b jek ti klase Dogadjaj uvaju se u k o n te jn e rsk o m o b je k tu tip a List<Dogadjaj> (ita se lista dogad aja"), o kojoj ete vie sazn ati u poglavlju uvanje objekata. Z a sada, dovoljno je zn ati da m eto d a a d d ( ) d o d aje Dogadjaje na kraj Liste, s iz e ( ) d a je b ro j stavki u Listi, foreach sintaksa pribavlja sukcesivne Dogadjaje iz Liste, a rem ove( ) u k lan ja d ati Dogajaj iz Liste.
//: unutrasnjeklase/upravljac/Upravljac.java // Generiki kostur za sve upravljake sisteme. package unutrasnjeklase.upravljac; import j a va .u ti1 .* ; public class Upravljac { // Klasa iz paketa java.util za uvanje objekata klase Dogadjaj: private List<Dogadjaj> 1 istaDogadjaja = new A r r a y L i s t< Do ga dj aj >(); public void dodajDogadjaj(Dogadjaj c) { 1 istaDo ga dj aj a. ad d( c); } publi c voi d p o k r e n i () { while(listaDogadjaja.size() > 0) // Napravite kopiju da ne biste modifikovali // dok birate elemente iz nje: for(Dogadjaj d : new Arra yL is t< Do ga dj aj >(lis t aD og ad ja ja )) listu

292

Misliti na Javi

if(d.spreman()) d.akcija();

S y s t e m . o ut .p ri nt ln (d); 1 i s t a D o g ad ja ja .r em ov e(d );

} }

} III-M eto d a pokreni( ) k ru i k ro z k o p iju listeDogadjaja, traei Dogadjaj koji je spreman( ) za p o k re tan je . Z a svaki Dogadjaj koji je spreman( ), isp isuju se p o d aci objektov om m e to d o m toStrin g( ) i poziv a m eto d a akcija( ). Z atim se Dogajaj uklanja iziiste. U viate d a za sad a ne zn ate n ita o to m e ta tano klasa Dogadjaj radi. To i jeste sr ovog p o stu p k a; k ako razdvaja o n o to se m en ja o d o n o g to o staje isto . O d n o sn o , da isk o ristim svoj te rm in , vektor p ro m e n e p red stav lja razliite akcije razliitih v rsta objekata klase Dogadjaj koje definiete pravei razliite p o tk lase za dogadaje. N a o v o m m e s tu n a scen u stu p a ju u n u tra n je klase. O n e o m o g u a v a ju dve stvari: 1. C elu realizaciju p ro g ram a koji upotreb ljav a k o stu r u pravljanja m oete da sm estite u je d n u klasu, im e kap su lirate sve to je za tu realizaciju p o treb n o . U n u tran je klase se up o treb ljav aju d a iskau vie razliitih akcija koje su p o tre b n e za reavanje problem a. 2. U n u tra n je klase p o je d n o sta v lju ju o v u realizaciju, a vi ste u m o g u n o sti d a lako p ristu p ite la n o v im a sp o ljn e klase. Bez ov ih m o g u n o sti, p ro g ra m bi m ogao da p o sta n e to lik o k o m p lik o v an a a biste m o rali d a p o tra ite altern ativ u . R a z m o trim o je d n u realizaciju k o stu ra u p rav lja n ja k oja u p rav lja fu n k cijam a staklene bate.4 Svaka akcija je p o tp u n o razliita: paljen je svetla, uklju iv an je vode i term o stata, ukljuivanje zv o nca i p o n o v n o p o k re ta n je sistem a. K o stur u p rav ljan ja o m o g u u je d a se taj razliiti k od lako razdvaja. U n u tra n je klase o m o g u av aju da p o m o u sam o jed n e klase n a p ra v ite vie izvedenih verzija o sn o v n e klase D o g a d ja j. Za svaki tip akcije napraviete nov u u n u tra n ju klasu izvedenu iz klase D o g ad ja j i k o d za u p rav ljan je n apisati u n u ta r m e to d e ak cija( ). Kao to je za k o stu r u p rav ljan ja u o b iajen o , klasa U p ra v Ija n je S ta k le n o m B a sto m je izvedena iz Idase U p rav ljac:
//: u n u t ra sn je klas e/Upravlja njeStaklenomBastom.java // Ov im se pravi poseban program za upravljaki sistem koji se ceo nalazi // u jednoj klasi. Unutranje klase omoguavaju da kapsulirate razliitu // funkcionalnost za svaki poseban tip dogadjaja. import un utrasnjeklase.upravljac.*; public class Upra vljanjeS ta kl en om Ba st om extends Upravljac { private boolean svetlo = false; private class UkljuciSvetlo extends Dogadjaj public Uk lj uciSvetlo(long vremeKasnjenja) public void akcija() { { { s u p e r( vr em eK as nj en ja); }

lz n e k o g ra z lo g a , o v aj p ro b le m sa m u v e k v o leo a re a v a m ; o n p o tie e iz m o je ra n ije kn jig e C + + Insiilc

& O u t , m e u tim , Java o m o g u a v a m n o g o b o lje re en je .

Poglavlje 10: Unutranje klase

293

// Ovde treba da bude kod za upravljanje hardverom // kojim se fiziki svetlo = true; pali svetlo.

}
public String t o S t r i n g O { return "Svetlo je ukljueno"; { { super(vremeKasnjenja); } }

}
public class IskljuciSvetlo extends Dogadjaj public IskljuciSvetlo(long vremeKasnjenja) public void akcija() {

// Ovde treba da bude kod za upravljanje hardverom. // kojim se fiziki gasi svetlo. svetlo = false;

}
public String t o S t r i n g O { return "Svetlo je iskljueno"; }

}
private boolean voda = false; public class UkljuciVodu extends Dogadjaj public UkljuciVodu(long vremeKasnjenja) public void akcija() voda = true; { { (super(vremeKasnjenja);}

// Ovde treba da bude kod za upravljanje hardverom.

}
public String toString() { return "Voda u stakleniku je putena"; { { su pe r(vremeKasnjenja); } }

}
public class IskljuciVodu extends Dogadjaj public IskljuciVodu(long vremeKasnjenja) public void akcija() voda = false; {

// Ovde treba da bude kod za upravljanje hardverom.

}
public String t o S t r i n g O { return "Voda u stakleniku je zatvorena"; }

private String termostat = "Dan"; public class TermostatNoc extends Dogadjaj public T e r m o s t a t N o c (1ong vremeKasnjenja) su pe r( vr e m e K a s n j e n j a ) ; { {

}
public void akcija() termostat = "No"; { // Ovde treba da bude kod za upravljanje hardverom.

}
public String t o S t r i n g O return "Noni { reim termostata";

public class TermostatDan extends Dogadjaj public TermostatDan(long vremeKasnjenja) s u p e r ( v r em eK as nj en ja);

{ {

294

Misliti na Javi

p ublic void a k c ija ( ) { // Ovde treba da bude kod za u p ra v lja n je hardverom. termostat = "Dan";

)
p ublic S trin g to S tr in g () { return "Dnevni reim term o stata";

} }
// Primer metode a k c ija ( ) koja ubacuje // novu a k c iju u li s t u dogaaja. p ub lic c lass Zvono extends Dogadjaj { p ublic Zvonoflong vremeKasnjenja) { super(vrem eKasnjenja); } p ublic void a k c ija ( ) { dodajDogadjaj(new Z von o(vrem eKasn jen ja));

}
p ub lic S trin g to S trin g O { retu rn "Z vo n c e !"; }

}
p ub lic c la s s R e sta rt extends Dogadjaj { p riv a te D ogad jaj[] lis ta D o g a d ja ja ; p ub lic R e sta rt(lo n g vremeKasnjenja) { super(vrem eKasnjenja); th is .lis ta D o g a d ja ja = 1ista D o g ad jaja; for(D ogadjaj d : 1is ta D o g ad jaja) { dodajDogadjaj ( d ) ;

}
p ublic void a k c ija ( ) { for(D ogadjaj d : 1ista D o g ad jaja) { d . s t a r t O ; // Ponovo pokreni svaki dogaaj dodajD ogadjaj(d);

}
s t a r t ( ) ; // Ponovo pokreni ovaj dogaaj dodajD ogadjaj(thi s ) ;

}
p ublic S trin g to S tr in g () { return "R e starto van je sistem a ";

} }
p ub lic s t a t ic c lass Z avrsi extends Dogadjaj { p ub lic Z a v rs i(lo n g vremeKasnjenja) { super(vrem eKasnjenja); } p ublic void a k c ija ( ) { S y s te m .e x it(0 ); } p ublic S trin g to S tr in g () { retu rn "Z a v r a v a n je "; }

} / / / =-

P rom enljive svetlo, voda i term ostat p rip a d a ju sp o ljn o j klasi UpravljanjeStaklenomBastom, a ipak u n u tra n je klase m o g u d a im p ristu p e bez u p o tre b e k valifikatora i bez p o sebn ih dozvola. U stv a rn o m p ro g ra m u , veina m eto d a ak cija( ) bi ukljuivala n eku vrstu up rav ljan ja h ard v ero m .

Poglavlje 10: Unutranje klase

295

Veina klasa izvedenih iz klase Dogadjaj sline su, ali se klase Zvono i Restart izdvajaju. Klasa Zvono pokree zvonce i u listu dogaaja dodaje jo jed an objekat klase Zvono, tako da e p o n o v o zazvoniti neto kasnije. O b ra tite p a n ju na to da u n u tran ja klasa izgleda gotovo kao v iestruko nasleivanje: klasa Zvono im a sve m eto d e kao i klasa Dogadjaj, a takoe izgleda kao da im a i sve m eto d e spoljne klase UpravljanjeStaklenomBastom. Klasa Restart do b ija niz objek ata Dogadjaj koje d od aje upravljau. Poto je Restart sam o jo je d n a v rsta klase Dogadjaj, u m eto d i Restart.akcija( ) u listu takoe m oete da d o d a te i o bjekat klase Restart, kako bi se sistem s v rem en a n a v rem e po no vo p o k ren u o . N a red n a klasa kon fig u rie sistem tako to p rav i o bjek at klase UpravljanjeStaklenomBastom i d o d ajte razn e v rste obj'ekata Dogadjaj. O vo je p rim e r p ro je k tn o g ob rasca C om m a n d (n a re d b a ) - svaki o b jek at klase listaDogadjaja jeste zah tev kap su liran kao objekat:
// : u n u tra sn je k lase/ U p ra vljacS tak len eB aste.jav a // K o n fig u rii i pokreni sistem staklene bate. // {A rg s: 5000) import u n u tra s n je k la s e .u p ra v lja c .* ; p u b lic c lass U p ra vlja cStak le n eBaste { p u b lic s t a t ic void m a in (S trin g [] args) { UpravljanjeStaklenomBastom gc = new UpravljanjeStaklenom Bastom (); // Umesto u p is iv a n ja direktno u program, na ovom mestu b iste mogli // da u ita te k o n fig u ra c iju iz tekstu alne datoteke: gc.dodajDogadjaj(gc.new Zvono(900)) ; D ogadjaj[] 1i staDogadjaja = { gc.new Term ostatNoc(O), gc.new U k lju c iS v e tlo (2 0 0 ), gc.new I s k l ju c iS v e tlo (4 0 0 ), gc.new U kljuciV od u(6 00 ), gc.new I s k l j u c i Vodu(800), gc.new TermostatDan(1400)

};
gc.dodajDogadjaj(gc.new R estart(2000, 1is ta D o g a d ja ja )) ; i f ( a r g s .1ength = = 1) gc.dodajD ogadjaj( new U pravljanjeStaklenom Bastom .Zavrsi( new In te g e r(a rg s [ 0 ] ) ) ) ; g c .p o k re n i( ) ;

}
} /* Is p is : Zvonce! Noni reim termostata S v e tlo je ukljueno S v e tlo je is k lju e n o Voda u stak len ik u j e ukljuena Voda u stakleniku j e is k lju e n a Dnevni reim termostata R estarto van je sistema Zavravanje

296

Misliti na Javi

O va klasa je o d g o v o rn a za in icijalizaciju sistem a, pa o n a u k lju u je sve p o tre b n e dogaaje. D ogaaj Restart se p o k ree vie p u ta, a o n svaki p u t uitava klasu listaDogadjaja u o b jek at UpravIjanjeStaklenomBastom. U koliko zad ate b ro j m ilisek u n d i kao a rg u m e n t n a k o m a n d n o j liniji, Restart e o k o n ati p ro g ra m posle tolik o m ilisek u n d i. (To se u p o trebljava p rilio m testiran ja.) N aravno, to se m oe p o stii i n a fleksibilniji n ain - u m e sto da u p iem o do gaaje direk tn o u p ro g ra m , p ro ita e m o n jih o v u in icijalizacion u listu iz datoteke. U je d n o m ve b an ju iz poglavlja Javin u la zn o -izla zn i sistem ba se to zah teva o d vas. O vaj p rim e r b i treb alo d a vas uveri u v re d n o st u n u tra n jih klasa, n aro ito kada se koriste u n u ta r k o stu ra up rav ljan ja. U po glavlju Grafika korisnika okruenja videete kako se u n u tra n je klase je d n o sta v n o u p o treb lja v aju za o p isiv an je akcija grafikog o k ru en ja. K ada p ro itate i to poglavlje, b iete uvereni u n jih o v u v red n o st.

Veba 24: (2) U d a to teci UpravljanjeStaklenomBastom.java d o d a jte u n u tra n je klase tip a Dogadjaj koje u k lju u ju i iskljuuju v en tilato re. K onfiguriite d a to te k u UpravljacStakleneBaste.java tako d a u p o treb ljav a te nove o b jek te tip a Dogadjaj. Veba 25: (3) U d atoteci UpravljanjeStaklenomBastom.java n asledite klasu UpravljanjeStaklenomBastom da b iste d o d a li u n u tra n je klase tip a Dogadjaj koje u klju uju i iskljuuju g e n erato re v o d e n ih kapljica. N apiite n o v u verziju p ro g ra m a UpravljacStakleneBaste.java tako da u p o tre b ljav a te nove o b jek te tip a Dogadjaj.

Nasleivanje unutranjih klasa


Poto k o n stru k to r u n u tra n je klase m o ra da b u d e p o vezan s referen co m na objekat spoljne klase, p ri n je n o m n asle iv an ju stvari se m alo k o m p lik u ju . P rob lem je to ta ,,tajna referenca na sp oljn i objekat m ora da b u d e inicijalizovana, a u izvedenoj klasi vie ne postoji p o d raz u m e v a n i objek at za koji bi o n a bila povezana. Reenje je u p o tre b iti sin tak su koja izriito o d re d u je povezivanje:
//: un u trasn jek lase/N asled iU n u trasn ju.java // N asle ivanje unutranje k lase. c la s s ImaUnutrasnju { cla s s Unutrasnja {)

1
public c la s s NaslediUnutrasnju extends Im aUnutrasnju.Unutrasnja { //! N aslediU nu trasn ju() { } // Ne moe da se prevede NaslediUnutrasnju(Im aUnutrasnju iu ) { iu .su p e rO ;

}
p ub lic s t a t ic void m a in (S trin g [] args) { ImaUnutrasnju iu = new ImaUnutrasnju ( ) ; NaslediUnutrasnju i i = new N a s le d iU n u tra s n ju (iu );

III--

Poglavlje 10: Unutranje klase

297

V idite da klasa NasleiUnutrasnju p ro iru je sam o u n u tra n ju klasu, ali ne i spoljanju. Kada d o e v rem e d a se n a p ra v i k o n stru k to r, p o d ra z u m e v a n i k o n stru k to r v am ne o d govara, a ne m o e te sam o d a p ro sled ite referen cu sp o ljn o m ob jek tu . Pored toga, u k o n stru k to ru m o ra te d a u p o tre b ite sintaksu:
ReferencaN aSpoljnuKla s u .s u p e r();

T im e o b ezb ed u jete n e o p h o d n u referen cu i m o i ete da prevedete p ro g ra m .

Veba 26: (2) N ap rav ite klasu koja sad ri u n u tra n ju klasu koja im a n e p o d ra zu m e v a n i
k o n stru k to r (o naj koji p rim a a rg u m e n te ). N ap rav ite d ru g u klasu koja sadri u n u tra n ju klasu n asled en u iz prve u n u tra n je klase.

Da li unutranja klasa moe da se redefinie?


ta se deava k ad a n a p ra v ite u n u tra n ju klasu, n asled ite sp o ljn u klasu i p o to m redefin iete u n u tra n ju ? O d n o sn o , d a li je m o g u e redefin isati u n u tra n ju klasu? in i se da bi to b io m o an k o n cep t, ali ,,red efin isanjem u n u tra n je klase k ao da je o n a m eto d a spoljne klase, u su tin i se nita ne postie:
//: u n u tra sn jek lase/V eli k o Ja je .ja v a // Unutranja k lasa ne moe da bude re d e fin isa n a poput metode. import s t a t ic n e t.m in d v ie w .u til. P r i n t . * ; c la s s J a j e { p riv a te Zumance z; protected c la s s Zumance { p u b lic Zumance() { p r in t("Ja je .Z u m a n c e ()" ) ;

}
public J a j e ( ) { p r i nt("Novo J a j e ( ) ) ; z = new Zumancef);

} }
p ub lic c la s s V e lik o Ja je extends J a j e { p ub lic c la s s Zumance { p ublic ZumanceO { pri n t("V e l i ko Jaje.Zu m an ce()" ) ;

}
p ub lic s t a t ic void m a in (S trin g [] args) new V e li k o J a j e ( ) ; {

}
} /* Is p is : Novo J a j e ( ) Jaje.Zum anceO

* ///:-

298

Misliti na Javi

Prevodilac au to m atsk i prav i p o d raz u m ev an i k o n stru k to r koji poziva p o d ra z u m e v a n i k o n stru k to r o sno v n e klase. S o b ziro m na to d a se p rav i klasa Velikojaje, m ogli biste oekivati d a e b iti po zv an a ,,redefinisana verzija klase Zumance, ali to se n e deava, kao to v id ite iz rezu ltata. O vaj p rim e r sam o pokazuje da se p rilik o m n asle iv an ja spoljanjiJi klasa n ita p o seb no ne deava u u n u tra n jim . Dve u n u tra n je klase su p o tp u n o o d v o jen i en tite ti, svaka u svom im e n sk o m p ro sto ru . Ipak, m o g u e je izriito n asleivanje u n u tra n je klase:
//: u n u tra sn je k la se / V e lik o Ja je 2 .ja v a // P ra v iln o n asle ivan je unutranje klase import s t a t ic n e t.m in d v ie w .u til. P r in t . * ; c la s s Ja je 2 { p rotecte c la s s Zumance { p u b lic Zumance{) { p rin t("Ja je 2 .Z u m a n c e ()" ) ; p u b lic void f ( ) { p r in t("Ja je 2 .Z u m a n c e .f()" ) ; } }

}
p riv a te Zumance z = new ZumanceO; publ ic J a je 2 () { print("N o vo J a j e 2 ( ) " ) ; } p u b lic void ubaciZumance(Zumance zz) { z = zz; } p u b lic void g () { z . f ( ) ; }

}
p ub lic c la s s V e lik o Ja je 2 extends Ja je 2 { p u b lic c la s s Zumance extends Jaje2.Zumance { p ub lic ZumanceO { p r i n t ( " V e l i koJaje2.Zum ance()" ) ; } p u b lic void f ( ) { p r i n t ( " V e li koJaje2.Zum ance.f( ) " ) ; }

}
p ub lic V e lik o Ja je 2 () { ubaciZumance(new Zumancef)) ; } p u b lic s t a t ic void m a in (S trin g [] args) { Ja je 2 e2 = new V e lik o J a je 2 ( ) ; e 2 .g ();

}
} /* Is p is : Jaje2.Zum ance() Novo Ja je 2 ( ) Jaje2.Zum ance() Vel i k o Ja je 2 .Zumance() V e lik o Jaje 2 .Z u m an ce (). f ( )

* ///:Sada klasa VelikoJaje.Zumance ek sp licitn o p ro iru je klasu Jaje2.Zumance i redefinie n jen e m e to d e. M etoda ubaciZumance( ) om o g u av a d a klasa VelikoJaje2 je d a n o d svojih u n u tra n jih ob jekata klase Zumance svede navie ka referenci z u klasi Jaje2. Kada m eto d a g ( ) pozove m e to d u z .f( ) bie u p o treb ljen a redefinisana verzija m eto d e f ( ). D ru g i poziv k o n s tru k to ru o sn o v n e klase Jaje2.Zumance( ) jeste poziv iz k o n stru k to ra Velikojaje2.Zuinance. I sam i v idite d a e p rilik o m poziva m eto d e g( ) biti p o zvana red efin isan a verzija m e to d e f ( ).

Poglav[je 10: Unutranje klase

299

Lokalne unutranje klase


Kao to je reeno, u n u tra n je klase se prave i u n u ta r blokova koda, najee u n u ta r tela m eto d e. L okalna u n u tra n ja klasa ne m oe im ati specifik ato r p ristu p a , p o to o n a nije deo sp oljne klase, ali m oe p ristu p a ti fin aln im p ro m en ljiv a m a u tek u em b lo k u k o d a i svim lan o vim a o k ru u ju e klase. Evo p rim e ra gde se p o re d i p ravljenje lokalne u n u tra n je klase i a n o n im n e u n u tra n je klase:
//: u n u trasn jeklase/Lo kalnalln u trasn jaK lasa.java // uva sekvencu Objekata. import s t a t ic n e t.m in d v ie w .u til. P r in t . * ; in te rfa c e Brojac { in t n e x t();

pub lic c la s s LokalnaUnutrasnjaKlasa { p riv a te in t broj = 0; Counter u zm iB ro jac(fin al S trin g ime) { // Lokalna unutrasnja klasa: c la s s Lokaln iB ro ja c implements Brojac { pu b lic L o k a ln iB ro ja c () { // Lokalna unutrasnja klasa moe imati konstruktor p ri nt ('' Lokal ni Brojac ( ) " ) ;

}
p ub lic in t n ex t() { p rin tn b (im e ); // Pristu p an je lo k a ln o j fin a ln o j return broj++;

return new L o k a ln iB ro ja c ( ) ;

}
// Is to sa anonimnom unutranjom klasom: B rojac L o k a ln iB ro ja c 2 (fin a l Strin g ime) { return new B ro ja c () { // Anonimna unutranja klasa ne moe imati imenovani // konstruktor, nego samo i n i c i j a l i z a t o r in stan ce:

{
pri nt ("B r o ja c O " ) ;

}
public in t nex t() { p rin tn b (im e ); // Pristu p an je lo k aln o j fin a ln o j return broj++;

} }; }
p u b lic s t a t ic void m a in (S trin g [] args) { LokalnaUnutrasnjaKlasa l i c = new Lo k a ln aU n u trasn jaK la sa();

300

Misliti na Javi

Brojac
cl = 1 ic.uzmiBrojac("Lokalna unutranja "), c2 = 1 ic.uzmiBrojac("Anonimna unutranja "); for(int i = 0; i < 5; i++) p r i n t( cl .n ex t( )); for(int i = 0 ; i <5; i++) p r in t( c2 .n ex t( ));

}
} /* Ispis: LokalniBrojac() Brojac() Lokalna unutranja 0 Lokalna unutranja 1 Lokalna unutranja 2 Lokalna unutranja 3 Lokalna unutranja 4 Anon im na unutranja 5 Anonimna uriutranja 6 An on im na unutranja 7 Anon im na unutranja 8 Anonimna unutranja 9

* ///:B ro ja c vraa sledeu v re n o st u sekvenci. R ealizovan je i kao lokalna klasa i k ao a n o n im n a u n u tra n ja klasa. O b e te klase je d n a k o se p o n aaju i im aju iste m o g u n o sti. Poto im e lo k aln e u n u tra n je klase nije d o s tu p n o izvan te m etode, je d in o o p ra v d a n je za u p o tre b u te klase u m esto a n o n im n e u n u tra n je klase bila bi p o tre b a za im e n o v a n im ko n stru k to ro m i/ili p rek lo p ljen im k o n stru k to ro m , jer a n o n im n a u n u tra n ja klasa m oe im ati sa m o inicijalizaciju instance. D ru g i razlog za p ravljenje lokalne u n u tra n je klase u m esto a n o n im n e u n u tra n je klase bila bi p o tre b a da se n ap rav i vie o b jek ata te klase.

Identifikatori unutranjih klasa


Poto za sv ak u kiasu p ostoji d a to tek a .class u kojoj se uvaju sve in fo rm acije o pravljenju ob jekata tog tip a (p o m o u tih in fo rm acija se d obija tzv. m eta klasa - objekat tip a C lass), vero v atn o p o g a a te da i za u n u tra n je klase m o ra p o sto jati njihova d ato tek a .class. Im ena tih d a to tek a /k lasa d o b ijaju se p o p recizn o j fo rm u li: im e spo ljn e klase posle koga dolazi znak $, a zatim im e u n u tra n je klase. N a p rim er, u p ro g ra m u L o k a ln a U n u tra s n ja K lasa.jav a bie n ap rav ljen e sledee d a to te k e .class:
Bro jac.class LokalnaUnutrasnjaKlasa$l. c l ass Lokal naUnutrasnjaKlasa$lLokalniBrojac.class LokalnaUnutrasnjaKlasa.klasa

Poglavlje 1

Unutranje klase

301

U koliko su u n u tra n je klase a n o n im n e , p revodilac k o risi i-rojeve kao n jihove id e n tifikatore. A ko su u n u tra n je klase u gn e en e u n u ta r d rugil in n ira n jih klasa, d o d a ju se o d g o v araju a im en a n a k o n znaka $ i id en tifik ato ra spoljni!. m z . N ain generisanja in te rn ih im en a je jed n o stav an , ali isto\i n en o ro b u s ta n i sp o so b an da o b ra d i veinu situ aciia.5 Poto je to sta n d a rd n a teh n ik a / o delu im en a u Javi, generisan e d ato te k e e a u to m a tsk i b iti nezavisne o d p latfo rm e. n p revo dilac n a vie nain a m e n ja vae u n u tra n je klase kako bi m ogle d a rade.)

Saetak
Interfejsi i u n u tra n je Jdase n a p re d n iji su k o n cep ti o d on ih ii p o sto je u m n o g im O O P jezicim a. N a p rim er, u C ++-U ne p o sto ji nita nalik njim a. ( i zajed no reavaju isti p ro b lem koji je i C + + p o k u ao d a rei p o m o u v iestru ko g nasieivanja. Isp ostavilo se d a se u C + + -u v iestru k o nasleivanje p rilin o teko upotreb ljava, d o k su Javini in terfejsi i u n u tra n je Iase m n o g o p ristu p an iji. Iako su te m o g u n o sti p rilin o jasne, n jih o v a u p o tre b a je 1 f anje pro je k to v an ja, p o p u t p o lim o rfiz m a. S v re m e n o m ete sve b olje p rep o zn av ati situa c u k o jim a tre b a da k oristite in terfe js ili u r.u tra n ju klasu, ili i je d n o i d ru g o isto v re ire U o v o m tre n u tk u , bilo bi d o b ro d a p o zn ajete b a re m n jih o v u sin tak su i sem an tik u. 1 o se b u d e te sretali sa njim a, sve vie ete ih prih v atati. Reenja odabranih vebi data su u elektronskom dokum entu Thin tions G uide , koji se moe kupiti na lokaciji www.MindView.net. 1ln Java A n n o ta ted S olu -

S d r u g e s tra n e , $ je m e ta z n a k u U n ix o v im k o m a n d n im o k ru e n ji >a ete p o n e k a d im a ti p ro b le m a p rilik o m lis ta n ja d a to te k a .cla ss. O v o je p o m a lo u d n o , je r k o m p a n ija S u n ra i u U n ix u . P r e tp o sta v lja m d a to p ita n je u S u n u n isu ni ra z m a tra li, m islei d a ete se i ro d n o u s re d s re d iti n a d a to te k e
s,i i/vo rn im kodom .

uvanje objekata
P r il i n o
je je d n o s t a v a n pro g ra m

KOJI

k o r is t i

t a Cn o

o d re en

bro j

o b je k a t a

p o znatog v rem en a trajanja. U p ro g ram im a p o prav ilu o b in o p rav ite nove objekte n a o snovu nekog k riteriju m a koji je p o zn at sam o u v rem e njegovog izvravanja. Sve d o k p ro g ram ne po ne da se izvrava, neete znati koliko vam o b jekata treb a, niti kog su o n i tipa. D a biste reili ovaj p rob lem , m o ra te b iti u stan ju d a n ap rav ite proizvoljan broj objekata, u bilo kom tren u tk u , bilo gde. Dakle, ne m o ete se o slo n iti n a to d a ete svakom o b jek tu m oi da p ristu p ate preko neke reference kojoj je dodeljen o ime:
MojaKlasa referenca

po to n ik a d a neete zn ati koliko e vam o b jek ata zaista treb ati. Veina jezika im a neki n a in za reavanje ovog tem eljn o g p ro b le m a. Java n u d i nekoliko n ain a za u vanje o b jek ata (o d n o sn o , referenci n a o b jek te). U sam jezik je u g ra en tip niza, o k o m e je ve bilo rei. N iz je najefikasniji n ain u vanja g ru p e o b jek ata i p re p o ru u je se za uvanje g ru p e p ro stih tip o v a. Ali n iz im a n e p ro m e n ijiv u v eliinu, a u o p tije m sluaju, u tre n u tk u p isan ja p ro g ra m a vi n e zn ate koliko o b jek ata e vam biti p o tre b n o , niti treb a li v am n eki n a p re d n iji n ain sklad iten ja objek ata; zato je n ep ro m enljiva veliina niza pretek o og ran ien je. Za reavanje ovog p ro b lem a, b ib lio tek a Javinih u slu n ih klasa im a p rili n o zao k ru en sk u p kontejnerskih k'lasa, iji su o sn o v n i tip o v i L ist, Set, Q u e u e (za redove za ekanje) i M ap. O vi tip o v i objek ata p o z n a ti su i p o d im e n o m klase kolekcija, ali p o to se naziv C olle c tio n u Javinoj biblioteci k o risti za ozn aav an je o d re d e n o g p o d sk u p a te biblioteke, koristiu precizniji p ojam ,,k o n tejn e r. K o n tejneri o m o g u av aju n a p red n ije nain e za sm etan je o b jek ata i p o m o u njih m o ete d a reite izn e n a u ju e veliki bro j p ro b lem a. P ored o stalih svojih obeleja na p rim er, Set (sk u p ) ne m o e da sari p o n o v ljen e elem en te, a M ap (m ap a) je asocijativan niz koji slui za u vanje p aro v a m e u so b n o asociran ih objek ata - Javine k o n tejn ersk e klase a u to m a tsk i podeavaju svoju veliinu. Stoga, za razliku o d nizova, u k o n te jn e r m o ete staviti pro izv o ljan broj p ro izv o ljn o velikih objekata; d o k piete p ro g ram , ne m o ra te zn ati koliki k o n te jn e r treb a da n ap rav ite. Iako u Javi n e m a ju d ire k tn u p o d rk u kroz rezervisane rei,' k o n tejn ersk e klase su jedna o d n ajsn anijih alatki za p ro g ra m ira n je , je r p rim e tn o sk rau ju v re m e pisan ja p ro g ram a. U ovom poglavlju stei ete o sn o v n a zn an ja p o tre b n a za rad s Javinom b ibliotekom k o n tejn era u tip i n im p rim e n a m a . B aviem o se k o n te jn e rim a koje ete u p o treb ljav ati u sv ak o d n ev n o m p ro g ra m ira n ju . Kasnije, u poglavlju D etaljno razm atranje kontejnera, u p o z n a e m o vas sa ostalim k o n te jn e rim a i s p o je d in o stim a o njih o v o j fu n k c io n a ln o sti i n a in im a korienja.

U graenu p o drk u za kontejnere im aju b ro jn i jezici, m eu kojim a Perl, P vthnn i Ruby.

Poglavlje 11: uvanje objekata

303

Generike klase i kontejneri za bezbedan rad s tipovima


Pre Jave SE5, jed an o d p ro b lem a p rilik o m k o rien ja k o n tejn era b io je to to je prevodilac d o p u ta o u m e tan je pogren o g tip a u k ontejner. N a p rim er, recim o d a n am tre b a k o n te jn e r sa ob jek tim a tip a Jabuka i da em o u p o tre b iti o sn o v n i k o n tejn e r za sve n am en e, ArrayList. Z a poetak, sm atrajte da je ArrayList din am ik i niz koji se au to m atsk i p ro iru je. K orienje klase ArrayList je jed n o stav n o : tre b a d a n ap rav ite kontejner, sm estite u njega objekte m eto d o m a d d ( ), a kasnije da ih izvadite m e to d o m g e t( ) uz korienje indeksa, ba kao u sluaju niza, ali bez uglastih zag rad a.2 ArrayList im a i m e to d u siz e ( ) k oja vraa tren u tn i bro j elem en ata i tim e vas spreava d a slu ajn o d o ete d o k raja i p ro u zro k u je te ,,izuzetak to k o m izvravanja (engl. ru n tim e exception); izuzeci e b iti p redstavljeni u poglavlju O brada greaka pom ou izuzetaka. U ovom p rim e ru , u k o n tejn e r se stavljaju i iz njega vade o bjekti tip o v a Jabuka i Narandza. Poto u p rim e ru nisu u p o tre b lje n i g en erik i tip o v i, Javin p rev o d ilac e vas u p o zo riti n a greku. D a b ism o spreili ispisivanje to g u p o zo ren ja, o vde sm o u p o treb ili p o se b n u anotaciju Jave SE5. A n otacije p o in ju zn a k o m @ i p rim a ju a rg u m en t; ova glasi @SuppressWarnings (,,u n ch eck ed ), a n jen a rg u m e n t p o k azu je d a n e elim o ispisivanje sam o u p o zo ren ja unchecked", koje k azuje d a nije ja sn o koji se tip (Jab u k a ili N a ran d za) d o b ija iz k on tejn era:
//: cuvanje/JabukelN arandzeBezGenerickihTipova.java // Jednostavan primer s kontejnerom (napisan tako da prevo d ilac // daje upozorenja). // (ThrowsExceptionj import j a v a . u t i l .* ; c la s s Jabuka ( p riv a te s t a t ic long b ro jac; p riv a te fin a l long id = brojac++; p ub lic long id ( ) { retu rn id ; }

}
c la s s Narandza {} p u b lic c lass JabukelNarandzeBezGenerickihTipova { @SuppressWarnings("unchecked") pu b lic s t a t ic void m a in (S trin g [] args) { A rra y L is t jabuke = new A r r a y L is t ( ) ; f o r ( i n t i = 0 ; i < 3 ; i++) jabuke.add(new Ja b u k a O ); // Nema prepreke da se jabukama dodaju Narande: jabuke.add(new N arandzaO ); f o r ( i n t i = 0; i < ja b u k e .s iz e (); i++)

O v d e bi d o b r o d o lo p re k la p a n je o p e ra to ra . K o n te jn e rsk e k la se u je z ic im a C + + i C # p ro iz v o d e istiju s in ta k s u z ato to se u p o tre b lja v a p re k la p a n je o p e r a to r a .

304

Misliti naJavi

( ( Ja b u k a )ja b u k e .g e t (i)). i d ( ) ; // Narandza se o tk riv a tek p rilik o m iz vravan ja

}
} /* (Po k re n ite da b is te v id e li r e z u lta t) * / / / -

Vie o a n o ta c ijam a u Javi SE5 saznaete u poglavlju Atiotacije. Klase Jabuka i Narandza se razliku ju ; n e m a ju n ita zajedniko, sem to p o ti u o d n a tklase Object. (N e zaboravite: uk olik o izriito ne n avedete klasu o d koje nasleujete, au to m atsk i n asle ujete Object.) P oto ArrayList sadri Jdase Object, m e to d o m add() k o n tejn e ra ArrayList u njega m o ete d o d a v ati ne sam o objekte tipa Jabuka, nego i objekte tip a Narandza, a da se n e p o b u n i n i p rev od ilac u v rem e prev o en ja, ni izvrno o k ru en je Jave u v rem e izvravanja. Kada m e to d o m get() k o n te jn e ra ArrayList p o k u ate d a p rib av ite o n o to su p o v aem m iljen ju o b jek ti tip a Jabuka, v ratie v am se referenca na Object koji m o ra te svesti n a tip Jabuka. N adalje, ceo izraz m o ra te zatvo riti u zagrade da b i se to svo en je obavilo p re p o ziv an ja m e to d e id() za klasu Jabuka; u p ro tiv n o m , izazvaete sin ta k sn u greku. U v re m e izvravanja, k ad a o b jek at tip a Narandza p o k u ate da svedete na tip Jabuka, javie se greka u o b lik u p re th o d n o s p o m e n u to g izuzetka. U poglavlju G eneriki tipovi sazn aete d a pravljenje klasa p o m o u odg ov arajueg Javin o g m e h a n iz m a u m e d a b u d e sloeno, ali d a je p rim e n a je d n o m d efin isanih generikih ldasa najee je d n o stav n a . P rim e ra rad i, da b iste definisali ArrayList p red v i en za uvanje objekata tip a Jabuka, p iete ArrayList<Jabuka> u m esto sam o ArrayList. U glaste zagrade o k ru u ju p ara m etre tipa (m o e ih b iti vie) a o n i zadaju tip(ove) koje taj p rim e rak k o n te jn era m o e da p rim i. M eh an izam g enerik ih tip o v a vas ve u vrem eprevoenja spreava da u k o n te jn e r stavite pog rean tip o b je k ta .' Evo p re th o d n o g p rim e ra n ap rav ljeno g p o m o u generikih tipova:
//: cuvanje/JabukelNarandzePom ocuGenerickihTipova.java import j a v a . u t i l .* ; p ublic c la s s JabukelNarandzePoinocuGenerickihTipova { p ub lic s t a t i c void m a in (S trin g [] args) { ArrayList<Jabuka> jabuke = new A rrayList< Ja b u k a > (); f o r ( i n t i = 0 ; i < 3 ; i++) jabuke.add(new Ja b u k a O ); // Greka koja se p r i j a v l j u j e u vreme prevoenja: // jabuke.add(new N arandzaO ); f o r ( i n t i = 0; i < j a b u k e .s iz e ( ) ; i++) S y ste m .o u t.p rin tl n (jab uk e.g et ( i ) . i d ( ) ) ; // Sintaksom foreach: fo r(Jab u k a c : jabuke) S y s t e m .o u t .p r in t ln ( c .id ( )) ;

}
' N a k ra ju p o g la v lja Cetteriki tipovi r a z m o tr i e m o d a li je to b a to lik o te ak p ro b le m . U is to m p o g la v lju v id e e te i d a s u Jav in i g e n e ri k i tip o v i p o d e s n i za m n o g o vie o d k o n te jn e ra koji se s ta ra ju za bezb e d a n ra d s tip o v im a .

Poglavlje 11: ujvanje objekata

305

} /* Is p is : 0

1
2

1
2

* ///:Sada e p revo d ilac spreiti stavljenje o b jek ta tip a N a ra n d /a m e u ja b u k e , p a e se ta greka p rijav iti u v rem e p rev o en ja, a ne u v rem e izvravanja. O b ra tite p a n ju i na to da p rilik o m v a en ja stavki iz Liste vi e nije p o tre b n o svoenje tip o v a. P oto Lista zna koji tip sadri, o n a obavlja svoenje k a<ta vi pozovete g et(). D akle, ne sa m o da p rev o d ilac zbog gen erik ih tip o v a p roverava tip o b jek ata koji stavljate u ko n tejn er, nego je i sintaksa p rilik o m u p o tre b e objek ata u n jem u istija. P rim e r p o k azu je i sledee: ako v am ne tre b a in d ek s svakop (lem en ta, za izb o r svakog ele m e n ta u Listi m oete u p o tre b iti sin tak su foreach. K ada tip o b jek ta u k o n te jn e ru zadate kao g eneriki p ara m t ir, ne m o ra te u k o n te jn e r stavljati sa m o taj tip, p o to svoenje navie rad i i s gen erik im p o v im a kao i sa o stalim tip o v im a: //: cuvanje/GenerickiTipoviISvodjenjeNavise.java import java.util
c la s s c la s s c la s s c la s s GreniSm it extends Jabuka {} Gala extends Jabuka {} Fudzi extends Jabuka {} Breburn extends Jabuka {}

p u b lic c la s s G e n erick iT ip o vilS vo d je n je N avise { p u b lic s t a t ic void m a in (S trin g [] args) { ArrayList<Jabuka> jabuke = new ArrayList<Jabuka>( jabuke.add(new G re n iS m itO ); jabuke.add(new G a la ( ) ) ; jabuke.add(new Fud zi( ) ) ; jabuke.add(new B re b u rn ()); fo r(Jab u k a c : jabuke) S y s te m .o u t .p r in tln (c );

}
} /* Is p is : (uzorak) Greni Smi t@7d772e G ala@llb86e7 Fudzi @35ce36 Breburn@757aef

* ///D akle, u k o n te jn e r o d re e n d a sadri objekte tip a Ja b u k a m o ete d o d av ati i p o d tip o v e klase Ja b u k a .

306

Misliti na Javi

Ispis proizvodi p o d ra z u m e v an a m e to d a toStringO klase Object, koja ispisuje im e klase i heksadecim alni h e k d d o b jek ta (koji g en erie m eto d a hashCode()). He kodove em o detaljno razm o triti u p o glavlju D etaljno razm atranje kontejnera.

Veba 1: (2) N apravite n o v u klasu MorskoPrase sa lan o m int brojPrasadi koji se inicijalizuje u k o n stru k to ru . D o d ajte klasi m e to d u sk a ce( ) koja ispisuje broj m o rsk o g p raseta i da li on o skae. N aprav ite n o v k o n te jn e r tip a ArrayList i d o d ajte nekoliko o b jek ata klase MorskoPrase u listu. Sada u p o tre b ite m e to d u g e t( ) da b iste se kretali kroz listu i pozivali m etodu skace( ) svakog o b jek ta tip a MorskoPrase.

Osnovni pojmovi
Kontejnerska b iblioteka Jave 2 obavlja z ad a tak uvanja objek ata" i deli ga u dva zasebna pojm a, izraena u o b lik u o sn o v n ih in terfe jsa bibliote'ke:

1. Collection (kolekcija): g ru p a p o je d in a n ih e lem en ata na koje je esto p rim en jen o jedno ili vie pravila. List (lista) m o ra d a uva elem ente u o d re e n o m redosledu, Set (skup) ne m oe da sadri p o n o v ljen e elem ente, a Queue (red) daje elem ente red o m
koji zadaje disciplina ekanja (o b in o istim red o m kojim su elem enti u m etan i).

2. Map (mapa): g ru p a p aro v a o b je k a t-k lju , koja o m o g u av a p ro n alaen je objekta p om ou njegovog kljua. ArrayList o m o g u av a p ro n alaen je objekta p o m o u njegovog indeksa, pa n a neki n ain p rid ru u je brojeve o b jek tim a. M apa om oguava pronalaenje objekta p o m o u n je m u p rid ru e n o g drugog objekta. N ju nazivaju i asocijativan niz, zato to objekte aso cira (p rid ru u je ) d ru g im o b jek tim a, ili renik, zato to objekat trai p o m o u kljua, k ao to se u reniku definicija trai p o m o u rei. Mape su m o n e p ro g ra m sk e alatke. Iako to nije uvek m ogue, bilo bi id ealn o da vei d eo vaeg k oda rad i s tim interfejsim a, a da jedino m esto na kojem ete sp o m e n u ti taan tip koji u p o treb ljav ate b u d e m esto pravljenja instance. Dakle, ovako m o ete n a p ra v iti Listu:
List<Jabuka> jabuke = new A rray List< Ja b u k a > ();

Povedite rauna o to m e da je ArrayList sveden navie na List, su p ro tn o n a in u na koji sm o radili u p re th o d n im p rim e rim a . In terfejs k o ristim o da b ism o realizaciju m enjali sam o na m estu pravljenja u koliko n a k n a d n o o d lu im o da je izm enim o. To se rad i ovako:
List<Jabuka> jabuke = new Link ed List< Jab uk a> ();

Dakle, obino pravite o b jek at k o n k re tn e klase, svedete ga navie na odgovarajui interfejs i zatim u ostatku k o d a u p o treb lja v ate taj interfejs. Ovaj pristu p nee rad iti uvek, zato to neke klase im aju d o d a tn u fu n k cio n aln o st. Na prim er, LinkedList im a d o d a tn e m e to d e k oje interfejs List ne sadri, a TreeMap im a m etode koje interfejs Map ne sadri. A ko v am tre b a ju te m eto d e, neete m oi d a svedete navie na optiji interfejs. Interfejs Collection p o o p ta v a ideju sekvence - nain a uvanja g ru p e o b jekata. Evo jednostavnog p rim e ra u k o jem se C ollection (ovde predstavljen ArrayListom) p u n i Integer objektim a, a zatim se svaki elem en t u rezu ltu ju e m k o n te jn e ru ispisuje:

Poglavlje 11: uvanje objekata

307

/ / : cu vanje/JednostavnaKolekcij a . ja va import j a v a . u t i l p u b lic c la s s Jed n o stavnaK o lekcija { p u b lic s t a t ic void m a in (S trin g [] args) { C ollection< Integer> c = new A rra y L is t< In te g e r> (); f o r ( i n t i = 0; i < 10; i++) c .a d d (i); // Automatsko pakovanje fo r (In te g e r i : c) S y s te m .o u t.p rin t(i + " , 1 1 );

}
} /* Is p is : 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,

* ///:P oto se u o v om p rim e ru u p o treb ljav aju sam o m eto d e interfejsa Collection, o dgovarao bi svaki ob jek at klase nasle en e o d to g in terfejsa, ali ArrayList predstavlja najosno vniji tip sekvence. Im e m e to d e add() n av o d i na p o m isa o d a o n a u kolekciju (Collection) d o d aje n o v elem e n t. M e u tim , u d o k u m e n ta c iji je p recizn o naved en o da add() ini d a ovaj p rim e ra k klase C ollection sadri d a ti e le m e n t. To je u ra e n o zbog sk u p a (potklase Set), koji elem e n t d o d a je kolekciji sam o ukoliko se o n ta m o ve n e nalazi. U sluaju klase ArrayList i svih vrsta Listi, add() uvek znai ,,d o d ati, p o to Liste dozvoljavaju p o stojan je d u p lik ata. K roza sve kolekcije m o ete p rolaziti sin ta k so m foreach, kao u p re th o d n o m p rim e ru . U n astav k u poglavlja n au iete da u p o treb ljav ate fleksibilniji Iterator.

Veba 2: (1 ) Izm enite p ro g ra m JednostavnaKolekcija.java tako da se za c upotrebljava Set. Veba 3: (2) Izm en ite p ro g ra m unutrasnjeklase/Sekvenca.java tako da m u m oete d o dati p ro izvoljan bro j elem en ata.

Dodavanje grupa elemenata


M eto d e za d o d av an je g ru p a elem en ata k o n te jn e ru (potklasi interfejsa C ollection) im aju i klasa Arrays i klasa C ollections paketa java.util. M etoda Arrays.asList() p rim a niz ili listu e le m e n ata razd v ojen ih zarezim a (koristei arg u m e n te pro m enljive d u in e) i p retv ara ih u o b jek at tip a List. CoIlections.addAU() p rim a o b jek at tip a Collection i niz ili listu e le m e n ata razd vojenih zarezim a, ije elem en te d o d aje tom k o n tejn eru . Evo p rim e ra u koje m su p rik a za n e obe te m eto d e, kao i k o n v en cio n aln ija m eto d a addAll() k o ju sadre svi tipo vi k o n tejn era:
//: cuvanje/DodavanjeGrupa.java // Dodavanje grupa elemenata objektima tip a C o lle c tio n . import j a v a . u t i 1.* ; p u b lic c la s s DodavanjeGrupa { p u b lic s t a t i c void m a in (S trin g [] args) { C o l1ection<Integer> k o le k c ija = new A rra y L is t< In te g e r> (A r ra y s .a s L is t(l, 2, 3, 4, 5 )) ;

308

Misliti na Javi

In te g e r[] jo sC elih B ro jeva = { 6, 7, 8, 9, 10 } ; kolekcija.addA l 1 (A r r a y s .a s L is t(jo s C e lih B r o je v a )) ; // Radi znatno bre, a l i ovako ne moete // n a p ra viti objekat tip a C o lle c tio n : C o lle c tio n s .a d d A ll(k o le k c ija , 11, 12, 13, 14, 15); C o llectio n s.ad d A ll (k o le k c ija , jo s C e lih B ro je v a ); // Pravi li s t u "podranu" nizom: List<Integer> l i s t a = A rra y s .a s L is t(1 6 , 17, 18, 19, 20); l i s t a . s e t ( l , 99); // OK -- izmenjen jedan element // 1i s t a . add(21); // Greka pri iz vrav an ju zato to se // pripadnom nizu ne moe m enjati v e li in a .

} III-K o n stru k to r za Collection m oe p rim iti d ru g i CoIIection koji u p o treb ljav a za so p stvenu inicijalizaciju, p a Arrays.asList() m o ete u p o tre b iti za p rav ljen je ulaza za k o n stru k tor. M e u tim , ColIections.addAlI() rad i m n o g o b r e , a je d n a k o je lako n ap rav iti Collection bez elem enata i zatim pozvati Collections.addAII(), p a to m p ristu p u treb a d ati p red n o st. M eto d a lan Collection.addAll() kao a rg u m e n t m o e p rim iti sam o d ru g i o b jek at tip a Collection, pa nije fleksibilna kao Arrays.asList() i ColIections.addAll() koje p rim a ju liste arg u m e n ata p rom enljive duine. Izlaz m etod e Arrays.asList() m oete u p o tre b iti i n ep o sred n o , kao Listu, ali je u to m sluaju on predstavljen nizom , a njegovu veliinu ne m o ete m en jati. U koliko takvoj listi pok uate da d o d ate - add() - ili o d u z m e te - delete() - elem en te, tim e biste pokuali da pro m en ite veliinu niza, to u v rem e izvravanja izaziva greku U n su p p o rte d O p e ra tio n . O g ran ien je m eto d e Arrays.asList() jeste to to o n a sa m o nagaa koji je rezu ltu ju i tip Liste i ne obraa panju na to em u je d o d elju jete. K atkada to izaziva p rob lem :
/ / : cuvanje/KaoNekakvaLista.java // A rr a y s .a s L is t () samo nagaa o kojem se tip u ra d i. import j a v a .u t il class c la s s c lass c la s s c la s s c lass Sneg {} Prsac extends Sneg {} Slab extends Prsac {} Krupan extends Prsac { } Krpe extends Sneg {} S lo ta extends Sneg { }

pu b lic c la s s KaoNekakvaLista { p ub lic s t a t ic void m a in (S trin g [] args) { List<Sneg> snegl = A r r a y s .a s L is t ( new K rp e (), new S l o t a ( ) , new P r s a c ( ) ) ; // Nee se p r e v e s t i: // List<Sneg> sneg2 = A r r a y s .a s L is t ( // new S la b ( ) , new K ru p a n (j);

Poglavlje 11: uvanje objekata

309

// Pre vo d ila c e s a o p titi: // found : j a v a . u t i 1. List<Prsac> // req uired : ja v a .u til.L is t< S n e g > // C o lle c tio n s .a d d A ll () nee se z b u n iti: List<Sneg> sneg3 = new A rrayList< Sn eg > (); C o lle c tio n s .a d d A ll (sneg3, new S l a b ( ) , new K ru p an O ); // Daemo smernicu // zadavanjem tip a List<Sneg> sneg4 = new S 1 a b (), new e k s p lic itn im argumenta: Arrays.< Sneg> asList( Krupan( ) ) ;

} } ///:Kada p okuava d a n ap rav i sneg2, Arrays.asList() im a sam o tip o ve klase Prsac, pa p ra vi List<Prsac>, a n e List<Sneg>, d o k CoUections.addAIl() lepo rad i je r iz p rv o g arg um e n ta zna koji je ciljni tip. Kao to vidite u p rim e ru o p rav ljenju liste sneg4, u sred in u Arrays.asList() m o ete staviti ,,sm ernicu koja e p rev o d io cu sao p titi stv a rn i ciljni tip rezu ltu ju eg tip a List koji pro izv odi Arrays.asList(). To se naziva ek sp licitn o zadavanje tipa argum enta. M ap e su k om p liko v an ije (v ideete), a Javina s ta n d a rd n a b ib lio tek a ne om o gu av a njiho vu a u to m a ts k u inicijalizaciju d ru gaije sem p rek o sadraja neke d ru g e M ape.

Ispisivanje sadraja kontejnera


Sadraj k o n te jn e ra se ispisuje lepo bez ikakve p o m o i, za razliku o d nizova gde m o ra te u p o tre b iti Arrays.toString( ). Evo p rim e ra koji isto v re m e n o ilu stru je i najvanije vrste k o n te jn e ra u Javi:
/ / : c u va n je / Isp isiv a n je K o n te jn e ra .ja va // Sadraj kontejnera se automatski is p is u je u it ljiv o m o b lik u . import j a v a . u t i 1 import s t a t ic n e t.m in d v ie w .u til. P r in t . * ; public c la s s Is p is iv a n je K o n te jn e ra { s t a t ic C o lle c tio n popuni(C ollection< String> k o le k c ija ) kolekci ja.add C 'pacov'1 ); kolekci ja.a d d C 'm a k a "); kolekci ja .a d d C 'k u e "); k o le k c ija .a d d ("k u e "); return k o le k c ija ;

}
s t a t ic Map popuni(Map mapa) { mapa.put C'pacov'1 , "Zb u n jen i" ) ; mapa.put("maka , "M ica'1 ); m apa.put("kue", " L e s i " ) ;

310

Misliti na Javi

m apa.put(''kue", B o b i"); return mapa;

}
p u b lic s t a t ic void print(popuni(new print(popuni(new p rin t(p o p u n i(new print(popuni(new print(popuni(new print(popuni(new p rin t(p o p u n i(new print(popuni(new m a in (S trin g [] args) { A rrayList< Strin g> ( ) ) ) ; L in k e d L is t< S tring> ( ) ) ) ; HashSet<String> ( ) ) ) ; TreeSet<String> ( ) ) ) ; LinkedHashSet<String> ( ) ) ) ; HashMap<String, String> ( ) ) ) ; TreeMap<String, String> ( ) ) ) ; LinkedHashMap<String, String> ( ) ) ) ;

}
} /* Is p is : [pacov, maka, kue, kue] [pacov, maka, kue, kue] [kue, maka, pacov] [pacov, maka, kue] {kue=Bobi, maka= M ica, pacov=Zbunjeni} {maka= Mica, kue=Bobi, pacov=Zbunjeni} {pacov=Zbunjeni, maka= M ica, kue=Bobi}

* ///:O v im su prik azane dve o sn o v n e k ategorije u Javinoj b iblioteci k o n te jn e ra . R azlik u ju se p o b ro ju elem enata koji se uv aju u svakoj lo k a d ji k o n tejn era. K ategorija C ollection uva sam o je d a n elem en t u svakoj lokaciji. O va k ategorija o b u h v ata interfejse: List (lista), koji uva g ru p u elem enata u zad ato m redosledu; Set (sk u p ), koji dozvoljava d o d av a n je sam o je d n o g e lem en ta iste v re d n o sti i Queue (red ), koji u m eta n je o b jek ata dozvoljava san io na je d n o m ,,kraju k o n tejn era, a va en je objek ata sam o na d ru g o m ,,k raju (za p o tre b e ovog p rim era , to je sam o d ru g i n ain p o sm a tra n ja sekvence, pa zato nije p rik a z an ). Mapa na svakoj lokaciji sadri dva objekta, klju i n jem u p rid ru e n u vrednost. Iz rezultata p ro g ram a vidite da p o d razu m ev an o ponaanje to se tie ispisivanja (izlaz m eto d e toString() svakog k o n tejn era) daje prilin o itljive rezultate. V rednosti objekata koji se uvaju u nekom o d k o n tejn era iz kategorije Collection ispisuju se u n u ta r uglastih zagrada, p ri em u su elem enti m e u so b n o razdvojeni zarezom . V rednosti o bjekata koji se uvaju u Mapi ispisuju se u n u ta r vitiastih zagrada, p ri em u su kljuevi i n jim a p rid ru e n e v red n o sti povezane zn ak o m jednakosti (kljuevi nalevo, v rednosti n ad esn o o d tog znaka). Prva m eto d a popuni() radi sa svim tip o v im a kategorije Collection, o d kojih svaki realizuje m e to d u add() za d o d av an je n o v ih elem enata. ArrayList i LinkedList su (o ig led n o ) tipovi k ategorije List, a iz rezu ltata p ro g ra m a vidite d a oba uvaju elem en te u p o re tk u u m etan ja . Razlika izm eu njih nije sam o u p erfo rm an sam a za o d re d e n e vrste o p eracija, nego LinkedList sadri vie operacija o d tip a ArrayList. O n jim a em o vie g o v o riti u n astavku poglavlja. HashSet, TreeSet i LinkedHashSet tipovi su k ategorije Set. R ezultat p ro g ra m a p o kazuje da svaki skup (Set) m oe sad rati sam o po jed an p rim e ra k id e n ti n ih elem enata, ali i da razliite realizacije sk u p o v a e lem en te skladite na razliite nain e. T ip HashSet skladiti elem en te na p rilin o k o m p lik o v an nain koji e biti detaljn ije o p isan u poglavlju D etaljno razm atranje kontejnera - zasad je d ovoljno znati da ta te h n ik a o m o g u av a naj-

Poglavlje 11 : uvanje objekata

311

b re vaenje elem enata, iako red o sled njegovog skladitenja izgleda b esm islen (esto je vano sa m o da li je objek at lan o d re en o g sk u p a iz kategorije Set, d o k redo sled elem en ata u to m sk u p u n em a znaaja). A ko je p o re d a k skladitenja vaan, m o ete u p o tre b iti TreeSet koji objekte uva u rastu e m p o re tk u p o re en ja, ili LinkedHashSet koji ih uva u re d o sle d u kojim su bili u m etan i. M ap a (koja se naziva i asocijativan niz) o m o g u av a p ro n alaen je o b jek ta p o m o u njegovog kljua, kao u jed n o stav n o j bazi p o d atak a . O b jek at p rid ru e n k lju u jeste njegova vrednost. U koliko im ate M a p u u kojoj su im e n a drava p rid ru e n i im e n im a n jih o v ih glavn ih g rad ova i elite d a saznate koji je glavni g ra d G ruzije, p retraiv an je ob avljate p o m o u kljua ,,G ruzija - gotovo kao da je u p ita n ju indeks niza. Z bog takvog p o n aan ja M ap a p rih v ata sam o po jed an p rim e ra k svakog kljua. Map.put(klju, vrednost) u m ee u m a p u v re d n o st (o n o to hoete d a u sk lad itite) i p rid ru u je je o re e n o m k lju u (p o m o u kojeg ete tu v re d n o st p ro n ai). Map.get(klju) daje v red n o st p ri ru e n u d a to m kljuu. U g o rn je m p rim e ru sa m o su d o davan i p aro v i k lju -v re d n o st, je r p retraiv an ja n ije bilo. To e b iti p o k a zan o kasnije. Im a jte u v id u da ne m o ra te zad ati veliinu M ap e (n iti se za to u o p te starajte), je r o n a svoju veliinu podeava a u to m atsk i. P o red toga, M ap e sam e u m e ju d a isp iu svoj sadraj, pri e m u se p rik azu ju p arovi k lju -v re d n o st. U M a p am a se kljuevi i v re d n o sti ne uvaju u o n o m p o retk u kojim su u m e ta n i, zato to realizacija tip a H a sh M a p u p o treb ljav a veom a brz alg o ritam koji o d re u je taj p oredak. U p rim e ru su u p o treb lje n a tri o sn o v n a tip a Mapa: HashMap, TreeMap i LinkedHashMap. Kao i HashSet, HashMap o b ezb edu je n ajb ru te h n ik u p retraiv an ja; takoe, ni ta m a p a ne uva svoje elem en te u lako razu m ljiv o m p o retk u . U m ap i tip a TreeMap kljuevi su p o re a n i u ra stu em p o re tk u , a u m a p i tip a LinkedHashMap u p o re tk u u m etan ja, p ri e m u je zad ran a b rz in a p retra iv a n ja tip a HashMap.

Veba 4: (3) N apravite generatorsku klasu koja vraa im ena (kao objekte tipa String) likova
vaeg o m iljeno g film a (ili Sneane i sedam p atuljaka ili Ratova zvezda, nije vano) svaki p u t kada pozovete next(), i vraa se na p o eeta k liste sa im en im a likova kada d o e d o n jen og kraja. U p o treb ite taj g e n erato r za p o p u n ja v an je o b in o g niza i po jed n o g p rim e rk a tipova ArrayList, LinkedList, HashSet, LinkedHashSet i TreeSet. Z atim ispiite sadraj svakog od tih kontejnera.

Liste
Liste uv aju elem en te u o d re e n o m p o retk u . Interfejs List kategoriji C ollection do aje vie m e to d a za u m e ta n je i uklan jan je elem enata iz sred ine Liste.
Postoje dva tip a Liste: O sn o v n i tip, ArrayList, izv rstan je za p ristu p a n je elem en tim a n a su m i n im red osled o m , ali je sporiji p rilik o m u m eta n ja i u k lan jan ja elem en ata iz sred in e Liste. T ip LinkedList o b ezb e u je o p tim a la n sekvencijalni p ristu p , a ni u m e ta n je i uldan jan je elem enata iz sred in e Liste ne kota m n o g o . LinkedList je relativ no sp o r p rilikom p ristu p a n ja elem en tim a n a su m in im red o sled om , ali su njegove m o g u n o sti vee nego o ne tip a ArrayList.

312

Misliti na Javi

U n a re d n o m p rim e ru uveem o b ib lio tek u typeinfo.pets, o p isa n u u poglavlju Podad o tipu u n astavku knjige. Ta b ib lio tek a sad ri h ije ra rh iju klasa Pet (k u n i lju b im ac) i neke alatke za n a su m i n o generisanje Pet objek ata. Z asad ne m o ra te zn ati sve p o jed in o sti, nego sam o da (1) p o sto ji klasa Pet i ra zn i n jen i p o d tip o v i, i d a (2) statin a m eto d a Pets.arrayList() vraa ArrayList p o p u n je n n a su m in o iza b ra n im Pet o bjektim a:
//: cu van je /O b e le z ja Lista .ja v a import ty p e in fo .p e ts .* ; import j a v a . u t i l .* ; import s t a t ic n e t.m in d v ie w .u til. P r i n t . * ; p u b lic c la s s O b elez jaL ista { pu b lic s t a t ic void m a in (S trin g [] args) { Random rand = new Random(47); List<Pet> pets = P e t s .a r r a y L is t ( 7 ) ; p r i n t ( " l : " + p e ts ); Hamster h = new H am ster(); p e ts .a d d (h ); // Automatski podeava svoju v e li in u p r in t ( " 2 : " + p e ts ); p r in t ( " 3 : " + p e ts .c o n ta in s (h )) ; p ets.rem o ve(h ); // U k la n jan je na osnovu objekta Pet p = p e ts .g e t(2 ); p r in t ( " 4 : " + p + " " + p e ts . in dex O f(p)) ; Pet cymric = new C ym ric (); p r in t ( " 5 : " + p e ts. in d ex O f(cym ric)) ; p r in t ( " 6 : " + p e ts. rem ove(cym ric)) ; // Mora b i t i tano odreeni o bjekat: p r in t ( " 7 : " + p e ts . rem ove(p)) ; p r in t ( " 8 : " + p e ts ); p ets.ad d(3 , new M o u se()); // Umee tamo gde pokazuje indeks p r in t (" 9 : " + p e t s ); List<Pet> sub = p e t s .s u b L is t ( l, 4 ); p r in t (" s u b L is t : " + s u b ); p rin t("1 0 : " + p e ts .c o n ta in s A l1(s u b )) ; C o lle c tio n s . s o r t (s u b ); // S o r tir a n je u mestu p r i n t ( s o rtira n a s u b L is t: " + s u b ); // Za metodu c o n ta in s A ll() poredak n ije vaan: p r i n t ( " l l : " + p e ts. c o n ta insA l1(s u b )) ; C o lle c tio n s .s h u ffle (s u b , rand ); // Izmeaj print("izm eana su b L is t: " + s u b ); p rin t("1 2 : " + p e ts. c o n ta insA l 1 (s u b )) ; List<Pet> kop ija = new A rra y L ist< P e t> (p e ts); sub = A rra y s . a s L is t (p e ts .g e t (1 ), p e ts .g e t (4 )) ; p rin t("s u b : " + su b ); k o p ija .r e ta i nAl 1 (s u b ) ; p r in t("1 3 : " + k o p ija ); kopija = new A rra y L ist< P e t> (p e ts); // Napravi novu kopiju k o p ija.rem o ve (2 ); // U klanjan je na osnovu indeksa p r in t("1 4 : " + kopij a ) ;

Poglavije I I : '

vanje objekata

31 3

kopi ja.rem oveAl 1 (s u b ); // U klanja samo objekte koji i.ario odgovaraju p rin t("1 5 : " + kopi j a ) ; k o p ij a . s e t ( l, new M o use()); // Ukloni jedan elemeni p r in t("1 6 : " + k o p ija ); k o p ija.ad d A l1(2, s u b ); // Umee l i s t u u sredinu p rin t("1 7 : " + k o p ija ); p r in t ("1 8 : " + p e ts .is E m p ty ()) ; p e t s .c le a r O ; // Ukloni sve elemente p rin t("1 9 : " + p e t s ) ; p rin t("2 0 : " + p e ts . is.Empty( ) ) ; p e t s .a d d A ll( P e t s .a r r a y L is t ( 4 ) ) ; p r i n t ( 21: " + p e ts ); O b je c t[] o = p e t s .t o A r r a y (); p rin t("2 2 : " + o [ 3 ] ) ; P e t[] pa = p ets.toA rray(new P e t [ 0 ] ) ; p rin t("2 3 : " + pa[ 3 ] . i d ( ) ) ;

)
} /* Is p is : 1: [R a t, Manx, Cymric, M utt, Pug, Cymric, Pug] 2: [R a t, Manx, Cymric, Mutt, Pug, Cymric, Pug, Hamstei 3: tru e 4: Cymric 2 5: -1 6: fa ls e 7: true 8: [R a t, Manx, M utt, Pug, Cymric, Pug] 9: [R a t, Manx, M utt, Mouse, Pug, Cymric, Pug] su b L is t: [Manx, Mutt, Mouse] 10: true so rtira n a su b L is t: [Manx, Mouse, Mutt] 11: true izmeana s u b L is t: [Mouse, Manx, Mutt] 12: true sub: [Mouse, Pug] 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: [Mouse, Pug] [R a t, Mouse, Mutt, Pug, Cymric, Pug] [R a t, Mutt, Cymric, Pug] [R a t, Mouse, Cymric, Pug] [R a t, Mouse, Mouse, Pug, Cymric, Pug] fa ls e [] true [Manx, Cymric, R at, EgyptianMau] EgyptianMau 14

* ///:Da histe svaki red rezultata m ogli da dovedete u vezu sa izvorn im ko do m , u p ro g ram u su naredb e p rin t n u m erisan e. Prvi red rezultata pokazuje prvobit- Listu k u nih ljubim aca, tj. objekata tipa Pet. Za razliku o d niza, Lista dozvoljava d od a\ j i uklanianie elem enata

314

Misliti na Javi

i nakon to je napravljena, p ri em u au to m atsk i podeava svoju veliinu. To je n jen a osnovn a vrednost: sekvenca koja se m oe m odifikovati. R ezultat dodavanja jed n o g objekta tipa Hamster vidite u red u 2 - ob jek at je d o d a t na kraj liste. M eto d o m contains() m o ete saz n ati d a li je o d re e n i elem en t deo Liste. U koliko o bjek at ho ete d a uk lonite, p ro sled ite njeg o v u referen cu m eto d i remove(). T akoe, ako im ate referencu nekog objekta, m e to d o m indexOf() m o ete sazn ati njegov in d ek s (red n i b ro j) u Listi, kao to se vidi iz red a 4 rezu ltata. M eto d a equals() (d eo k orenske ldase Object) u p o treb ljav a se za ispitivanje da li je o d red e n i elem en t d eo Liste, te za p ro n ala e n je in d ek sa elem en ta i u k lan jan je e lem en ta iz Liste na o sn o v u reference. Svaki o b jek at tip a Pet p o definiciji je jed in stv en ; a k i ako u listi ve p o sto je dva o b jek ta tip a Cymric, ako n a p ra v im n o v takav o b jek at i p ro sled im ga m eto d i indexOf(), n jen rezu ltat e b iti -1 (to znai d a taj n o v i nije p ro n a e n ), pa e i p okuaji u k lan jan ja tog objekta m e to d o m remove() v ra titi false (to zn ai da u k lan jan je nije uspelo). Za d ru g e klase, equals() se m o e dru g a ije d efinisati. P rim e ra radi, o bjekti tip a String (znak o vni nizovi) je d n a k i su u koliko je sadraj o b a zn ak o v n a niza id en tian . D akle, da ne bi bilo iznen a en ja, p o vedite ra u n a o to m e da se p o n aa n je Lista m en ja u zav isn o sti o d m eto d e equals(). Iz redova 7 i 8 rezu ltata vidi se d a je usp elo u k lan jan je o b jek ta koji tan o o d g o v ara jed n o m o b jek tu u Listi. E lem en t je m o gu e u m e tn u ti u sred in u Liste, kao to v idite iz reda 9 rezu ltata i koda koji m u p re th o d i, ali iz toga sledi zakljuak: za tip LinkedList, u m e ta n je i u k lan jan je iz sredin e liste jeftin a je o p eracija (sem u o v o m sluaju , k ad a se u sre d in u liste p ristu p a nasu m i n o ), ali za ArrayList to je sku pa o p eracija. Da li to znai da n ik a d a ne treb a u m e tati elem en te u sred in u ArrayListe i d a treb a p rei na LinkedList ini takvo neto zatreba? Ne - o to m e p ro sto tre b a da v o d ite rau n a, pa ako n a p rav ite m n o g o u m e ta n ja u sre d in u ArrayListe ia k o p ro g ram p o n e d a u spo rav a, m o d a je za to kriva vaa realizacija Liste (takva uska grla se najbolje p ro n alaze p ro fajlero m , kao to ete v ideti u d o d a tk u na lokaciji http://M indV iew .net/B ooks/B etterJava). O p tim iz acija je sloen p o sao i n ajbolje je ne baviti se n jo m d o k vas p ro g ra m n a to ne n atera (m a d a nije loe zn ati o em u se rad i). M eto d a subList() slui za pravljenje iseka vee liste, pa je p riro d n o da se d o b ija rezultat true kada se iseak p rosledi m e to d i containsAll() za tu veu listu. Z an im ljiv o je da pri to m p o re d ak nije vaan - u red o v im a 11 i 12 rezu ltata vid ite da na rezu itat m e to d e containsAll() ne utie po zivanje m eto d a C ollections.sort() (za so rtira n je ) i Collections.shuffle() (za m ean je e lem en ata) na p o d listi sub. M etoda subList() pro izv o d i listu povezanu sa o rig in a ln o m listom . Z ato se izm en e u vraen o j listi od raav aju na o rig in alnoj, i o b rn u to . M eto d a retaiuAll() zapravo spro v o d i o p e ra c iju preseka skupova". U ovom sluaju, o na u o b je k tu kopija zadrava sve elem en te koji su i u p o d listi sub. I o p et, rezu ltu ju e po naanje se m en ja u zavisnosti o d m eto d e equals(). C etrn a e sti red ispisa p rik a z u je rezu ltat u k lan jan ja elem en ta na o sn o v u njegovog indeksa, to je jed n o stav n ije nego u k lan jan je na o sn o v u reference objekta, p o to vas indeksi oslob adaju brige o p o n a a n ju m e to d e equals(). I p o n aan je m eto d e removeAll() zavisi o d m e to d e equals(). Kao to joj i englesko ime kae, o n a iz Liste uklanja sve o b jek te koji su joj p ro sle en i u a rg u m e n tu sub.

Poglavlje I I : uvanje objekata

315

M eto d a set() je loe n azv an a, je r ju je m og ue p o m e a ti s klasom Set - tu bi bolji naziv bio ,,replace (zam en i), p o to o n a e lem en t d ato g in d ek sa (p rv i a rg u m e n t) zam en juje d ru gim arg u m e n to m . S ed am n aesti red ispisa p rik az u je da za Liste p o sto ji p rek lo p ljen a m eto d a addAll() koja slui za u m e ta n je nove liste u sred p rv o b itn e , u m esto o b in o g d o dav an ja na kraj p rv o b itne o n o m m e to d o m addAll() koja je deo klase Collection. R edovi ispisa 18 -2 0 p o k a z u ju rezultate m eto d a isEmpty() i clear(). R edovi ispisa 22 i 23 p o k a z u ju kako svaki k o n te jn e r (o bjekat tip a Collection) m eto d o m toArray() m o ete p re tv o riti u niz. Ta m e to d a je preklop ljena; verzija koja ne p rim a arg u m e n te vraa niz elem en ata tip a Object, ali ako prek lo pljen oj verziji p ro sled ite niz ciljnog tip a, o n a e n a p ra v iti niz to g tip a (u ko liko o n zadovolji p ro v eru tip ov a). Ako je niz pro sle e n kao a rg u m e n t p re m a li za sk laditenje svih ob jekata u Listi (kao to je ovde sluaj), m e to d a toArray() e n a p ra v iti n o v n iz od g o v araju e veliine. Pet ob jek ti im aju m e to d u id() - v id ite d a je p o zv an a za je d a n o d o b jek ata u rezu ltu ju e m nizu.

Veba 5: (3) Prepravite p ro g ra m ListFeatures.java tako da um esto objekata tipa Pet u p o trebljava cele brojeve (setite se au to m atsk o g pakovanja!) i objasnite sve razlike u rezultatim a.

Veba 6: (2) P rep ravite p ro g ra m ListFeatures.java tako d a u m esto objek ata tip a Pet u p o treb ljav a zn ak o vne nizove (o b jek t tip a String) i o b jasn ite sve razlike u rezultatim a. Veba 7: (3) N ap rav ite klasu, a zatim inicijalizovan niz o b jek ata te klase. P o p u n ite tim n izo m jed n u Listu. N aprav ite p o d sk u p liste m e to d o m su bL ist( ), a zatim ga u k lo n ite iz nje.

Iteratori
U svakoj kon tejn ersk o j klasi m o ra p o sto jati n ain d a se elem enti d o d a ju i izvade. O sn o v n i za d atak k o n te jn e ra i jeste da n eto sadri. U klasi List, o b jek ti se u m e u m eto d o m a d d (), a m eto d a g e t ( ) je jed an o d n ain a da se pro itaju. Ako p o n ete da razm iljate na viem niv o u o k o n tejn ersk o j biblioteci, pojavljuje se jed a n n e o statak : m o ra te ta n o zn ati tip k o n tejn era d a biste ga koristili. To na prv i po gled ne izgleda loe, ali ta ako n ap iete k o d za Listu, a kasnije o tk rijete da bi isti k o d treb alo da p rim e n ite na sk u p (Set)? Ili, p rim e ra radi, elite da nap iete generiki kod koji ne zna (ili m u nije b itn o ) s kojim tip o m k o n tejn era radi, kako biste m ogli da ga koristite s razliitim tip o v im a k o n te jn e ra a d a ga pri to m n e piete ponovo? O vakva apstrak cija se p o stie k o rien je m iteratora (i to je p ro je k tn i obrazac). Ite ra to r je o bjek at iji je zad atak da se p o m e ra kroz niz o b jek ata i d a izabere svaki o bjek at u to m n izu, p ri em u p ro g ra m e r k lijent ne zna (ili m u nije vano) kakva je s tru k tu ra tog niza. P ored toga, ite ra to r se o b i n o naz.iva lak o b jek at (engl. light-w eight), je r koristi m alo resursa. Z b o g toga ete za iterato re esto v iati naizgled u d n a o g ran ien ja; na p rim er, Javin Iterator m oe da se kree sa m o u je d n o m sm e ru . Sa ite ra to ro m ne m oete b o gzn a ta da rad ite, o sim da:

1. Z a tra ite o d k o n te jn e ra da vam m e to d o m iterator( ) p ro sledi Iterator. D obijeni Iterator je sp re m a n da v ra ti prv i elem en t niza posle prv og poziva njegove m eto d e n e x t(). 2. D obijete sledei o b jek at u n izu p o m o u m e to d e n e x t().

316

Misliti na Javi

3 . U stanovite da li im a j o ob jekata u n iz u p o m o u m e to d e h a s N e x t( ). 4. U k io n ite posled nji elem en t koji je ite ra to r v ra tio p o m o u m e to d e r e m o v e ( ). D a b ism o videli kako o n radi, o p e t e m o u p o tre b iti kJasu P et i n jen e m e to d e , o p isan e u poglavlju Podaci o tipu:
//: c u va n je / Je d n o s ta v n a lte ra c ija .ja v a import ty p e in fo .p e ts .* ; import j a v a . u t i l .* ; p ub lic c lass Je d n o s ta v n a lte ra c ija { pu b lic s t a t ic void m a in (S trin g [] args) { List<Pet> Ijub im ci = P e t s .a r r a y L is t (1 2 ) ; Iterator< Pet> i t = lj u b i m c i. it e r a t o r ( ) ; w h ile (it.h a s N e x t ()) { Pet p = i t . n e x t ( ) ; S y s te m .o u t.p rin t(p .id () + + p + " ");

}
S y s te m .o u t.p rin tln (); // Je d n o s ta v n iji p ris tu p , kada j e mogu: fo r (P e t p : Iju b im c i) S y s te m .o u t.p rin t(p .id () + + p + " "); S y s te m .o u t.p rin tln (); // It e r a t o r moe i da u klan ja elemente: i t = 1ju b im c i. i t e r a t o r ( ) ; f o r ( i n t i = 0 ; i < 6 ; i++) { it .n e x t (); it .r e m o v e ();

}
S y s te m .o u t.p rin tln (l ju b im c i) ;

}
} /* Is p is : 0:Rat l:Manx 2:Cymric 3:Mutt 4:Pug 5:Cymric 6:Pug 7:Manx 8:Cymric 9:Rat 10:E g yp tianMau ll:H am ster 0 :Rat l:Manx 2:Cymric 3:Mutt 4:Pug 5:Cymric 6:Pug 7:Manx 8:Cymric 9:Rat 10:E g yp tianMau ll:H am ster [Pug, Manx, Cymric, R at, EgyptianMau, Hamster]

* ///:Uz ite ra to r ne m o ra te da b rin e te o b ro ju ele m e n ata u k o n tejn e ru . U m esto vas, o to m e vode ra u n a m e to d e h a s N e x t( ) i n e x t ( ). V idite da je foreach sin tak sa p rik la d n ija za je d n o sta v n o k re tan je u n a p re d kroz L istu, ukoliko ne p okuavate d a m o d ifik u jete sam L ist objekat. Ite r a to r takoe uklanja p o sled nji ele m e n t pro izv ed en m e to d o m n e x t(), to znai da n e x t() m o ra te pozvati p re nego to pozovete rem o v e().'1

rem ov e() spada u takozvane o p cio n e m etod e (im a ili jo), to znai da je ne m o raju realizovati sve realizacije Ite ra to ra . O tom e govorim o u poglavlju Detaljno raztnatranje kontejnera. Kako svi stand ard ni kon tejn eri Java biblioteke realizuju rem o v e(), o tom e ne m o rate da b rin ete fdok ne dodete do sp o m e n u to g poglavlja).

Poglavlje I 1; uvanje objekata

317

K orienje k o n tejn e ra i k re tan je k ro z njega d a b i se izvravale operacije n ad svakim e le m e n to m v eo m a je m o n a te h n ik a i zapaziete d a se esto p rim e n ju je u ovoj knjizi. P ogledajm o sada kako izgleda m eto d a ispisiS ve() koja rad i sa svim v rstam a ko ntejnera:
// : c u v a n je / Ite ra cija K ro z S ve V rs te K o n te jn e ra .ja va import ty p e in fo .p e ts .* ; import j a v a . u t i l .* ; p u b lic c la s s Ite ra c ija K ro z S ve V rste K o n te jn e ra { p u b lic s t a t ic void is p is iS v e (Ite r a to r < P e t> i t ) w h ile (it.h a s N e x t ()) { Pet p = i t . n e x t ( ) ; S y s te m .o u t.p rin t(p .id () + + p + 1 1 ");

}
S y s te m .o u t .p r in tln ();

}
p ub lic s t a t ic void m a in (S trin g [] args) { ArrayList< Pet> ljubim ci = P e t s . a r r a y L i s t ( 8 ) ; LinkedList<Pet> 1jubiinci LL = new LinkedList< Pet> (1 ju b im ci) ; HashSet<Pet> ljubim ciH S = new HashSet< Pet> (ljubim ci) ; TreeSet<Pet> ljubim ciTS = new T reeSet< Pet> (lju b im ci) ; is p i si S v e (lju b im c i. i t e r a t o r ( ) ) ; i s p is iS v e (l jubim ci L L .i t e r a t o r O ) ; is p is iS v e ( lj u b im c iH S . it e r a t o r ( ) ) ; is p is iS v e ( lj u b im c iT S .it e r a t o r ( )) ;

}
} /* Ispis: 0:Rat l:Manx 2:Cymric 3:Mutt 4:Pug 5:Cymric 6:Pug 7:Manx 0:Rat l:Manx 2:Cymric 3:Mutt 4:Pug 5:Cymric 6:Pug 7:Manx 4:Pug 6:Pug 3:Mutt l:Manx 5:Cymric 7:Manx 2:Cymric 0:Rat 5:Cymric 2:Cymric 7:Manx l:Manx 3:Mutt 6:Pug 4:Pug 0:Rat

* ///:U oili ste d a m eto d a ispisiS ve() nita ne zna o vrsti sekvence kroz koju prolazi, a to pokazuje p rav u sn ag u Ite ra to ra : m o g u n o st da razdvoji o p eraciju pro laska kroz sekvencu o d njen e stru k tu re . Z ato se k atk a kae da ite ra to ri objedinjuju pristup kontejneritna. V eba 8: (1) P repravite vebu 1 tako da se za k reta n je kroz L istu u p o tre b lja v a Ite ra to r kada se pozove skoci(). V eba 9: (4) P repravite p ro g ram u n u tra sn je k la se /S e k v e n c a .ja v a tako da S ekvenca radi sa Ite ra to ro m u m esto sa S electo ro m . V eba 10: (2) P repravite vebu 9 iz poglavlja P olim orfizam tako da se G lo d a ri uvaju u Array L isti i da se za k retan je kroz niz G lo d a ra u p o tre b ljav a Ite ra to r. V eba 11: (2) N apiite m e to d u koja u p o treb ljav a I te r a to r za p ro lazak kroz k o n tejn er (objekat tipa C o lle c tio n ). M e to d o m to S trin g O ispiite sadraj svih o b jekata u k o ntejneru . P o p u n ite o b je k tim a sve vrste k o n tejn era i n a svaki o d njih p rim e n ite svoju m eto d u .

318

Misliti na Javi

Listlterator
Za liste p o sto ji n a p red n iji iterato r, Listlterator. D ok se Iterator m oe p o m e ra ti sam o u n a p re d , Listlterator je d v o sm eran . T akode, u m e da pro izv ed e indekse sledeeg i p re th o d n o g elem e n ta u o d n o su n a m esto n a koje ite ra to r p ok azu je u listi, kao i d a m e to d o m set() z a m e n i p o sled n ji e lem en t koji je p osetio. P ozivom m e to d e listIterator() nap rav iete Listlterator koji p ok azu je n a p o eta k Liste, a zadavanjem arg u m e n ta n (kao u listlterator(n)) nap ra v ie te Listlterator koji p o inje p o k azu ju i n a in d eks n u listi. Evo p rim e ra u k o jem su p o k az an e sve n av ed ene m o gu n osti:
//: c u v a n je / Ite ra c ij a L i s t e . ja va import ty p e in fo .p e ts .* ; import j a v a . u t i l .* ; p u b lic c la s s It e r a c i ja L is t e { p u b lic s t a t i c void m a in (S trin g [] args) { List<Pet> 1jubimci = P e t s .a r r a y L is t ( 8 ) ; Lis tIte ra to r< P e t> i t = 1ju b im c i.1 i s t I t e r a t o r ( ) ; w h i1 e (it.h a s N e x t()) S y s t e m .o u t .p r in t(it.n e x t () + " , + it.n e x tln d e x () + " , " + it.p r e v io u s In d e x () + " ; " ) ; S y s te m .o u t .p r in tln (); // Unazad: whi1e ( i t.h a s P re v i ous( ) ) S y s t e m .o u t .p r in t (it .p r e v io u s ( ).id ( ) + " " ) ; S y s te m .o u t .p r in tln (); S y s te m .o u t.p rin tln (lju b im c i); i t = 1ju b im c i.1 i s t I t e r a t o r ( 3 ) ; w h ile (it.h a s N e x t ()) { it .n e x t (); i t . s e t ( P e t s . randomPet( ) ) ;

}
S y s te m .o u t.p rin tln (lju b im c i);

}
} /* Is p is : Rat, 1, 0; Manx, 2, 1; Cymric, 3, 2; Mutt, 4, 3; Pug, 5, 4; Cymric, 6,

5;
Pug, 7, 6; Manx, 8, 7; 7 6 5 4 3 2 1 0 [R a t, Manx, Cymric, Mutt, Pug, Cymric, Pug, Manx] [R a t, Manx, Cymric, Cymric, Rat, EgyptianMau, Hamster, EgyptianMau]

* ///:M e to d o m Pets.randomPet() zam e n jen i su svi o bjekti tip a Pet u Listi o d lokacije 3 u n ap re d .

Veba 12: (3) N ap rav ite i p o p u n ite je d n u celo b ro jn u listu (List<Integer>). N apravite d ru g u c e lo b ro jn u listu iste veliine i u p o tre b ite Listlteratore za itan je elem en ata iz prve Liste i u m e ta n je u d ru g u o b rn u tim red osled om . (O vaj zadatak bi treb alo da reite na vie naina .)

Poglavlje 11: uvanje objekata

319

Ulanana lista
I klasa LinkedList realizuje osn o v n i interfejs List kao ArrayList, ali o d re e n e o p eracije (u m e ta n je u sre d in u Liste i uklan jan je iz nje) obavlja efikasnije n ego ArrayList. S d ru g e stra n e , m an je je efikasna za o p eracije p ristu p a n ja nesekvencijalnim red o sled o m . LinkedList d o d aje i m e to d e koje o m o g u av aju d a je u p o tre b im o kao stek, k ao red za ekanje (Queue) iii d v o stra n i red za ekanje (engl. double-ended queue, deque). N eke o d ovih m e to d a su p se u d o n im i (alijasi) ili m e d u so b n e n e z n a tn e varijacije, d a bi se o b ila im en a p o z n ata u k o n tek stu o d red en e u p o tre b e (n a ro ito u re d o v im a ek an ja). N a p rim er, getFirst() i elem ent() iden tin e su - o n e vraaju elo (p rv i elem en t) liste, a da ga ne u k lo n e, i gen eriu izuzetak NoSuchElementException ako je Lista p razn a. M eto d a peek() je n jihov a varijacija koja vraa null ako je lista p razn a. Id e n ti n e su i m eto d e removeFirst() i remove() o n e u k lan jaju i v raaju kao rezu ltat elo liste, i g eneriu izuzetak NoSuchElementException ako je lista p raz n a, d o k n jih ov a varijacija poll() 11 to m sluaju vraa null. addFirst() um ee elem en t n a p o etak liste. offer() je ista kao add() i addLast(). Sve o n e d o d aju e lem en t na rep (kraj) liste. removeLast() uklanja i vraa poslednji elem en t liste. N ared n i p rim e r p ok azu je o snovne slinosti sp o m e n u tih m eto d a i razlike iz m e d u njih. U n je m u se ne ponavlja p o n aan je p rik azan o u p ro g ra m u MogucnostiListe.java:
//: cuvan je/M eto deK laseLin kedlist.java import ty p e in fo .p e ts .* ; import j a v a . u t i l .* ; import s t a t ic n e t.m in d v ie w .u til. P r in t . * ; pub lic c la s s M etodeKlaseLinkedList { p u b lic s t a t ic void m a in (S trin g [] args) { Lin k e d L ist<Pet> Ijubim ci = new L in k e d L is t< P e t> (P e ts .a rra y L is t( 5 ) ) ; p r i n t ( l ju b im ci) ; // Id e n tin e : p r i n t ("1 ju b im c i. g e t F ir s t ( ) : " + 1ju b im c i. g e t F i r s t ( ) ) ; p r i n t ( " l jubim ci .e le m e n t(): 1 1 + 1jubim ci .e le m e n tO ); // R azlik e samo u r e a k c iji na praznu li s t u : p r in t ( " lj u b im c i. peek( ) : " + lju b im c i. peek( ) ) ; // Id e n ti n e ; u k lan jaju i vraaju prvi element: p r i n t ( " l jubim ci .rem o ve(): " + lju b im c i.re m o ve O ); p r i n t ("1 ju b im c i. rem oveFirst( ) : " + 1ju b im c i. rem o veFirst( ) ) ; // R azlik e samo u r e a k c iji na praznu li s t u : p r i n t ( " l ju b im c i. p o l1( ) : " + 1ju b im c i.p o ll( ) ) ; p r in t ( lju b im c i) ; 1ju b im c i.add First(new R at( ) ) ; p r in t (" P o s le ad d Fir s t ( ) : " + lju b im c i); 1ju b im c i. o f f e r ( P e t s . randomPet( ) ) ; p r in t (" P o s le o f f e r ( ) : " + lju b im c i);

320

Misliti na Javi

1ju b im ci.a d (P ets.ran d o m Pet()) ; p rin t("Po s 1 e a d d (): " + lju b im c i); 1jubim ci.addLast(new H a m s te r()); p r in t("P o s le a d d L a st(): " + lju b im c i); p r i n t ( " l jubim ci .re m o v e L a st(): 1 1 + 1ju b im c i. removeLast ( ) ) ;

}
} /* Is p is : [R a t, Manx, Cymric, Mutt, Pug] 1jubim ci . g e t F i r s t ( ) : Rat 1jubim ci ,e le m e n t(): Rat 1ju b im c i.p e e k (): Rat lju b im c i.re m o ve (): Rat 1ju b im c i.re m o v e F irs t(): Manx 1ju b im c i. pol 1 ( ) : Cymric [M utt, Pug] Posle a d d F ir s t (): [R at, M utt, Pug] Posle o f f e r ( ) : [R at, Mutt, Pug, Cymric] Posle a d d (): [R at, Mutt, Pug, Cym ric, Pug] Posle a d d La st(): [R at, Mutt, Pug, Cymric, Pug, Hamster] 1ju b im ci.re m o v e La st(): Hamster

* ///:R ezultat m etode Pets.arrayList() p ro sle u je m o k o n s tru k to ru liste LinkedList da bism o je p o p u n ili. U d o k u m e n ta c iji o in terfe jsu Queue videete m eto d e elem ent(), offer(), peek(), poll() i remove() koje su klasi LinkedList d o d a te da bi ta klasa m ogla da b u d e reaIizacija red a ekanja. K om pletni p rim e ri redova ekanja dati su u n astavku poglavlja.

Veba 13: (3) U p rim e ru unutrasnjeklase/UpravljacStaklenika.java, klasa Upravljac u p o treb ljav a listu ArrayList. Isk o ristite u m esto nje listu LinkedList, a za k ru en je kroz sk u p dogaaja p rim e n ite Iterator. Veba 14: (3) N apravite p ra z n u listu LinkedList<Integer>. U sred in u Liste d o d ajte Integere pomou Listlteratora.

Pravljenje steka od ulanane liste


Stek se p o n ek ad naziva LIFO k o n te jn e r (engl. last-in, first-out, poslednji koji ue, prvi izlazi). To znai da o n o to p o sled n je stav ite na stek (za to se tra ic io n a ln o koristi o p eracija p ush) prvo m oete da sk in ete s njega (za to se tra d ic io n a ln o koristi op eracija pop). Stek se esto p o re d i sa sto g o m - p o sled n ji naviljak sena koji bacite na v rh stoga, prv i ete sk in u ti s njega. Klasa LinkedList im a m eto d e koje d ire k tn o realizuju stek, pa u m esto da p rav ite poseb n u klasu steka, m oete u p o tre b iti u la n a n u listu. U n ekim sluajevim a ipak je bolje n a p ra v iti klasu steka:
//: n e t/m in d view /u til/Sta ck .java // P r a v lje n je steka od objekta tip a L in k e d L ist. package n e t.m in d v ie w .u til; import ja v a .u t il.L in k e d L is t ;

Poglavlje 1I : uvanje objekata

321

p u b lic c la s s Stack<T> { p riv a te LinkedList<T> s k la d is te = new L i nkedList<T> (); p u b lic void push(T v) { s k la d is te .a d d F ir s t ( v ) ; } p u b lic T peek() { return s k l a d i s t e . g e t F i r s t ( ) ; } p u b lic T pop() { return s k la d is te .re m o v e F irs t( ) ; } p u b lic boolean empty() { return s k la d is te .is E m p ty ( ) ; } p u b lic S trin g to S trin g O { return skl adi s te . to S tr i ng ( ) ;

} ///:O v im sm o dali najjed n o stav n iji p rim e r definicije generike klase. O n o <T> n a k o n im e n a klase, k azuje p rev o d io cu d a e ovo b iti pa ra m etrizo va n i tip, i d a je p a ra m e ta r tip a - k o ji e b iti zam e n jen stv a rn im tip o m kada klasa b u d e u p o tre b lje n a - o n o T. U su tin i, to k azu je d efiniem o Stack koji uva objekte tip a T. Taj Stack se realizuje u la n a n o m listo m kojoj je tak o e reen o da uva tip T. O b ra tite p an ju na to d a p u sh ( ) u zim a o b jek at tip a T koji v raaju p e e k ( ) i p o p ( ). M eto d a p e e k ( ) vraa najvii elem en t a d a ga ne skida s v rh a steka, d o k ga p o p ( ) u k lan ja i vraa. A ko elite d a se o g ran i ite sam o n a fu n k c io n a ln o st steka, nasleivanje nije p rik la d n o , zato to bi d a lo klasu sa svim o stalim m e to d a m a u lan an e liste (videete u p o glavlju D etaljno razm atranje kontejnera d a su tak v u g reku n ap rav ili p ro je k ta n ti b iblioteke Java 1.0 u klasi java.util.Stack). Evo je d n o sta v n o g p rim e ra ove nove klase Stack:
//: cu van je/Stek T est.ja va import n e t.m in d v ie w .u til.* ; p u b lic c la s s StekTest { p u b lic s t a t ic void m a in (S trin g [] args) { Stack<String> stek = new S tack < Strin g > (); fo r (S t r in g s : "Moj pas ima buve". s p li t (" ) ) s t e k .p u s h (s ); w hi1e ( ! stek.em pty( ) ) System .out. p r i n t ( s t e k .pop() + " " ) ;

}
} /* I spi s : buve ima pas Moj

* ///:U koliko u svom k o d u h o ete da u p o tre b ite ovu klasu Stack, m o raete p o tp u n o da specificiratc n jen paket - ili p ro m e n ite im e klase kada je b u d e te pravili; u p ro tiv n o m , vero v a tn o ete izazvati suk o b s klasom Stack iz pak eta java.util. P rim e ra radi, ukoliko u g ornji listing uvezem o ja v a .u til.* , m o ra e m o da u p o treb ljav a m o im en a p aketa kako b ism o spreili d v o sm islen o st im en a i sukobe:
//: cuvanje/StekSukob.java import n e t.m in d v ie w .u til.* ; p u b lic c la s s StekSukob { p u b lic s t a t ic void main (S tr in g [] args) { n e t.m indview .u t i 1. Stack< String> stek =

322

Misliti na Javi

new n e t.m in d v ie w .u til.S ta c k < S trin g > (); f o r (S tr in g s : "Moj pas ima b u ve". s p l i t (" " ) ) s te k .p u s h (s ); w h ile (!s te k .e m p ty ()) Sy ste m .o u t.p rin t(s te k .p o p () + " " ) ; S y s te m .o u t.p rin tln (); ja v a .u til.S ta c k < S trin g > stek2 = new ja v a .u til.S ta c k < S tr in g > (); fo r (S tr in g s : "Moj pas ima b u v e ".s p li t (" " ) ) ste k 2 .p u s h (s ); whi1e ( ! stek2 . empty( ) ) Sy ste m .o u t.p rin t(stek 2 .p o p () + " " ) ;

}
} /* Is p is : buve ima pas Moj buve ima pas Moj

* ///:O b e klase Stack im aju isti interfejs, ali u p ak e tu java.util ne p o sto ji zaje dn iki interfejs Stack - verovatno zato to je o rig in a ln a, loe p ro jek to v an a klasa java.util.Stack u Javi 1.0 usvojila to im e. Iako java.util.Stack p osto ji, LinkedList p ravi bolji stek, p a p re d n o s t treb a dati p ristu p u o stv aren o m u pak etu net.mindvievv.util.Stack. Izb o r realizacije klase Stack kojoj treb a dati p re n o s t in oete k o n tro lisa ti eksp licitn im uvozom :
import n e t.m in d v ie w .u til.S ta c k ;

Sada e svako upu iv an je na klasu Stack izabrati verziju net.m indview .util, a da biste izabrali java.util.Stack, m o ra te u p o tre b iti p o tp u n o im e koje o b u h v a ta i im e paketa.

Veba 15: (4) U p ro g ram sk im je z i m a stekovi se esto u p o treb ljav aju za izraun avan je v red n o sti izraza. Izrau n ajte sledei izraz koristei paket net.m indview.util.Stack, gde + znai stavite n a stek sledee sIovo, a - zn ai skinite v rh steka i ispiite ga: - - + r + u + l+ e + s ,,+ U + n + c t e + r + t + a -+ i-+ n + t+ y ---- 1

Funkcionalnost skupa
Skup (Set) ne m o e da sadri vie o d je d n e in stan ce v re d n o sti objekta. U k oliko p ok u ate da d o d ate vie od je d n e instance ekviv alen tno g objekta, Set e spreiti d u p liran je . Set se najee u p otreb ljav a za u tv rd iv an je p rip a d n o sti, pa je lako saznati da li je o d re e n i o b jek at u o d re d e n o m sk u p u . Z ato je za Set pretraiv an je o b i n o najvanija o p eracija, pa se najee b ira realizacija HashSet, o p tim iz o v an a za b rz o pretraiv anje. Set (sku p ) im a isti interfejs kao Collection, to znai da nem a d o d a tn ih fu nk cija kao to im aju dve razliite liste. S kup je isti kao kolekcija, sam o se razliito p o n aa. (O vo je p rim e r savrenog korienja n asledivanja i p o lim o rfizm a: za izraavanje d rug aijeg pon aanja.) Set u tv r u je p rip a d n o st na o sn o v u ,,v red n o sti objekta. O b jan je n je o d ega se sastoji v re d n o st objekta sloeno je, kao to ete v ideti u poglavlju D etaljno razm atranje kontejnera.

Poglavlje I 1: uvanje objekata

323

Sledei p rim e r pokazuje po n aan je sk u p a HashSet sa c elo b ro jn im (Integer) objektim a:


//: cu van je/Skup C elihBrojeva.java import ja v a . u t i l p ub lic c ia s s SkupCelihBrojeva { p u b lic s t a t ic void m a in (S trin g [] args) { Random slu cajan = new Random(47); Set<Integer> skupcelihb = new H ashSet< Integer> (); f o r ( i n t i = 0; i < 10000; i++) sk u p c e lih b .a d d (s lu c a ja n .n e x tln t(3 0 )); S y ste m .o u t.p rin tln (s k u p c e lih b );

1
} /* Is p is : [15, 8, 23, 16, 7, 22, 9, 21, 6, 1, 29, 14, 24, 4, 19, 26, 11, 18, 3, 12, 27, 17, 2, 13, 28, 20, 25, 10, 5, 0]

* ///:U skup je d o d a to deset hiljada slu ajnih b ro jev a izm e u 0 i 29, pa je lako zam isliti kako svaki o d tih b rojeva im a m n o g o d u p lik ata. Pa ipak, p rilik o m ispisa se vidi d a skup sadri sam o po je d n u in stan cu o d re e n e v red n o sti. P rim etili ste da brojevi u rezu ltatu n em aju sm isleni p o red ak . U zrok to m e je tran sfo rm isan je kljua (engl. hashing), koje HashSet u p o treb ljav a rad i vee b rzin e - o n o je o b jan jen o u poglavlju D elaljno razm atranje kontejnera. R edosled koji o drava HashSet razlikuje se od redosleda u TreeSet ili LinkedHashSet, p o to svaka realizacija skladiti elem en te n a razliit nain. TreeSet ih uva u re en e u c rv e n o -c rn o j s tru k tu ri stabla, d o k HashSet p rim e n ju je tra n sfo rm isan je kljua, p ro je k to v an o specijalno rad i b rzo g pronalaenja. I LinkedHashSet up o trebljav a je u c rv en o -crn o j s tru k tu ri stabla, d o k HashSet p rim e n ju je tran sfo rm isan je kljua rad i brzog p ro n ala e n ja , ali n a izg led p o m o u u lan a n e Iiste od rava elem en te u p o re tk u u m etan ja. A ko ho ete da rezultati b u d u u re en i, m o ete u p o tre b iti TreeSet u m esto HashSeta:
//: cuvanje/UredjenSkupCelihBrojeva.ja va import j a v a . u t i l .* ; pu b lic c la s s UredjenSkupCelihBrojeva { p ub lic s t a t ic void m a in (S trin g [] args) { Random slu cajan = new Random(47); SortedSet<Integer> skupcelihb = new T reeSet< In teg er> (); f o r ( i n t i = 0; i < 10000; i++) sk u p c e lih b .a d d fs lu c a ja n .n e x tln t(3 0 )); System .out. pri n tln (s k u p c e lih b );

}
} /* Is p is : [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29]

* ///:-

324

Misliti na Javi

M e u n aje im o p era c ija m a koje ete obavljati jeste u tv r iv an je p rip a d n o sti sk u p u p o m o u m e to d e contains(), ali p o sto je i operacije koje e vas p o d se titi na Venove dijag ram e koje ste m o d a uili u o sn o v n o j koli:
// : cuvanje/O peracijeSaSkupovim a.java import j a v a . u t i l .* ; import s t a t i c n e t.m in d v ie w .u til. P r i n t . * ; p u b lic c la s s OperacijeSaSkupovima { p u b lic s t a t i c void m a in (S trin g [] args) { Set<String> skupl = new H ashSet< String> (); C o lle c tio n s .a d d A ll(s k u p l, " A B C D E F G H I J K L ".s p lit(" " ) ) ; sk u p l.a d d ("M "); p r in t (" H : " + s k u p l.c o n ta in s (" H ")); p r in t (" N : " + s k u p l.c o n ta in s (" N ")); Set<String> skup2 = new H ashSet< String> (); C o lle c tio n s .a d d A ll(s k u p 2 , "H I J K L " . s p l i t ( " " ) ) ; p rin t("sk u p 2 deo skupal: 1 1 + sk u p l.co n tain sA U (skup2)) ; sk u p l.rem oveC 'H "); p r in t("s k u p l: " + s k u p l); p rin t("sk u p 2 deo skupal: " + sk u p l.c o n ta in sA ll (skup2)) ; sk u p l.rem o veA ll(sk u p 2 ); p rin t("sk u p 2 uklonjen iz skupal: " + sk u p l); C o lle c tio n s .a d d A ll(s k u p l, "X V Z " . s p l i t ( " " ) ) ; p r i n t ( " 'X Y Z' dodati skupul: " + skupl) ;

}
} /* Is p is : H: true N: fa ls e skup2 deo skupal: true sk u p l: [D, K, C, B, L, G, I , M, A, F, J , E] skup2 deo skupal: fa ls e skup2 uklonjen iz skupal: [D, C, B, G, M, A, F, E] 'X Y Z' dodati skupul: [Z, D, C, B, G, M, A, F, Y, X, E]

* ///:Im e n a m e to d a lako e ra z u m e ti o n i koji zn aju engleski. Im a ih jo nekoliko i njih ete nai u d o k u m e n ta c iji JD K-a. Lista je d in stv en ih ele m e n a ta u m e d o b ro d a p oslui. N a p rim er, recim o kako h oete da ispiete sve rei d ato tek e OperacijeSaSkupovima.java. Za o tv ara n je i uitavanje dato tek e u skup u p o tre b i e m o uslu n i p ro g ra m net.mindview.TextFile (p red stav iem o ga u nastavku knjige):
// : cu van je /Je d in s tve n e R e c i. ja va import j a v a . u t i l .* ; import n e t.m in d v ie w .u til.* ;

Poglavlje 11: uvanje objekata

325

p u b lic c la s s Jed in stven eR e ci { p u b lic s t a t i c void m a in (S trin g [] args) { Set<String> re c i = new TreeSet<String>( new T e x tFile ("O p e ra c ije Sa Sk u p o vim a .ja va ,,> "\W + ")); S y s t e m .o u t .p r in t ln (r e c i);

}
} /* Is p is : [A, B, C, C o lle c tio n s , D, E, F, G, H, HashSet, I , J , K, L, M, N, O peracijeSaSkupovim a, Is p is , P r in t , S e t, S t r in g , X, Y, Z, add, addA ll, arg s, c la s s , co n tain s, c o n ta in s A ll, cuvanje, deo, d o d a ti, f a ls e , import, iz , ja v a , main, mindview, n e t, new, p r in t, p u b lic , remove, removeAll, skupl, skupal, skupul, skup2, s p l i t , s t a t i c , tru e , uklonjen, u t i l , void]

* ///:-

TextFile je n asle en iz klase List<String>. K o n stru k to r TextFile o tv a ra d a to tek u i deli je na rei u sk lad u s regularnim izrazom \W + , koji zn ai jed n o ili vie slova (reg u la rn i izrazi su o b jan je n i u poglavlju Z n a ko vn i nizovi). R ezultat se p red aje k o n s tru k to ru sk u p a TreeSet koji sadraj Liste d o d a je sebi. P oto je u p ita n ju TreeSet, rezu ltat je u re d en . U o v o m sluaju, u red iv an je se obavlja leksikografski, p a su velika i m ala slova u zaseb n im g ru p a m a . U ko liko b iste h teli abecedno ure iv an je, k o n s tru k to ru sk u p a TreeSet p ro sled ite String.CASE INSENSITIVE ORDER Comparator (com paratorje objek at koji u sp o stavlja p o red ak ):
//: cuvanje/JedinstveneReciA becedno.java // Abecedno is p is iv a n je . import j a v a . u t i l .* ; import n e t.m in d v ie w .u til.* ; p u b lic c la s s JedinstveneReciAbecedno { p u b lic s t a t ic void m a in (S trin g [] args) { Set<String> reci = new T reeSet< Stri n g > (S tring.CASE_INSENSITIVE_ORDER); r e c i . addAl1( new T e x tFile ("O p e ra c ije Sa Sk u p o vim a .ja va ", "\W + ")); S y s t e m .o u t .p r in t ln (r e c i);

}
} /* Is p is : [A, add, ad d A ll, args, B, C, c la s s , C o lle c tio n s , con tain s, c o n ta in s A ll, cuvanje, D, d o d a ti, E, F, f a ls e , G, H, HashSet, I , import, iz , J , ja v a , K, L, M, main, mindview, N, n et, new, OperacijeSaSkupovima, Is p is , P r in t , p u b lic , remove, rem oveAll, S e t, skupl, skupal, skupul, skup2, s p l i t , s t a t i c , S tr in g , tru e , uklonjen, u t i l , void, X, V, Z]

* ///:-

Comparatori e biti d e taljn o raz m o tren i u poglavlju N izovi. Veba 16: (5) N ap rav ite sk u p (Set) sam o g lasn ik a. P rep rav ite p ro g ra m JedinstveneRec i.ja v a tako d a p reb ro ji i ispie b ro j sam o g lasn ik a u svakoj ulaznoj rei, kao i u k u p a n bro j sam o g lasn ik a u u lazn o j dato teci.

326

Misliti na Javi

Funkcionalnost mape
M ap iran je je d n ih objek ata n a d ru g e m o e biti izu zetn o m o a n n ain reavanja p ro g ram ersk ih zadataka. P rim e ra rad i, u z m im o p ro g ra m za ispitivanje sluajnosti brojeva iz Javine klase R a n d o m . U id ea ln o m sluaju, R a n d o m bi davala savreno raspodeljene sluajne brojeve, ali da biste to ispitali, m o ra te d a generiete vie slu ajn ih brojeva i da izb ro jite o n e koji p rip a d a ju razliitim opsezim a. M a p a reava taj zad atak s lakoom ; u ovom sluaju, klju je bro j koji p ro izv o d i klasa R a n d o m , a v re d n o st je bro j p u ta koliko se taj broj pojavio:
//: c u v a n je / S ta tis tic k iP o d a c i.ja v a // Jednostavan prikaz rada HashMape. import j a v a . u t i l .* ; p ub iic c lass S t a tis t ic k iP o d a c i { pu b lic s t a t ic void m a in (S trin g [] args) { Random slu cajan = new Random(47); Map<Integer,Integer> m = new H ashM ap< Integer,Integer> (); f o r ( i n t i = 0; i < 10000; i++) { // P r a v lje n je broja izmeu 0 i 20: in t s = s lu c a ja n .n e x tln t (2 0 ); In teg er ucestanost = m .g e t(s ); m .put(s, ucestanost = = n u ll ? 1 : ucestanost + 1 );

}
S y s te m .o u t.p rin tln (m );

}
} /* Is p is : {15=497, 4=481, 19=464, 8=468, 11=531, 16=533, 18=478, 3=508, 7=471, 12=521, 17=509, 2=489, 13=506, 9=549, 6=519, 1=502, 14=477, 10=513, 5=503, 0=481}

*///U m eto d i m a in (), a u to m atsk o pak o v an je p retv ara slu ajn o g en erisan in t u referencu objekta tip a In te g e r koja se m oe staviti u m a p u tipa H ash M a p . (U k o n tejn e r ne m oete da stavite v re d n o st p ro sto g tip a, ve sam o referencu na objekat.) M eto d a g et() vraa n u ll ukoliko takvog kljua nije b ilo u k o n te jn e ru (to znai da je taj broj prv i p u t p ro n a e n ). U p ro tiv n o m , m eto d a vraa p rid ru e n u In te g e r v re d n o s t kljua koja se poveava za jedan (a u to m a tsk o pakovanje p o n o v o p o jed n o stav lju je izraz, ali se obavlja p retv aran je u In teg e r i iz njega). Evo p rim e ra u kojem se o b jek ti tip a P et p ro n a la ze p o m o u tek stu aln o g opisa, tj. objek ata tip a S trin g . P okazan o je i kako se m e to d a m a c o n tain sK e y () i c o n tain sV a lu e () ispitu je da li M ap a sari o d re e n i klju, o n o sn o v red n o st:
//: cuvanje/M apaljubim aca.java import ty p e in fo .p e ts .* ; import j a v a . u t i 1 .*; import s t a t ic n e t.m in d v ie w .u til. P r i n t . * ;

Poglavlje 11 : uvanje objekata

327

p u b lic c la s s Mapaljubimaca { p u b lic s t a t i c void m a in (S trin g [] args) { Map<String,Pet> mapaljubimaca = new HashMap<String,Pet>(); mapaljub im aca.put("M oja maka", new C a t (" M ic a " )) ; mapaljub im aca.put("M oj pas", new D o g ("B ru n o ")); m apaljubim aca.put("Moj h rak ", new H am ste r("Bo ko ")); p rin t(m ap alju b im aca); Pet pas = m apaljubim aca.get("Moj p a s "); p r in t ( p a s ) ; print(m apaljubim aca.containsKey("M oj p a s " ) ) ; p rin t(m a p alju b im a c a .c o n ta in sV a lu e (p a s));

}
} /* Is p is : {Moja maka=Cat Mica, Moj hrak=Hamster Boko, Moj pas=Dog Bruno} Dog Bruno true true

* ///:M ap e jc lako p ro iriti n a vie d im en zija, kao i nizove i kolekcije; sam o n a p ra v ite M a p u ije su v re d n o sti d ru g e M a p e (a v re d n o sti tih M a p a m o g u b iti d ru g i k o n te jn e ri, ak i d ru ge M ap e). D akle, m o n e s tru k tu re p o d ata k a p rave se veom a lako i brzo, p o m o u k o m b in o v an ja k o n te jn e ra . N a p rim er, p re tp o sta v im o da p ro g ra m treb a d a p ra ti o so b e koje im a ju vie k u n ih Ijubim aca - sam o vam treb a M a p < P e rso n , L i s t < P e t :
//: cuvanje/M apaLista.java package cuvanje; import ty p e in fo .p e ts .* ; import j a v a . u t i l .* ; import s t a t i c n e t.m in d v ie w .u til. P r i n t . * ; p ub lic c la s s MapaLista { pu b lic s t a t ic Map<Person, List< ? extends P e t 1jud iSljub im cim a = new HashMap<Person, L ist< ? extends P e t ( ) ; s t a t ic { 1jud iSljub im cim a.pu t(new P e r s o n ("Z o r a "), A rra y s .a s L is t(n e w Cymric("Mol i ) ,new M u t t(" a r k o ''))); 1ju d iS l jubimcima.put(new P e rs o n (''K a ta "), A rra y s .a s L is t(n e w C a t ( e l j k a " ) , new C a t ( " E lz a " ) , new D o g C 'M a rg a re ta "))); 1ju d iS1 jub im cim a.put(new Person( " M ir n a " ) , A r r a y s .a s L is t ( new PugC'Lui i l i Luis D is a li Meh"), new C a t("S ta n e i l i Smradi el Negro"), new C a t( "P i nkola " ) ) ) ; 1jud iS1 jub im cim a.put(new Person( L u k a "), A r r a y s .a s L is t (new R a t ( " F a z i" ) , new R a t ( " F i z i " ) ) ) ; 1ju d iS l jubimcima.put(new P e r s o n (" Is a k " ), A rra y s .a s L is t(n e w R a t("P e g a v i ) ) ) ;

328

Misliti na Javi

p ub lic s t a t i c void m a in (S trin g [] args) { p r i n t ( " l j u d i : " + 1judiS1jubim cim a. keySet( ) ) ; p r in t ( " lj u b im c i: " + 1ju d iS lju b im c im a .v a lu e s ()); fo r(Perso n osoba : 1ju d iS lju b im c im a .k e y S e t()) { p rin t(o so ba + " im a :"); fo r (P e t ljubim ac : lju d iS lju b im c im a .g e t(p e rs o n )) p r in t ( " " + lju b im a c);

) /* Is p is : l j u d i : [Person Luka, Person M irna, Person Is a k , Person Zora, Person Kata] lju b im c i: [[R a t Fa z i, Rat F i z i ] , [Pug Lui i l i Luis D is a li Meh, Cat Stane i l i Smradi el Negro, Cat P in k o la ], [Rat P e g a v i], [Cymric M o li, Mutt a rk o ], [Cat e ljk a , Cat E lz a , Dog M argareta]] Person Luka ima: Rat Fazi Rat F iz i Person Mirna ima: Pug Lui i l i Luis D is a li Meh Cat Stane i l i Smradi el Negro Cat Pinkola Person Isak ima: Rat Pegavi Person Zora ima: Cymric Moli Mutt arko Person Kate ima: Cat e ljk a Cat Elza Dog Margareta

* ///:-

Mapa m oe da vrati skup svojih kljueva, kolekciju svojih v re d n o sti ili sk u p svojih p arova. M etoda keySet() p rav i sk u p svih kljueva u m ap i ljudiSljubimcima, koja se u foreach n ared b i u p o treb ljav a za ite rira n je k ro z Mapu. Veba 17: (2) K lasu MorskoPrase iz vebe 1 stavite u m a p u tak o da im e o b jek ta klase MorskoPrase kao String b u d e klju za o b jek at koji stavljate u tab elu . U zm ite Iterator za sk u p koji daje m e to d a keySet( ) i u p o tre b ite ga za k retan je k ro z m a p u i p ro n alaen je o b jek ta klase MorskoPrase za svaki klju, p o to m ispiite svaki klju i p o zo v ite m e to d u sk a ce( ) za svaki objekat. Veba 18: (3) P o p u n ite m a p u HashMap p a ro v im a k lju -v re d n o st. Ispiite rezu ltate tako
da po k aete u re e n je p o he k o d u . Izvadite p arove i u red ite ih p o k ljuu, a rez u ltat stavite u m a p u LinkedHashMap. Pokaite d a je red o sled u m e ta n ja zad ran .

Veba 19: (2) P o n o v ite p re th o d n u vebu sa sk u p o v im a HashSet i LinkedHashSet. Veba 20: (3) P rep rav ite vebu 16 tako d a se b ro ji uestalo st pojave svakog sam oglasnika.

Poglavije 11: uvanje objekata

329

Veba 21: (3) K oristei Map<String,Integer> i o b lik p ro g ra m a JedinstveneReci.java,


napiite p ro g ra m koji b ro ji uestalo st pojave rei u dato teci. U red ite rezu ltate m e to d o m

C ollections.sort() kojoj je d ru g i a rg u m e n t String.CASE_ INSENSITIVE_ORDER (d a bi


se d o b ilo u re en je p o abecedi) i p rik aite rezu ltat.

Veba 22: (5) P reprav ite p re th o d n u vebu ta k o d a u p o treb ljav a klasu koja sadri String i po lje b ro jaa za skladitenje svih razliitih rei, i Set tih o b je k a ta za o d rav an je liste rei. Veba 23: (4) N a o sn o v u p ro g ra m a StatistickiPodaci.java na p iite p ro g ra m koji staln o
izvrava test i prov erav a da li se u rezu ltatim a n ek i b ro j p ojavljuje ee o d d ru g ih .

Veba 24: (2) P o p u n ite m a p u LinkedHashMap k lju ev im a tip a String i o b je k tim a p o svom izb o ru . P o to m izdvojite parove, u re d ite ih p o k ljuu i p o n o v o u m e tn ite u Mapu. Veba 25: (3) N apravite M ap<String,A rrayL ist<Integer. O tv o rite te k stu a ln u d ato teku p ro g ra m o m net.mindview.TextFile i u itajte je re p o re (k ao d ru g i a rg u m e n t k o n stru k to ra TextFile u p o tre b ite \W +). P rebrojte rei to k o m u itav a n ja i za svaku re u d ato teci, zapiite u listi ArrayList<Integer> koliko se p u ta p o jav ila - to je, efektivno, m esto u d a to teci gde je ta re p ro n a e n a .

Veba 26: (4) P reprav ite rezu ltu ju u Mapu iz p re th o d n e vebe tak o d a p o n o v o u sp o stavite p o re d a k rei iz o rig in aln e datoteke.

Pravljenje reda za ekanje (od ulanane liste)


Red za ekanje (engl. queue) je FIFO k o n te jn e r (engl. first-in , fir s t-o u t- p rv i koji u e, prvi izlazi). To znai da elem en te stavljate s je d n o g k raja, a sk id ate s d ru g o g , tj. o n i izlaze red o sled o m kojim su stavljani. R edovi za ekanje se o b in o k o riste za p o u z d a n o p reb acivanje o b jekata iz je d n o g dela p ro g ra m a u d ru g i. K ao to ete v id eti u p oglavlju Paralelno izvravanje, redovi za ekanje su n aro ito vani u p a ra le ln o m p ro g ra m ira n ju , p o to bezb e d n o p reb acu ju objekte iz je d n o g posla (engl. task) u d ru g i. L in k ed L ist im a m eto d e koje o p o n aaju red za ek an je i realizuje interfejs Queue, pa se m oe u p o tre b iti za realizovanje reda za ekanje. Poto je L in k e d L ista svedena navie na Q u e u e , u ovom p rim e ru u p o treb ljen e su m e to d e specifine za red za ekanje u in terfejsu Q ueu e:
//: cuvanje/QueueDemo.java // P r a v lje n je reda za ekanje od klase L in k e d L ist svoenjem navie // na Queue. import ja v a . u t i l p u b lic c la s s QueueDemo { p u b lic s t a t ic void printQ(Queue redZaCekanje) { w hile(redZaC ekanje.peek() != n u ll) System .out.print(redZaC ekanje.rem ove() + " " ) ; S y s te m .o u t .p r in tln ();

}
p u b lic s t a t ic void m a in (S trin g [] args) { Queue<Integer> redZaCekanje = new L in k e d L ist< In te g e r> ();

330

Misliti na Javi

Random slu cajan = new Random(47); f o r ( i n t i = 0; i < 10; i++) re d Z a C e k a n je .o ffe r(s lu c a ja n .n e x tln t(i + 1 0 )); printQ (redZaC ekan je); Queue<Character> rcZnakova = new LinkedList<Character>; fo r (c h a r znak : ,lB ro n to sa u ru s".to C h arA rray()) rc Z n ak o va.o ffe r(z n ak ); p rin tQ (rcZ n ak o va);

}
} /* Is p is : 8 1 1 1 5 14 3 1 0 1 B r o n t o s a u r u s

* ///:M eto d a o ffe r ( ) je d n a je o d o n ih specifinih za k lasu Queue; o n a u m ee elem en t na re p red a za ekanje ako m o e, ili vraa false. M eto d e p e e k ( ) i elem en t( ) v raaju elo red a za ekanje, a da ga ne uklone, ali p e e k ( ) v raa null ako je red za ekanje p ra za n , d o k elem en t( ) g enerie izuzetak NoSuchElementException. M eto d e p o lI( ) i rem ove( ) u k la n ja ju i v ra a ju elo red a za ekanje, ali p o ll( ) v raa null ako je red za ekanje p ra za n , d o k rem ove( ) u to m sluaju generie izu zetak NoSuchEIementException. A u to m atsk o pak o v an je a u to m atsk i p retv ara int rezu ltat m e to d e n e x tln t( ) u objek at tip a Integer koji zahteva redZaCekanje, k ao i char znak u Character o b jek at koji zahteva rcZnakova. Interfejs Queue suava p ristu p m e to d a m a liste LinkedList tak o da b u d u d o s tu p n e sam o p rik la d n e m e to d e , p a ba i n iste u isk u en ju d a u p o treb ljav ate m eto d e liste LinkedList (ovde biste redZaCekanje zaista m o g li da svedete u n a za d na LinkedList, ali to v am je b a re m o tean o ). Im ajte u v id u da m e to d e specifine za Queue p ru aju p o tp u n u i sa m o sta ln u fu n k cio n a ln o st. D ru g im reim a, m o ete n ap rav iti u p o treb ljiv red za ekanje a da ne p rim e n ite n ije d n u m e to d u klase Collection, o d koje je Queue n asleen.

Veba 27: (2) N apiite klasu Naredba koja sadri je d a n String i im a m e to d u operacija( ) koja ga p rik azuje. N apiite d ru g u klasu s m e to d o m koja p o p u n jav a Queue o b jek tim a tip a Naredba i vraa taj red za ekanje. P o p u n jen Queue p ro sled ite m eto d i u treoj klasi koja u zim a o b jekte reda za ekanje i poziva n jihove m e to d e operacija( ).

Prioritetni red ekanja (PriorityQueue)


P rvi izlazi o naj koji je p rv i uao (engl. first-in first-o u t, FIFO) o p isu je n a jtip i n iju discip lin u ekanja. A ko im a m o g ru p u elem en ata u red u za ekanje, discip lin a ekanja o d re u je koji ele m e n t sledei izlazi iz reda. Po p rin c ip u FIFO, sledei elem en t tre b a da b u d e o naj koji n a jd u e eka. P rioritetni red ekanja kazuje d a iz reda p rv o izlazi ele m e n t koji im a najveu p o tre b u (p rio rite t) d a izae. N a p rim e r, na a e ro d ro m u se iz red a za ekanje izvlai p u tn ik iji se a vio n u p ra v o sp rem a za p o letan je. Ako n a p rav ite sistem za ra z m e n u p o ru k a, neke p o ru k e su vanije o d d ru g ih i treb a ih p re o b ra d iti, bez o bzira na to kada su stigle. Klasa PriorityQueue d o d a ta je Javi SE5 da bi se n ap rav ila au to m a tsk a realizacija takvog p o n aan ja.

Poglavlje 1I : uvanje objekata

331

K ada m eto d o m offer( ) p o n u d ite (engl. offer) n ek i o bjek at klasi PriorityQueue, o n e biti so rtira n i u b aen n a odgovarajue m esto to g red a za ekanje.5 P ri p o d ra z u m e v a n o m so rtira n ju (u re iv an ju ) u p otrebijava se prirodni redosled o b jek ata u re d u za ekanje, ali p o redak m oete da izm enite svojim Comparatorom. PriorityQueue se sta ra d a p ri pozivan ju m eto d e p eek ( ), poII( ) ili rem ove( ), o d nje dobijete e lem en t s n ajviim p rio rite to m . V eom a je lako n ap rav iti p rio rite tn i red za ekanje koji ra d i sa u g ra e n im tip o v im a kao to su Integer, String ili Character. U n a re d n o m p rim e ru , p rv i sk u p v re d n o sti ine id en ti n i sluajn i bro jev i iz p re th o d n o g p rim era , p a se m o ete uv eriti d a o n i iz p rio rite tn o g rad a za ekanje izlaze u d ru g o m p o retk u :
//: cuvanje/PriorityQueueDem o.java import j a v a . u t i l p u b lic c la s s PriorityQueueDemo { p u b lic s t a t ic void m a in (S trin g [] args) { PriorityQueue<Integer> p rio rite tn iR e d = new PriorityQ u eu e< Integer> (); Random slu cajan = new Random(47); f o r ( i n t i = 0; i < 10; i++) p r io r ite tn iR e d .o ff e r (s lu c a ja n .n e x tIn t(i + 1 0 )); QueueDemo.pri ntQ (pri o ri te tn i Red); List< Integer> c e liB r o je v i = A rra y s .a s L is t(2 5 , 22, 20, 18, 14, 9, 3, 1, 1, 2, 3, 9, 14, 18, 21, 23, 25); p rio rite tn iR e d = new P r i o r i tyQueue<Integer>(celi B r o j e v i) ; QueueDemo.printQ(priori te tn iR e d ); p rio rite tn iR e d = new PriorityQ ueue<Integer> ( cel iB r o je v i , s i z e ( ) , Col le c t io n s .r e v e r s e O r d e r O ); p r io r i te tn i Red.addAl1( c e li B r o je v i) ; QueueDemo.pri n tQ (p rio ri te tn iR e d ); S trin g c in je n ic a = "EDUCATION SHOULD ESCHEW OBFUSCATION"; List< String> strin g s = A r r a y s .a s L is t (c in je n ic a . s p lit ( " " ) ) ; PriorityQ ueue<String> stringPQ = new P r io r i tyQueue<Stri ng>(stri n g s ); QueueDemo.printQ(stringPQ); stringPQ = new PriorityQ ueue< String> ( s t r in g s .s iz e O , Col le c tio n s .re v e r s e O rd e rO ); strin g PQ .ad dA l1( s t r in g s ) ; QueueDemo.printQ(stringPQ); Set<Character> skupZnakova = new HashSet<Character>(); fo r(c h a r c : c in je n ica.to C harA rray( ) ) skupZnakova.add(c); // Automatsko pakovanje PriorityQueue<Character> characterPQ =
U stv a ri, o v o zavisi o d realizacije. A lg o ritm i p rio r ite tn ih re d o v a za e k a n je o b i n o s o rtira ju e le m e n te o d m a h n a k o n u m e ta n ja (o d ra v a ju i gomilti), ali iz b o r n a jv a n ije g e le m e n ta m o g u o b a v iti i n a k o n u k la n ja n ja . K oji se a lg o rita m k o risti, v a n o je ak o se p rio r ite t o b je k ta m o e p r o m e n iti d o k o n eka u re d u .

332

Misliti na Javi

new PriorityQueue<Character>(skiipZnakova); QueueDemo.printQ(characterPQ);

}
} /* Is p is :

0 1 1 1 1 1 3 5 8 14 1 1 2 3 3 9 9 14 14 18 18 20 21 22 23 25 25 25 25 23 22 21 20 18 18 14 14 9 9 3 3 2 1 1
A A B C C C D D E E E F H H I I L N N 0 0 0 0 S S S T T U U U W W U U U T T S S S O O O O N N L I I H H F E E E D D C C C B A A A B C D E F H I L N O S T U W

* ///:V idite d a su d u p lik a ti dozvo ljen i i d a n a jm a n je v re d n o sti im a ju najvii p rio rite t (u tip u

String, i razm aci se b ro je k ao v re d n o sti, i im aju vii p rio rite t o d slova). D a b iste videli kako svojim o b jek to m tip a Comparator m o ete izm en iti red o sled so rtira n ja , trei poziv k o n stru k to ra PriorityQueue<Integer> i d ru g i p o ziv k o n stru k to ra PriorityQ ueue<String> u p o treb ljav aju Comparator o b rn u to g red o sled a koji daje m eto d a C ollections.reverseO rder( ) - d o d a ta u Javu SE5. U p o sled n jem o deljku d o d a je se HashSet koji elim in ie d u p lik a te znakova (Character), sam o d a bi b ilo m alo zanim ljivije. Integer, String i Character ra d e s kJasom PriorityQueue zato to njihove klase ve
im aju u g ra e n svoj p riro d n i p o re d a k . U koliko u p rio rite tn o m re d u za ekanje ho ete da u po treb ljav ate sopstvene Jdase, m o ra te d o d a ti i fu n k c io n a ln o st koja p ro izv o d i p riro d n i p o red a k ili o b ezb ed iti svoj Comparator. U p o glavlju D etaljtio razm atranje kontejnera n ai ete i ta n a n iji p rim e r koji to p o k azu je .

Veba 28: (2) P o m o u m eto d e o ffe r( ), p o p u n ite p rim e ra k kJase PriorityQueue brojevim a tipa D ouble n ap rav ljen im p o m o u klase java.util.Random. Z atim u k lo n ite elem en te m e to d o m p o ll( ) i p rik aite ih.
V eba 29: (2) N aprav ite je d n o sta v n u klasu koja n asle u je O b je c t i n e m a lanova, i p okaite d a ne m oete usp en o d o d a ti vie ele m e n a ta te Jdase p rio rite tn o m redu za ekanje. To e b iti p o tp u n o o b jan je n o u poglavlju D etaljno razm atranje kontejnera.

Poreenje kolekcija i Iteratora


Collection je ko renski interfejs koji o p isu je o n o to je zajed n ik o svini k o n tejn erim a sekvenci. M oete ga sm a tra ti slu ajn im in terfe jso m koji je n astao zbog m e u so b n e slinosti d ru g ih interfejsa. Sem toga, klasa java.util.AbstractCollection im a p o d ra z u m e v an u realizaciju Jdase Collection, p a n ove p o d tip o v e o d AbstractCollection m oete p raviti b ez n e p o tre b n o g d u p lira n ja koda. Jedan o d a rg u m en ata za pravljenje interfejsa jeste to da o n o m o g u av a pisanje optijeg koda. A ko je k o d nap isan za interfejs a ne za realizaciju, p rim en ljiv je n a vie tipova objekata.6 U koliko n ap iem m e to d u koja u zim a Collection, ta m eto d a se m oe p rim en iti na
6 Im a lju d i koji z ag o v a ra ju a u to m a ts k o p ra v lje n je in te rfe js a za svaku m o g u u k o m b in a c iju m e to d a u klasi - p o n e k a d i za s v ak u k la su . S m a tr a m k a k o in te rfe js tre b a d a z n a i vie o d m e h a n i k o g d u p iir a n ja k o m b in a c ija m e to d a , p a o b i n o p rv o s a g le d a m v re d n o s t k o ju e in te rfe js d o n e ti i tek ta d ga p ra v im .

Poglavlje 11: Luvanje objekata

333

svaki tip koji realizuje Collection - a to svakoj novoj klasi p ru a m o g u n o st da realizuje Collection da bi m ogla b iti u p o tre b lje n a s m o jo m m eto d o m . Z anim ljivo je p rim e titi da s ta n d a rd n a biblioteka C + + -a za svoje k o n tejn ere n em a zajed nk ku o sn o v n u klasu - sve to im je zajedniko, postie se ite ra to rim a . M oda b i u Javi bilo p a m e tn o slediti p rim e r iz C + + -a i izraziti slinost k o n tejn era ite ra to ro m , a ne kolekcijom . M e u tim , ta dva p ristu p a su povezana, p o to realizovanje klase Collection znai i o bezbedivanje m eto d e iterator( ):
//: c u v a n je / P o re d je n je ln te rfe js a iIte ra to ra .ja v a import ty p e in fo .p e ts .* ; iirport j a v a . u t i l .* ; p u b lic c la s s P o r e d je n je ln t e r fe js a iIt e r a t o r a { p u b lic s t a t ic void is p is i(Ite r a to r < P e t> i t ) { w h ile (it.h a s N e x t ()) { Pet p = i t . n e x t ( ) ; S y s te m .o u t .p r in t(p .id () + + p + " ");

}
S y s te m .o u t .p r in tln ();

}
p u b lic s t a t ic void is p is i(C o lle c tio n < P e t> lju b im c i) fo r (P e t p : lju b im c i) S y s te m .o u t.p rin t(p .id () + + p + " "); S y s te m .o u t .p r in tln ();

}
p u b lic s t a t i c void m a in (S trin g [] args) { List<Pet> lis ta lju b im a c a = P e t s .a r r a y L is t ( 8 ) ; Set<Pet> skupljubimaca = new HashSet<Pet>( lis t a lju b im a c a ); Map<String,Pet> mapaljubimaca = new LinkedHashM ap<String,Pet>(); S t r in g [] imena = ( " R a lf , E r ik , Robin, L e js i, " + " B r i t n i , Sima, Tufna, P a p e r j a s t " ) . s p l i t ( " , " ) ; f o r ( i n t i = 0; i < im ena.length; i++) m ap alju b im aca.p u t(im en a[i], 1i s ta lju b im a c a .g e t( i ) ) ; i s p i s i (1i s t a lju b i m aca); is p is i(s k u p lju b im a c a ); i spi s i (1 i s ta lju b im a c a .i t e r a t o r ( ) ) ; i s p is i(s k u p ljub im aca.i t e r a t o r ( ) ) ; S y s te m .o u t.p rin tln(m apaljubim aca); System .o ut.p ri n tln(m apalju bim aca.keySet( ) ) ; i s p i s i (mapaljubim aca.v a l ues( ) ) ; is p is i(m a p a lju b im a ca .v a lu e s( ) . i t e r a t o r ( ) ) ;

}
} /* Is p is : 0 :Rat l:Manx 2:Cymric 3:Mutt 4:Pug 4:Pug 6:Pug 3:Mutt l:Manx 5:Cymric 0 :Rat l:Manx 2:Cymric 3:Mutt 4:Pug 4:Pug 6:Pug 3:Mutt l:Manx 5:Cymric 5:Cymric 6:Pug 7:Manx 7:Manx 2:Cymric 0:Rat 5:Cymric 6:Pug 7:Manx 7:Manx 2:Cymric 0 : Rf! 1

{R alf= R at, Erik=Manx, Robin=Cymric, Lejsi= M utt, Britni= Pu g, Sima=Cymric, Tufna=Pug, Paperjast=Manx}

334

Misliti na Javi

[ R a lf , E r ik , Robin, L e js i, B r i t n i , Sima, Tufna, P a p e rja st] 0:Rat l:Manx 2:Cymric 3:Mutt 4:Pug 5:Cymric 6:Pug 7:Manx 0:Rat l:Manx 2:Cymric 3:Mutt 4:Pug 5:Cymric 6:Pug 7:Manx

* ///:O be verzije m e to d e isp isi( ) ra d e i sa o b je k tim a tip a Map i s p o d tip o v im a o d Collection. I interfejs Collection i Iterator o d elju ju m e to d e is p is i( ) o d p o zn av an ja k o n k re tn e realizacije p rip a d n o g k o n tejn era. U ov o m sluaju, ob a p ristu p a su jed n ak a. U stvari, Collection je n eto bolji p o to se m oe iterirati (im a svojstvo Iterable), p a se u realizaciji ispisi(C ollection) m o e u p o tre biti foreach sintaksa, zbog ega je k o d n eto istiji. U p o treb a Iteratora p o staje obavezna k a d a realizu jete stra n u k lasu (o n u koja nije p o d tip o d Collection), u kojoj b i bilo teko ili b esm islen o realizovati interfejs Collection. P rim era radi, ako Collection realizu jem o n asle iv an jem klase koja sadri Pet objekte, m o ra m o realizovati sve m e to d e in terfejsa Collection, ak i ako n a m n e tre b a ju u m e to d i isp isi( ). Iako je to lako n ap rav iti p o m o u n asle iv an ja klase AbstractCollection, ipak m o rate da realizujete i iterator( ) i s iz e ( ), d a b iste o b ezb ed ili m e to d e koje AbstractColIection ne realizuje, ali ih u p o tre b ljav a ju d ru g e m eto d e u AbstractCollection:
//: cu van je/Sekven caKolekcija.java import ty p e in fo .p e ts .* ; import j a v a . u t i l . * ; public c la s s SekvencaKolekcija extends A bstractC o l1ection<Pet> { p riv a te P e t[] ljubim ci = P e ts .c r e a te A r r a y (8 ); p ublic in t s iz e () { return 1ju b im c i.le n g th ; } public Iterator<Pet> it e r a t o r ( ) { return new Iterato r< P et> () { p riv a te in t indeks = 0; public boolean hasNext() { return indeks < 1ju b im c i.le n g th ;

}
p ublic Pet n ex t() { return 1jub im ci[index+ + ]; } p ublic void remove() { // N ije realizovano throw new UnsupportedO perationException();

} }; }
public s t a t ic void m a in (S trin g [] args) { SekvencaKolekcija c = new S e k v e n c a K o le k c ija (); P o r e d je n je ln t e r fe js a iIt e r a t o r a .i s p is i ( c ) ; P o r e d j e n j e ln t e r f e j s a il t e r a t o r a . is p is i( c . it e r a t o r ( ) ) ;

}
} /* Is p is : 0:Rat l:Manx 2:Cymric 3:Mutt 4:Pug 5:Cymric 6:Pug 7:Manx 0:Rat l:Manx 2:Cymric 3:Mutt 4:Pug 5:Cymric 6:Pug 7:Manx

* ///= -

Poglav[je 11: uvanje objekata

33 5

M eto d a rem ove( ) je d n a je o d o p c io n ih o p eracija, s k ojim a em o vas u p o z n a ti u poglavlju D etaljno razm atranjc kontejnera. O vde je nije n e o p h o d n o realizovati, p a ete izazvati izuzetak ako je pozovete. Iz ovog p rim e ra m o ete v id eti sledee: ako realizujete Collection, realizujete i iterat o r ( ), a realizovanje sa m o m e to d e itera tor( ) zah teva tek m alo m an je posla o d nasleivan ja klase AbstractCoIlection. M e u tim , u k o lik o vaa klasa ve nasleuje d ru g u klasu, n e m o e naslediti i AbstractCollection. U to m sluaju, da biste realizovali Collection, m o ra li biste d a realizujete sve m e to d e to g in terfejsa. Tada bi bilo m n o g o lake naslediti Collection i d o d a ti m o g u n o st p rav ljen ja iteratora:
//: cu vanje/SekvencaBezKolekcije.java import ty p e in fo .p e ts .* ; import j a v a . u t i l . * ; c la s s Sekvencaljubim aca ( protected P e t [] lju b im ci = P e t s .c r e a te A r r a y (8 );

}
pub lic c la s s SekvencaBezKolekcije extends Sekvencaljubimaca ( p u b lic Iterator< Pet> it e r a t o r ( ) ( retu rn new Ite rato r< P e t> () ( p riv a te in t indeks = 0; p u b lic boolean hasNext() { return indeks < 1ju b im c i.le n g th ;

}
p ub lic Pet n ex t() { return 1jub im ci[indeks+ + ]; } p ub lic void remove() { // N ije realizo vano throw new UnsupportedO perationException();

} }; }
p u b lic s t a t ic void m a in (S trin g [] args) { SekvencaBezKolekcije nc = new Se k v e n ca B e z K o le k c ije (); P o r e d je n je ln t e r f e js a iIt e r a t o r a .i spi s i ( n c . i t e r a t o r ( ) ) ;

}
} /* Is p is : 0:Rat l:Manx Z:Cymric 3:Mutt 4:Pug 5:Cymric 6:Pug 7:Manx

* ///:Pravljenje Iteratora je n ajrazd v o jen iji nain povezivanja sekvence i m eto d e koja p rim a tu sekvencu, a pri to m se klasa sekvence o g ran iav a z n a tn o m anje nego kada se im plem e n tira klasa Collection.

Veba 30: (5) Izm en ite p ro g ra m ColIectionSequence.java tako d a ne nasle uje AbstractCollection, nego da realizuje Collection.

336

Misliti na Javi

Foreach i iteratori
D o sad sm o foreach sin tak su u g lav n o m up otreb ljav ali s nizovim a, ali o n a rad i i sa svim o b jek tim a Collection. Z apravo, ve ste videli nekoliko p rim e ra za to u k o jim a se u p o tre bljavala ArrayList, ali ovo je o p ti dokaz:
//: cu van je/Fo rEachK o lek cije.java // Sve k o le k c ije rade s foreach sintaksom. import ja v a . u t i l p u b lic c la s s ForEachK olekcije { p u b lic s t a t ic void m a in (S trin g [] args) { C ollection< String> cs = new L in k e d L ist< S trin g > (); C o lle c tio n s .a d d A l1(c s , "Take the long way h o m e ".s p lit(" ) ) ; fo r (S tr in g s : cs) "); S y s te m .o u t.p rin t(.... + s +

}
} /* Is p is : 'Take' 'th e ' 'lo n g ' 'way' 'home'

* ///:P oto je cs tip a Collection, ovaj k o d do k azuje da sve kolekcije m o g u da rad e s foreach sintak so m . O vo fu nkcion ie zato to je Java SE5 uvela nov interfejs nazvan Iterable koji sadri m eto d u iterator( ) za pravljenje Iteratora. Za k retanje kroz sekvencu, foreach u p o treb ljav a interfejs Iterable. D akle, ako n a p rav ite klasu koja realizuje Iterable, m o ete je u p o tre b iti u foreach naredbi:
/ / : c u v a n je / Ite ra b i1n aK lasa.java // Sve to j e ite ra b iln o funkcionie s foreach sintaksom. import j a v a . u t i l .* ; p u b lic c la s s Ite ra b iln a K la s a implements Iterable< String> { protected S t r in g [] rechi = ("And th a t is how " + "we know the Earth to be banana-shaped." ) . s p li t ( " " ) ; p u b lic Iterator< String> it e r a t o r ( ) { return new Ite ra to r< S trin g > () { p riv a te in t indeks = 0; p u b lic boolean hasNext() { return indeks < r e c h i.le n g th ;

}
p u b lic S trin g n ex t() { return r e c h i[ i ndeks++]; } p ub lic void remove() { // N ije realizovana throw new UnsupportedO perationException();

}
};

Poglavlje 11: uvanje objekata

337

p u b lic s t a t ic void m a in (S trin g [] args) { f o r (S t r in g s : new It e r a b iln a K la s a ()) S y ste m .o u t.p rin t(s + " " ) ;

}
} /* Is p is : And th a t is how we know the Earth to be banana-shaped.

* ///:M eto d a iterator( ) vraa in sta n cu a n o n im n e u n u tra n je realizacije klase Iterator<String> koja daje svaku re niza. U m e to d i m a in ( ) m o ete v id eti da IterabilnaKIasa zaista ra d i u foreach naredbi. U Javi SE5 ite rab iln o je vie klasa, p rv en stv en o sve klase C ollection (ali n e M a p e ). Na p rim e r, ovaj p ro g ra m p rik azu je sve p ro m en ljiv e o k ru en ja o p erativ n o g sistem a:
//: cuvanje/Prom enljiveO kruzenja.java import j a v a . u t i l . * ; p u b lic c la s s PromenljiveOkruzenja { p u b lic s t a t ic void m a in (S trin g [] args) { fo r(M ap .En try stavka: S y s te m .g e te n v ().e n try S e t()) S y s te m .o u t.p rin tln (s ta v k a .g e tK e y () + " : " + s ta v k a .g e tV a lu e O );

} }
} /* (Po k re n ite da b is te v id e li re z u lta te ) *///:-

System .getenv( )' vraa Mapu; en tryS et( ) p rav i sk u p (Set) e lem en ata Map.Entry, a p o to je sk u p iterab ilan , m oe se u p o treb iti u fo reach petlji. F oreach n ared b a rad i s nizovim a i sa svim to je iterab iln o , ali to ne znai d a je svaki niz au to m a tsk i iterab ilan , niti da se obavlja ikakvo a u to m atsk o pakovanje:
/ / : c u v a n je / N iz N ije lte ra b ila n .ja v a import j a v a . u t i l .* ; p u b lic c la s s N iz N ije lte r a b ila n { s t a t i c <T> void test(Iterab le< T > ib ) fo r(T t : ib ) S y s te m .o u t.p rin t(t + " " ) ;

}
p u b lic s t a t ic void m a in (S trin g [] args) { t e s t ( A r r a y s . a s L i s t ( l, 2, 3 ) ) ; S t r in g [] znakovni_nizovi = { "A ", " B " , "C" } ; // Niz radi u foreach naredbi, a l i n ij e it e r a b ila n : // ! te s t(z n a k o v n i_ n iz o v i); // Morate ga i z r i i t o p r e t v o r iti u neto it e r a b iln o : t e s t(A r r a y s .a s L is t(z n a k o v n i_ n iz o v i)); Ova metoda nije postojala pre Jave SE5, poto se smatralo da bi bila previe usko povezana sa operativnim sistemom, pa bi se krilo pravilonapii jednom, izvravaj bilo gde. injenica da metoda sada postoji k.izuje da su projektanti Jave nakon pojave .NET-a postali pragmaliniji.

338

Misliti na Javi

}
} /* Is p is : 1 2 3 A B C

* ///:Pokuaj da se niz p ro sle d i kao ite rab ila n arg u m e n t, n e uspeva. N e p o sto ji a u to m atsk a konverzija u Iterable; m o ra te je o bav iti ru n o .

Veba 31: (3) Izm en ite polimorfizam/oblik/GeneratorSlucajnihOblika.java tako da


p o sta n e iterab ilan . M orate d o d a ti k o n s tru k to r koji p rim a broj elem en ata koji ite ra to r treb a d a n ap ra v i p re zaustavljanja. D o kaite d a to radi.

Adapterska metoda
ta ako im a te ite ra b iln u klasu i h teli b iste d a d o d a te je d a n ili vie n o v ih n ain a u p o tre b e te klase u fo reach naredbi? P rim e ra rad i, p re tp o sta v im o d a b iste hteli d a b irate h o ete li k roz listu rei ite rira ti u n a p re d ili u n azad . U koliko sam o n asledite tu klasu i redefiniete m e to d u iterator( ), zam en iete p o sto je u m e to d u i neete m o i d a b irate. Jed n o reenje sam n azvao A dapterska m etoda. A dapterski d eo p o tie iz p ro je k tn ih o b razaca, p o to m o rate o b ezb ed iti o d re e n i interfejs d a biste zadovoljili foreach n ared b u . K ada im ate je d a n interfejs a tre b a v am d ru g i, p ro b le m ete reiti u koliko napiete adapter. O vde h o u da dodam m o g u n o st pravljenja iterato ra u n azad p o d ra z u m e v a n o m ite rato ru u n a p re d , p a ne m o g u da ga redefiniem . U m esto toga, d o d a u m e to d u koja proizvodi itera b ila n o b jek at koji se p o to m m oe u p o tre b iti u foreach naredb i. Kao to ete videti, tim e d o b ija m o vie n ain a u p o tre b e fo reach sintakse:
//: cuvanje/AdapterskaMetoda.ja va // "Adapterska metoda" omoguava upotrebu foreach // sin tak se s dodatnim vrstama it e r a b iln ih objekata. import j a v a . u t i l .* ; c la s s ReversibleArrayList< T> extends ArrayList<T> { p u b lic R e versib le A rrayList(C o llectio n < T > c) { s u p e r(c ); } p u b lic Iterable<T> obrnuto() { retu rn new Iterable< T> () { p u b lic Iterator<T> it e r a t o r ( ) { return new Iterator< T > () { in t tekuci = s iz e () - 1; p u b lic boolean hasNext() { return tekuci > -1; } p u b lic T n ex t() { return g e t(te k u c i ) ; } p u b lic void remove() { // N ije realizovano throw new UnsupportedO perationException();

}
};

}
};

} }

Poglavjj'e I 1: uvanje objekata

339

p u b lic c la s s AdapterskaMetoda { p u b lic s t a t ic void m a in (S trin g [] args) { R eversib leA rrayList< Strin g > ra l = new R eversib leA rrayList< Strin g > ( A rra y s .a s L is t("T o be or not to b e " . s p l i t ( " " ) ) ) ; // Metodom it e r a t o r ( ) grabi se obian it e r a t o r : fo r (S t r in g s : r a l) S y s te m .o u t.p rin t(s + " " ) ; S y s te m .o u t .p r in tln (); // D ajte ga iterab iln om objektu po svom izboru f o r (S t r in g s : ra l .obrn utoO ) Sy ste m .o u t.p rin t(s + " " ) ;

}
} /* Is p is : To be or not to be be to not or be To

* ///:A ko o b jek at ra l sam o stavite u foreach n a re d b u , dob iete (p o d raz u m ev an i) ite ra to r u n a p re d . Ali u koliko za taj objek at po zovete m e to d u o b r n u t o ( ), o n a e d a ti d rug aije p o n aan je. Na taj nain , klasi Ite ra b iln a K la sa .ja v a m o g u se d o d a ti dve adap tersk e m etod e:
//: c u va n je / V is e s tru k o Ite ra b iln a K la s a .ja v a // Dodavanje v i e adapterskih metoda. import j a v a . u t i l .* ; p u b lic c la s s V is e s tru k o Ite ra b iln a K la s a extends Ite ra b iln a K la s a { p u b lic Iterable< String> obrnutoO { return new Iterab le < S trin g > () { p ub lic Iterato r< Strin g > it e r a t o r ( ) { return new Ite ra to r< S trin g > () { in t tekuci = re c h i.le n g th - 1; public boolean hasNext() { return tekuci > -1; } public S trin g n ex t() { return r e c h i[te k u c i- - ]; } p ub lic void remove() { // N ije realizovano throw new UnsupportedO perationException();

} }; } }; }
p u b lic Iterable< String> slu c a jn o () { return new Ite ra b le < S trin g > () { p u b lic Iterato r< Strin g > it e r a t o r ( ) { List< String> izmesana = new A rrayLi st< Strin g > (A rrays.asLi st(w o rd s )) ; C o lle c tio n s .sh u ffle (iz m e s a n a , new Random(47));

340

Misliti na Javi

return iz m e s a n a .ite r a to r ();

} }; }
p ub lic s t a t ic void m a in (S trin g [] args) { V ise s tru k o Ite ra b iln a K la s a mic = new V is e s tr u k o It e r a b iln a K la s a (); f o r (S tr in g s : m ic.o b rn u to ()) Sy ste m .o u t.p rin t(s + " " ) ; S y s te m .o u t.p rin tln (); f o r (S tr in g s : m ic .s lu c a jn o O ) S y ste m .o u t.p rin t(s + " " ) ; S y s te m .o u t.p rin tln (); f o r (S tr in g s : mic) S y ste m .o u t.p rin t(s + " " ) ;

}
} /* Is p is : banana-shaped. be to Earth the know we how is th a t And is banana-shaped. Earth th a t how the be And we know to And th a t is how we know the Earth to be banana-shaped.

* ///:O b ra tite p a n ju n a to da d ru g a m e to d a , ra n d o m ( ), n e p ra v i so pstveni Iterator nego vraa o n aj iz izm eane Liste. Iz rezu ltata vid ite da m e to d a C ollections.shuffIe( ) n e u ti e n a o rig in a ln i niz, nego sam o m ea reference u o b jek tu izmesana. O vo je ta n o sam o zato to m e to d a slu cajn o() o m o ta v a ArrayList oko rezu ltata m eto d e Arrays.asList( ). Da je Lista nap rav ljen a m eto d o m Arrays.asList( ) izm ean a d ire k tn o , o n a bi m odifiko v ala p rip a d n i niz, kao to vidite:
//: cuvan je/M o d ifik o van jeN iz o vaK ao Listi.java import j a v a . u t i l .* ; pu b lic c la s s M odifikovanjeN izovaKaoListi p ublic s t a t ic void m a in (S trin g [] args) Random slucajan = new Random(47); { {

In te g e r[] nizcb = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 } ; List< Integer> l i s t a l = new A rra y L is t< In te g e r> (A r ra y s .a s L is t(n iz c b )); S y s te m .o u t.p rin tln ("P re meanja: " + l i s t a l ) ; C o ll e c t io n s . s h u f f le ( li s t a l, s lu c a ja n ); System .o ut.p rin tln("N ako n meanja: " + l i s t a l ) ; S y s te m .o u t.p rin tln ("n iz : " + A rra y s . to S tr in g (n iz c b )) ; List< Integer> 1ista 2 = A r r a y s .a s L is t (n iz c b ); S y s te m .o u t.p rin tln ("P re meanja: " + 1i s t a 2 ) ; C o lle c t io n s .s h u f f le (lis t a 2 , s lu c a ja n ); System .o ut.p rin tln("N ako n meanja: " + l i s t a 2 ) ; S y s te m .o u t.p rin tln ("n iz : " + A r r a y s .t o S t r in g (n iz c b ));

Pog).-- ie i ' :'vanje objekata

341

} /* Is p is : Pre meanja: [1 , 2, 3, 4, 5, 6, 7, 8, 9, Nakon meanja: [4, 6, 3, 1, 8, 7, 2, b, n iz : [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] Pre meanja: [1 , 2, 3, 4, 5, 6, '/, 8, 9, Nakon meanja: [9, 1, 6, 3, 7, 2, 5, 10, n iz : [9, 1, 6, 3, 7, 2, 5, 10, 4, 8]

10] 10, 9] 10] 4, 8j

* ///:U p rv o m sluaju, izlaz m eto d e A rrays.asList( ) p re d ai r >ostruktoru ArrayList( ), a o n p rav i ArrayList koja referen cira elem en te niza nizcb iije tih referenci n e m o difikuje niz. M e u tim , uk o lik o rez u ltat m e to d e A rra y s.a s l > , >zc b ) u p o tre b ite d irek tno, m ean je m o ifik u je p o re d a k niza nizcb. Im a jte u v id n ,ja Arrays.asList( ) pravi o b jek at tip a List koji u p o treb ljav a p rip a d n i n iz k ao svoju fi?ii< i ealizaciju. U koliko bilo kako izm e n ite tu Listu, a n e elite d a se m o d ifik u je o rig in a b r liz, n a p rav ite n jeg o v u k op iju u d ru g o m k o n te jn e ru .

Veba 32: (2) N a o sn o v u p rim e ra VisestrukoIterabilrmJHa; Kolekcije.java d o d a jte m e to d e o b rn u to ( ) i slu ca jn o ( ), a i realizujte Iterable, i p o k a ite da u fo reach n ared b am a funkc

irogramu-SekvencaBezi SekvencaBezKolekcije u sve te varijante.

Saetak
Java im a vie n a in a za u v an je objekata: 1 . N iz povezuje n u m e ri k e in d ek se sa o b jek tim a. O n sad o b jek te p o z n ato g tip a, pa n ak o n u zim a n ja o b jek ta iz niza ne m o ra te da v rite k rziju tip o v a. N iz m oe da b u d e v ie d im e n z io n ala n i da sadri p ro ste tipove. M e li m , ne m o ete d a m u p ro m en ite veliinu. 2. K olekcija sadri p o jed in a n e elem en te, d o k m a p a sad ri povezane parove. P o m o u Javinih g en erik ih tip o v a zadajete tip objekta koji c u v ati u k o n te jn e ru , p a u njega ne m o ete staviti p o g rean tip i ne m o ra te da i iijate tip elem en ata kada ih izvadite iz njega. I kolekcije i M ap e a u to m atsk i : rii; lavaju svoju veliinu n akon d o d a v a n ja i u ld an jan ja elem en ata. K o n tejn er re uioe ad ri p ro ste tipove, ali se a u to m a tsk o p akov an je stara za p rev o en je p ii, :tih i u r . i o m o tak e tipove koji se uvaju u k o n te jn e ru , i o b rn u to . 3. P o p u t niza, lista tak o e povezuje n u m erik f m o g u se sm a tra ti u re e n im k o n te jn erim a . u. * o b jek tim a; nizovi i liste a ristu p , a LinkedList ako 'L ist.

4. U p o treb ite listu ArrayList ako esto k o risfite


esto um eete i u k lan jate elem en te n sredini 5 . R edovi za ek an je i stekovi p rav e se p o m o u l c i , ; .in

6. Mapa je nain za povezivanje objekai; ,, ... >je d ru g im objek tim a. Klasa HashMap je p ro jek to v an a za b rz p ristu p , dok ite e M a p cuva kljueve u u re e n o m red o sledu i zato nije ta k o b rz a krtO ria sh M .,| '<' HashMap uva svoje elem en te u u re d e n o m red o sled u , ali tra n sfo rm ' ev o b ezbe u je b rz p ristu p .

342

Misliti na Javi

7. Set uva sam o objekte razliitih v red n o sti. Klasa HashSet o b e z b e u je p ro n alaen je m a k sim a ln o m b rz in o m , d o k TreeSet uva e lem en te u u re e n o m red o sled u . Klasa LinkedHashSet uva svoje elem en te u u re e n o m red o sled u . 8. N em a p o tre b e da se u n o v im p ro g ra m im a k o riste stare klase Vector, Hashtable i Stack.
Pogledajte p o jed n o stav ljen d ijag ram Javinih k o n te jn e ra (bez a p stra k tn ih klasa i starih k o m p o n e n a ta ). U n jem u su sa m o interfejsi i klase koje ete red o v n o sretati.
Iterator Collection f* Map

... J ... ;

Proizvodi

Proizvodi

.......'A........J 1
Proizvodi
1 ir f L J 51
C * J C l

""S '"
i

...... Listlterator i 1
i
ArrayList

/ i

Q ueue

HashMap

TreeMap

1 f
LinkedList PriorityQueue

LinkedHashMap

Alatke HashSet i Comparator !


IjnkedHashSet

TreeSet

Collections Arrays

V idite d a p o sto je sam o etiri k o n tejn ersk e k o m p o n e n te : Map, List, Set i Queue, i sam o dve ili tri realizacije svake o d njih (rea liza je klase Queue iz pak eta java.util.concurrent nisu p rik aza n e n a d ija g ra m u ). N ajee ete u p o tre b lja v ati k o n te jn e re oiviene d eb e lo m p u n o m lin ijo m . T akastim o k v irim a obeleeni su interfejsi, a o k v irim a s p u n o m lin ijo m o b in e (k o n kre tn e ) klase. Isp rek id an e p ra zn e strelice u k azu ju da o d re e n a klasa realizuje interfejs. Strelice s p u n o m lin ijo m u k azu ju da klasa m oe da prav i o b jek te klase na koju po k azu je strelica. Na p rim er, bilo koja kolekcija m oe da pravi Iterator, d o k List m o e da n ap rav i Listlterator (kao i ob ian Iterator, p o to je List izvedena iz Collection). Evo p rim e ra u kojem se vidi razlika izm e u m e to d a razn ih klasa. K od je naveden u poglavlju G eneriki tipovi ; ovde ga sam o p ozivam d a b ih d o b io rezu ltate. U n jim a se vide i interfejsi koje realizuje p o je d in a klasa ili interfejs:
//: cuvanje/KontejnerskeM etode.java import n e t.m in d v ie w .u til.* ; public c la s s KontejnerskeMetode { p ub lic s t a t ic void m a in (S trin g [] args) { R azlikeKon tejnerskih M etoda.m ain (args);

1
} /* Is p is : (prim er) C o lle c tio n : [add, ad d A ll, c le a r , con tain s, c o n ta in s A ll, equals, hashCode, isEmpty, it e r a t o r , remove, rem oveAll, r e t a in A U , s iz e , toA rray]

Poglavlje 11: uvanje objekata

343

In t e r f e j s i u k la s i C o lle c tio n : [ It e r a b le ] Set extends C o lle c tio n , dodaje: [] In t e r f e j s i u k la s i S e t: [C o lle c tio n ] HashSet extends S e t, dodaje: [] In t e r f e j s i u k la si HashSet: [S e t, C loneable, S e r ia liz a b le ] LinkedHashSet extends HashSet, dodaje: [] In t e r f e j s i u k la s i LinkedHashSet: [S e t, C loneable, S e r ia liz a b le ] TreeSet extends S e t, dodaje: [ p o llL a s t , navigableHeadSet, d escen d in g lte rato r, lovver, headSet, c e ilin g , p o l lF i r s t , subSet, n a v ig a b le T a ilS e t, comparator, f i r s t , f lo o r , la s t , navigableSubSet, h igher, t a il S e t ] In t e r f e j s i u k la s i T reeSet: [N avig a b leS et, C loneable, S e r ia liz a b le ] L is t extends C o lle c tio n , dodaje: [1 i s t l t e r a t o r , indexOf, g et, s u b L is t, s e t, la stln d ex 0 f] In t e r f e j s i u k la s i L is t : [C o lle c tio n ] A rra y L is t extends L is t , dodaje: [ensu reC apacity, trim ToSize] In t e r f e j s i u k la s i A rra y L is t: [ L i s t , RandomAccess, Cloneable, S e r ia liz a b le ] Lin k e d L ist extends L is t , dodaje: [p o llL a s t, o f f e r , d escen d in g lterato r, a d d F irs t, peekLast, rem oveFirst, p e e k F irs t, removeLast, g e tLa st, p o l lF i r s t , pop, p o l l , addLast, rem oveFirstO ccurrence, g e t F ir s t , element, peek, o ffe rL a s t, push, o f f e r F i r s t , removeLastOccurrence] In t e r f e j s i u k la s i L in k e d L ist: [ L i s t , Deque, C loneable, S e r ia liz a b le ] Queue extends C o lle c tio n , dodaje: [o f f e r , element, peek, p o ll] In t e r f e j s i u k la s i Queue: [C o lle c tio n ] PriorityQ ueue extends Queue, dodaje: [comparator] In t e r f e j s i u k la s i Prio rityQ u eu e: [S e r ia liz a b le ] Map: [ c le a r , containsKey, con tain sV alu e, e n try S e t, equals, g et, hashCode, isEmpty, keySet, put, p u tA ll, remove, s iz e , values] HashMap extends Map, dodaje: [] In t e r f e j s i u k la s i HashMap: [Map, Cloneable, S e r ia liz a b le ] LinkedHashMap extends HashMap, dodaje: [] In t e r f e j s i u k la s i LinkedHashMap: [Map] SortedMap extends Map, dodaje: [subMap, comparator, f ir s t K e y , la stK e y , headMap, tailM ap] In t e r f e js i u k la s i SortedMap: [Map] TreeMap extends Map, dodaje: [descendingEntrySet, subMap, p o l1L a s tE n try , la stK e y , flo o rE n tr y , la s t E n t r y , lowerKey, navigableHeadMap, n avig ab leTaiIMap, descendingKeySet, ta ilM a p , c e ilin g E n tr y , higherKey, p ol1F i rs tE n try , comparator, f ir s t K e y , flo o rK e y, h ig h erEn try, f i r s t E n t r y , navigableSubMap, headMap, lo w erEn try, c e ilin g K e y] In t e r f e js i u k la s i TreeMap: [NavigableMap, C loneable, S e r ia liz a b le ]

* ///:V idite da svi sku p ovi (o b jek at tip a Set) sem tip a TreeSet im a ju p o tp u n o isti interfejs kao Collection. List i Collection se z n a tn o razlik u ju , iako List zahteva m e to d e koje su u interfejsu Collection. S d ru g e stran e , m eto d e in terfe jsa Queue su sam o staln e; za p ravljenje fu n k cio n aln e realizacije reda za ekanje (Queue), m e to d e interfejsa Collection nisu p o treb n e. N ajzad, jed in a d o d irn a tak a klasa Map i C ollection jeste injenica da Map m oe da pravi kolekcije p o m o u m eto d a en tryS et( ) i v a lu es( ).

344

Misliti na Javi

Obratite panju na interfejs java.util.RandomAccess koji je prikaen uz ArrayList, ali ne i uz LinkedList. Time se prua informacija algoritmima ije e se ponaanje dinamiki menjati u zavisnosti od upotrebe odreene Liste. Ova organizacija je malo udnija od objektno orijentisane hijerarhije. Meutim, kako budete vie saznavali o kontejnerima paketa java.util (naroito u poglavlju Dctaljno razmatranje kontejnera), videete da nije problematina samo udna struktura nasleivanja. Oduvek je bilo teko projektovati biblioteke kontejnera, jer treba zadovoljiti meusobno suprotstavljene sile. Zato poneki kompromis ne bi trebalo da vas iznenadi. Uza sve to, Javini kontejneri su osnovne alatke kojima svoje programe moete uiniti jednostavnijim, snanijim i delotvornijim. Moda e vam trebati malo vremena da se naviknete na odreene aspekte ove biblioteke, ali mislim da ete njene ldase brzo poeti da nabavljate i upotrebljavate.
Reenja odabranih vebi su u elektronskom dokum entu The Thinking in Java Annotated Solution Guide, koji se moe kupiti n a lokaciji www.MindView.net.

Obrada greaka pomou izuzetaka


O S N O V N A M U D R O ST JAVE GLASI DA K O D KOJI JE LOSE N A PISA N N EC E N I BITI IZVRSAVAN.

N ajb o lje bi bilo k ad a b iste sve greke m og li d a o tk rijete to k o m prev o en ja, p re nego to u o p te p o k u ate da p o k re n e te p ro g ra m . M e u tim , ne m o g u se sve greke o tk riti p ri p re v o e n ju . N eki p ro b lem i se m o ra ju reavati za v rem e rad a, fo rm a ln im p o stu p c im a ; o n i o m o g u u ju v in o v n ik u greke da p ro sled i odg ov araju e in form acije p rim a o c u k oji e znati kako d a se n a p rav i n a in iz b o ri s tekoom . P o b o ljan o op o ravljan je o d greaka je d a n je o d n ajb o ljih n ain a za po veanje ro b u sn o sti k o d a. O poravljan je o d greaka je najvanije za svaki p ro g ra m koji piete, a po se b n o je v a n o u Javi, gde je je d a n o d p rim a rn ih ciijeva pravljenje p ro g ra m sk ih k o m p o n e n a ta za v i e k ra tn u u p o tre b u . D a biste napravili robustan sistem, svaka njegova kom p onenta m ora biti robusna. Java om o gu av a d a k o m p o n e n te p o u z d a n o dojave svoje p ro b le m e k lijen tsk om k o d u tak o to o bezb e u je d o sled an m o d el prijavljivanja greaka p o m o u izuzetaka. C iljevi o b ra d e greaka u Javi su p o jed n o stav ljiv an je pravljen ja velikih, p o u z d a n ih p ro g ra m a p o m o u m an je k o d a nego to je tr e n u tn o m ogue, i u z poveavan je v ero v atn o e da se u aplikaciji nee p o jav iti greka koja nije o b ra e n a. Izuzetke nije tolik o teko savlad ati, a sp a d a ju m e u elem en te Jave o d kojih p ro jek ti im aju tre n u tn e i p rim e tn e koristi. P oto je o b ra d a izuzetaka je d in i zvan ini nain n a koji Java prijavlju je greke, a sp ro v o d i je prev odilac Jave, u ovoj k njizi ne m o e biti m n o g o p rim e ra n a p isa n ih bez nje. U o v o m p oglavlju u p o zn aje te se s p rav ilim a rad a sa izuzecim a i n a in o m n a koji m o ete da g en eriete sopstvene izuzetke k ad a se neka o d vaih m eto d a splete.

Koncepti
C i d ru g i stariji jezici esto su im ali i po nekoliko em a za o b ra d u greaka koje su p o p ra vilu bile u tv rd e n e d o g o v o ro m , o d n o sn o nisu bile deo p ro g ram sk o g jezika. U glavn om se vraala p o seb n a v red n o st ili se p o stavljao in d ik ato r, a p rim alac bi p o sm a tra o v red n o st, o d n o s n o in d ik a to r i odlu iv ao d a li je sve u red u. M e u tim , to k o m g o d in a o tk riv e n o je da p ro g ra m e ri koji koriste b ib lio tek u o b i n o sebe sm a traju n epogreivim i razm iljaju u stilu: ,,Da, greke se m od a deavaju d ru g im a, ali u m om k od u ih n e m a . Z b o g toga ne iznen a u je to n isu proveravali uslove p o d ko jim a je nastajala greka (a p o n e k a d su uzroci greke bili previe glupi d a bi se p ro v erav ali).1 Kada biste bili d o v oljn o paljivi d a traite greku posle svake m eto d e, va k o d bi se p retv o rio u neitljivu n o n u m o ru . P oto su p ro gram e.ri i dalje m ogli da o d rav aju sistem e n ap isan e o p isa n o m te h n ik o m , o d b ijali su da p riz n a ju istinu: ovakav p ristu p isp rav ljan ju greaka u m n o g o m e je og ran iav ao pisanje velikih, sn a n ih p ro g ra m a koji bi se lako odravali. R eenje je d a se p o stu p k u o b ra d e greaka o d u z m e p riro d n o st i da se ojaa fo rm aln o st. O vaj nain , zapravo, im a d u g u isto riju , je r se obrada izuzetaka (engl. exception handling) sree jo u o p e ra tiv n im sistem im a iz ezdesetih g o d in a p ro lo g veka, ak i u n a re d b i on error goto iz p ro g ra m sk o g jezika BASIC. Tako se o b ra d a greaka u jeziku C + + zasnivala na jeziku A da, a Javina p rete n o n a C + + - U (iako vie podsea n a o b jek tn i Pascal).
P r o g ra m e r n a je/.iku C , n a p rim e r, m o e d a z atra i p o v r a tn u v re d n o s t fu n k c ije p r in t f ( ).

346

Misliti na Javi

Re ,,izuzetak se k o risti u sm islu o g ra u jem se o d tog a. K ada se pojavi p ro b lem , m o da n eete zn ati kako d a ga o tk lo n ite , ali ete sig u rn o zn a ti d a p ro g ra m ne m oe tek tako da nastav i rad . M o ra te se zau sta v iti i n ek o n egde m o ra sm isliti ta d a se rad i. M e u tim , m o d a u to m tr e n u tk u n e m a te d o v o ljn o in fo rm ac ija d a biste reili p ro b lem . Z bog tog a ga p ren o site n a vii nivo i p re d a je te n e k o m e ko je d o v o ljn o stru a n da ga rei. Jo je d n a p rilin o k o risn a o so b in a izuzetaka jeste da o n i o b in o p o jed n o stav lju ju kod za o b ra d u greaka. D a n e m a izuzetaka, m o ra li b iste d a tra ite o d re e n u g reku i d a se s n jo m b o rite n a nekoliko m esta u p ro g ra m u . Sa izuzecim a, vie n e m o rate da p rov erav ate greke p rilik o m p oziva m e to d a , p o to izuzetak g a ra n tu je d a e ih neko uh v atiti. P rob lem reavate na je d n o m m estu , a to je tzv. blok za obradu izuzetaka (engl. exception handler). O n skrau je pisan je k od a i o d v aja k o d k oji o p isu je ta elite d a u ra d ite to k o m n o rm a ln o g izvravanja, o d k o d a koji se izvrava k a d a n eto k ren e n aop ak o. O b in o je k o d za u p isivanje, itanje i p ro n a la e n je greaka m n o g o p reg led niji kad a se k oriste izuzeci nego kada se p rim e n ju je sta ri n a in ra d a s grekam a.

Osnovni izuzeci
Vanredno stanje (engl. exceptional condition) jeste p ro b le m koji spreava n astavak ra d a tekue m e to d e ili p ro g ra m sk o g blo k a. V ano je razlikovati v a n re d n o stanje o d u o b iajen og p ro b lem a, s kojim m o ete d a se izb o rite u tek u em o k ru e n ju . Kada n astu p i v an re d n o stanje, n e m o ete nastav iti o b ra d u zato to n e m a te n e o p h o d n e in fo rm acije da biste se izb o rili s p ro b le m o m u tekuem okruenju. M o ete sam o d a iskoite iz tekueg o k ru en ja i da p ren esete p ro b le m u vie o k ru en je. To se deava kada se p o jav i izuzetak. Jednostavan p rim e r je deljenje. D a b i se izbeglo d eljenje n u lo m , d o b ro je prov eriti delilac u n ap red . Ipak, ta zaista zn ai k ad a je delilac nula? M oda znate, u k o n tek stu p ro b lem a koji p o k uavate da reite o d re e n o m m e to d o m , kako ete se izb oriti s d eliocem koji je n u la. M e u tim , ako je to n eo ek iv an a v re d n o st, s n jo m ne m o ete d a se izbo rite i zbog toga m o ra te da generiete izu zetak u m e sto da nastavite s to m p u ta n jo m izvravanja. K ada n astan e izuzetak, deava se n ekoliko stvari o d je d n o m . Prvo, p rav i se objekat izuzetka na isti n ain kao svi d ru g i Javini o bjekti: u d in am ik o j m em o riji, o p e ra to ro m new . P o tom se ak tu eln a p u ta n ja izvravanja (o n a koja se ne m oe n astav iti) zaustavlja, a referenca n a o bjekat izuzetka izbacuje se iz tre n u tn o g ok ru en ja. U to m tre n u tk u , k o n tro lu izvravanja p re u z im a m e h a n iz am o b ra d e izuzetaka i p o in je da tra i odg ov arajue m esto s kog bi se nastav ilo izvravanje p ro g ra m a . To o dg o v araju e m esto je blok za obradu izuzetaka (engl. exception handler), iji je z ad a ta k d a rei p ro b lem p rim e n o m d rugaijeg p ristu p a p ro b le m u , ili d a p ro sto nastav i izvravanje o d neke d ru g e take. Kao jed n o sta v a n p rim e r g en erisan ja izuzetka, p o sm a tra jm o referencu na objekat koja se zove t. M oe se desiti d a v am b u d e p ro sle e n a referenca koja nije inicijalizovana, to ete h teti d a p ro v erite p re n eg o to p rek o te reference p o k u ate da pozovete neku m eto d u . M oete da poaljete in fo rm ac ije o greci u vie o k ru en je ako na p rav ite o bjekat koji p red stavlja te in fo rm acije i zatim ga ,,izbacite iz tek u eg k on tek sta. To se zove generisanje izuzetka (engl. throw ing an exception). Evo k ako izgleda:
if(t = = n u ll) throw new Nul1P o in te rE x c e p tio n ();

Poglavlje 12: Obrada greaka pomou izuzetaka

347

O v im se g enerie izu zetak koji u tek u em o k ru e n ju o m o g u u je odbacivanje o d g o v o rn o sti za ra z m a tra n je ove tem e. P ro b lem se, n ek im u d o m , raz m a tra n a n ek o m d ru g o m m estu . Koje je to m esto, bie u sk o ro p o kazano. Izuzeci o m o g u av a ju d a sve to rad ite sm a tra te tran sak cijo m , a izuzeci uvaju te tran sak cije: ...o s n o v n a p rem isa tran sak cija jeste d a u d is trib u ira n o m izrau n av an ju m o ra m o im ati o b ra d u izuzetaka. T ransakcije su ra u n a rsk i ekvivalent u g o v o rn o g prava. Ako b ilo ta p o e p o zlu, p ro sto em o o d b ac iti sve to izraunavanje."2 Izuzetke m o ete sm a tra ti i u g ra e n im siste m o m za p o n itav an je, p o to (u z m alo panje) u p ro g ra m u m o ete im a ti vie m esta o p o rav k a. A ko je d a n d eo p ro g ra m a zakae, izuzetak e p o n i titi taj n e u sp eh i v ra titi se n a p o z n a tu stab iln u ta k u u p ro g ra m u . Jed an o d n ajvan ijih asp ek ata izuzetaka jeste sledei: u koliko se d o g o d i neto loe, o n i p ro g ra m u n e dozvoljavaju d a n astavi izvravanje svojom u o b i a jen o m p u ta n jo m . U jezicim a kao to su C i C + + , to je zaista bio p ro b lem ; n aro ito u C -u , gde se ne m oe p rim o ra ti p ro g ra m d a p re k in e sa izvravanjem k a d a n astan e p ro b le m , p a je p ro b le m e bilo m o g u e d u g o ig n o risati i ta k o zapasti u p o tp u n o n eo d g o v arajue stanje. A ko n ita d ru g o , izuzeci o m o g u av a ju d a prisilite p ro g ra m d a se zaustavi i sao p ti ta n e valja, ili (u idealn o m sluaju) da n a te ra te p ro g ra m d a rei p ro b le m i v ra ti se u o d re e n o stab iln o stanje.

Argumenti izuzetka
P o p u t b ilo kog Javinog objekta, izuzeci se uvek prave u dinam ik o j m em o riji p o m o u o p e ra to ra new . Taj o p e ra to r zau zim a p ro s to r u m em o riji i poziva k o n stru k to r. U svim sta n d a rd n im izu zecim a p o sto je dva k o n stru k to ra: jed a n je p o d razu m ev an i, a d ru g i im a arg u m e n t tip a zn ako v nog niza u koji se m o g u sm estiti vane in fo rm acije o izuzetku:
throw new Nul 1Po in terEK cep tio n C 't = n u l l " ) ;

Z n ak ovn i niz se k asnije inoe izdvojiti p o m o u razn ih m eto d a, kao to e u sk o ro biti p ok azan o . R ezervisana re th ro w p ro u zro k u je brojna zanim ljiva deavanja. N akon to o p e ra to ro m new n ap ra v ite objekat koji prestavlja izuzetak, d o b ijen u referencu ete p roslediti rezervisanoj rei th ro w . T im e se postie da se taj objekat v rati iz m etode, iako o n a o b in o nije p ro jek to vana da vraa taj tip objekta. O b rad u izuzetaka p ojednostavljeno m oete shvatiti kao drugaiji m eh a n iza m vraanja rezultata, m ad a ovu analogiju n e bi trebalo da shvatate suvie doslovno. I o b in i p ro g ram sk i blokovi m o g u se p rek in u ti generisanjem izuzetka. U svakom sluaju, vraa se objekat izuzetka, a m eto d a ili p rogram ski b lo k se n ap u taju . Svaka slin o st sa o b i n im p o v ra tk o in iz m e to d e tu se i zavrava, p o to se m e sto p o vratk a p o tp u n o razlikuje od m esta p o v ratk a iz u o b iajen o g poziva m eto d e. (O breete se u o d g o v araju em b lo k u za o b ra d u izuzetaka koji n a steku s p o ziv im a p ro c e u ra m oe da b u d e u daljen vie nivoa o d m esta gde je izuzetak nastao.) Pored toga, m oete d a generiete proizvoljan objek at tip a T h ro w a b le , to je korenska klasa svih izuzetaka. O b i n o ete generisati d ru g aiju klasu izuzetaka za svaki tip greke.

Izjavio D im G re j, d o b itn ik T ju rin g o v e n a g ra d e za d o p r in o s n je g o v o g tim a u o b la sti tra n s a k c ija , u ra z g o v o ru o b ja v lje n o m n a www.acmqucue.org.

348

Misliti na Javi

Inform acije o greci su p redstavljene i u n u ta r o b jek ta izuzetka i im p lic itn o u im e n u klase izuzetka, d a bi neko u viem o k ru en ju shv atio ta da ra d i s vaim izu zetk o m . (T ip izuzetka je esto jed in a inform acija, p o to se u n u ta r objekta izuzetka retk o uva neto k o risn o .)

Hvatanje izuzetka
D a b iste videli kako se hvata izuzetak, p rv o m o ra te da ra z u m e te p o ja m uvane oblasti (engl. guarded region). To je d eo k o d a koji m o e d a g en erie izuzetke, iza koga sledi k o d koji o b ra u je te izuzetke.

Blok try
A ko se n alazite u n u ta r m eto d e i g en eriete izu zetak (ili neka d ru g a m e to d a k o ju p ozivate iz te m e to d e generie izuzetak), izu zetak e p ro u z ro k o v a ti n a p u ta n je m eto d e. A ko ne elite da th r o w izazove izlazak iz m e to d e, m o ete d a uv ed ete sp ecijalan b lo k u n u ta r te m eto d e koji e h vatati izuzetak. To se zove ispitni blok (engl. try block), z ato to isp ro b av ate razn e pozive m eto d a. Isp itn i b lo k je o b ia n p ro g ra m sk i b lo k isp red koga se nalazi rezervisana re try : tr y {
// Kod k o ji moe da generie izuzetke

) Kada ste paljivo traili greke u p ro g ra m sk o m jezik u koji ne p o d ra v a o b ra d u izuzetaka, m o rali ste svaki p oziv m eto d e da o k ru ite k o d o m za p rip re m u i isp itiv an je greaka, ak i ako ste istu m e to d u pozivali n ekoliko p u ta . Uz p o stu p a k o b ra d e greaka koji se koristi u Javi, sve to treb a ispitati sm eta se u isp itn i blok, a svi izuzeci se h v ataju na istom m estu . To znai da se k o d m n o g o lake pie i ita, je r se njegova sv rh a ne p rep lie s p ro v erom greaka.

Blokovi za obradu izuzetaka


G enerisani izuzetak m o ra negde da se zavri. Z avrie se u bloku za obradu izuzetaka (engl. exception handler). Takav b lo k p o sto ji za svaki tip izuzetka koji elite d a o b rad ite. B lokovi za o b ra d u izuzetaka slede o d m a h n ak o n isp itn o g b loka i o zn aen i su rezervisan o m rei catch: tr y {
// Kod k o ji moe da generie izuzetke } c a tc h (T ip l id l) { // Obrada izuzetaka tip a 1 } catch(Tip2 id2) // Obrada izuzetaka tip a 2 } catch(Tip3 id3) { // Obrada izuzetaka tip a 3

}
// i t d . ..

Poglavlje 12: Obrada greaka pomou izuzetaka

349

Svaka o d red n ica catch (b lo k za o b ra d u izu zetak a) n alik je m aloj m e to d i koja p rih v a ta sam o je d a n a rg u m e n t o d re d e n o g tip a. Id en tifik ato r (id l, id2 itd.) m o e se k o ristiti u n u ta r p ro ced u re, p o p u t a rg u m e n ta m eto d e. Id e n tifik a to r se p o n e k a d n e k o risti je r tip izu zetka p ru a d o v oljn o in fo rm ac ija za o b ra d u , ali a rg u m e n t ip ak m o ra d a postoji. Blokovi za o b ra d u izuzetaka m o ra ju da slede o d m a h posle isp itn o g bloka. Ako n asta n e izuzetak, m e h an iza m njegove o b ra d e tra i p rv i b lo k iji a rg u m e n t o d g o v a ra tip u izu zetka. P o to m se izvrava taj b lo k , n a k o n ega se sm a tra d a je izu ze tak o b ra e n . T raenje blo kova za o b ra d u izuzetak a zaustavlja se p o izv rav an ju to g b loka. Izvrava se sam o p rv i o d go v araju i blok; p o stu p a k se. razlikuje o d n a re d b e Switch u kojoj je n a k o n svake o d re d b e case p o tre b n a n are d b a break da b i se spreilo izvravanje o stalih grana. O b ra tite p a n ju n a to d a u n u ta r isp itn o g b lo k a n ekoliko poziv a razliitih m eto d a m o e da generie isti izuzetak, ali v a m za o b ra d u sv ih tre b a sa m o je d a n blok.

Prekidanje ili nastavljanje?


Postoje dva o sn o v n a m o d e la u te o riji o b ra d e izuzetaka. Java p o d rav a m o d e l prekidanja (engl. term in a tio n ),3 gde se p retp o sta v lja d a je grek a to lik o velika d a n e m a n a in a v ra titi se n a m esto gde se desio izuzetak. O n aj koji je g en erisao iz u ze tak o d lu io je d a n e p o sto ji nain za izlazak iz te situacije, i da nee d a se v ra a nazad . A lternativa se naziva nastavljanje (engl. resum ption). To zn ai da se o d blo k a za o b ra d u i/.uzetaka oekuje d a u rad i n eto kako bi reio situ aciju , n a k o n ega se p o n o v o p o k u av a s izvravanjem neisp ravn e m eto d e p o d p retp o sta v k o m d a e p o n o v lje n o izvravanje b iti uspeno. Ako k o ristite ovaj m o d el, znai da se i d alje n ad a te kako ete n astav iti izvravanje n ak o n o b ra d e izuzetka. U koliko h o e te d a n astav ite izvravanje, n e m o jte g en erisati izu zetak k ad a n ai ete n a greku, ve p o zovite m e to d u koja reava p ro b lem . D ru g i n a in je d a se isp itn i b lo k sm esti u n u ta r petlje w h ile koja ulazi u taj b lo k sve d o k re z u lta t n e p o sta n e zadovoljavajui. Istorijski gled an o , ak su i p ro g ra m e ri koji su ko ristili m o d el nastavljanja n a k raju o d ustajali od toga i poeli da k o riste iskljuivo m o d e l p rek id an ja. Iako nastavljanje na prv i po gled izgleda privlano , nije toliko k o risn o u p raksi. N ajvaniji razlog v ero v atn o je povezanost koja nastaje: nastavljaki b lo k za o b ra d u m o ra d a zn a gde je izu zetak n asta o i m o ra sad rati negenerik i kod specifian za m e sto p o jav ljiv an ja izuzetka. To oteava p isanje i o d rav an je koda, n aro ito u velikim sistem im a gde se izuzetak m o e p o jav iti na m n o g im m estim a.

Pmvljenje sopstvenih izuzetaka


N iste p rim o ra n i d a k o ristite sam o p o sto jee Javine izuzetke. P ro je k ta n ti h ije ra rh ije izuzetaka u Javi nisu m ogli da p red v id e sve greke koje b iste m o g li h te ti d a prijavite, pa m o ete da p rav ite sop stv en e izuzetke koji e n azn aiti p o se b n e p ro b le m e n a koje vaa biblioteka m oe d a naie.

'

K ao i v e in a je z ik a, in e u k o jiin a su C + + , C # , P y th o n , D itd .

350

Misliti na Javi

Vau klasu izuzetka m o ra te d a izvedete iz neke o d po sto jeih klasa izuzetaka, ako je m og ue iz o n e ije je znaenje blisko sm islu novog izuzetka (m a d a to esto nije m o gu e). N ajp rostiji nain za pisanje n ovog tip a izuzetka jeste p re p u s titi p re v o d io c u d a n a p ra v i p o d ra z u m e v a n i k o n stru k to r, to g otovo da ne zahteva p isan je koda:
//: iz u zeci/N asle ivan jelz uz etak a.java // P r a v lje n je sopstvenih izuzetaka. c lass Jednostavanlzuzetak extends Exception { } p ublic c la s s Nasleivanjelzuzetaka { p ub lic void f ( ) throvvs Jednostavanlzuzetak { System .o u t.prin tln("Bacam Jednostavanlzuzetak iz f ( ) " ) ; throw new Jednostavanlzuzetak ( ) ;

}
p u b lic s t a t ic void m a in (S trin g [] args) { Nasleivanjelzuzetaka sed = new N a sle iv a n je lz u z e ta k a (); try { s e d .f ( ) ; } catch(JednostavanIzuzetak e) { S y s te m .e rr.p rin tln (''U h v a tio sam ga! " ) ;

} }
} /* Is p is : Bacam Jednostavanlzuzetak iz f ( ) Uhvatio sam ga!

* ///:K ada p revodilac n ap rav i p o d razu m ev a n i k o n stru k to r, o n e a u to m a tsk i (i nevidljivo) p ozvati p o d raz u m e v an i k o n stru k to r osn o v n e klase. N aravn o, u o vo m slu aju neete d ob iti k o n stru k to r Je d n o s ta v a n lz u z e ta k (S trin g ), ali o n se u p rak si retko k oristi. Kao to ete videti, najvanija o so b in a izuzetka je im e klase, p a je u najveem b ro ju sluajeva izuzetak p o p u t p rik azan o g sasvim zadovoljavajui. O vde se rezu ltat tam p a na konzoli, gde ga a u to m atsk i hvata i ispitu je sistem ove knjige za ispisivanje rezultata. M e u tim , m o d a ete greku hteti da poaljete u s ta n d a rd n i izlazni to k za greke, tako to ete je upisati u to k p o d atak a System.err. To je, o b in o , bolje m esto za slanje inform acija o greci o d to k a System.out koji m o e biti p re u sm eren . Ako rezultate aljete u System.err, o n i nee b iti p re u sm e re n i zaje dn o s p o d ac im a iz to ka System .out, pa je vea verovatnoa da e ih k o risn ik p rim etiti. M oete n ap rav iti i klasu izuzetaka s k o n stru k to ro m koji im a a rg u m e n t tip a S trin g:
//: iz u zeci/P o tp u n iK o n stru k to ri.java c la s s Mojlzuzetak extends Exception { pu b lic M ojlzuzetakO {} pu b lic M ojIzu zetak(Strin g poruka) {

su per(poruka);

Poglavlje 12: Obrada greaka pomou izuzetaka

351

p ub lic c lass PotpuniKonstruktori { pu b lic s t a t ic void f ( ) throws Mojlzuzetak { System .o u t.p rin tln ("Iz b acu jem Mojlzuzetak iz metode f ( ) " ) ; throw new M o jIz u z e ta k ();

}
pu b lic s t a t ic void g () throws Mojlzuzetak { Syste m .o u t.p rin tln ("Iz b acu jem Mojlzuzetak iz metode g ( ) " ) ; throw new M ojIzuzetak("N astao u metodi g ( ) " ) ;

}
p ub lic s t a t i c void m a in (S trin g [] args) { try { f(); } catch(M ojIzuzetak e) { e .p rin tS ta c k T ra c e (S y s te m .o u t);

}
try { g(); } catch(M ojIzuzetak e) { e .p rin tS ta c k T ra c e (S y s te m .o u t);

} }
} /* Is p is : Izbacujem Mojlzuzetak iz metode f ( ) MojIzuzetak at P o tp u n iK o n s tru k to ri,f(P o tp u n iK o n s tru k to ri,ja v a :ll) at Potpu niK on stru k tori.m ain (Potp u n iK on stru k tori.java :1 9 ) Izbacujem Mojlzuzetak iz metode g () M ojlzuzetak: Nastao u metodi g () at Po tp u n iK o n stru k to ri.g (P o tp u n iK o n stru kto ri. j a v a : 15) at Potpuni Konstruktori.m ai n(Potpuni K o n s tru k to ri. j a v a :24)

* ///:D o d a ti kod je k ratak i sadri sam o dva k o n stru k to ra koji d efin iu n ain p rav ljen ja izuzetka tip a Mojlzuzetak. U d ru g o m k o n stru k to ru se, p o m o u rezervisane rei super, eksp lic itn o poziva k o n stru k to r o sn o v n e klase sa a rg u m e n to m tip a String. U blok ov im a za o b ra d u izuzetaka poziva se jed n a o d Throvvable m eto d a: printStackTrace( ). Kao to vidite iz rezultata, tim e se d o b ijaju in fo rm acije o n iz u m e to d a koje su bile pozivane pre dolaska na m esto gde se desio izuzetak. O vde se in fo rm ac ije o p o lo aju na steku izvravanja alju u to k po d atak a System.out i au to m atsk i hvataju i isp isu ju na izlazu. M ed u tim , ako pozovete p o d ra z u m e v a n u verziju:
e .p rin tS ta c k T ra c e ( ) ;

in fo rm ac ije odlaze u sta n d a rd n i izlazni to k za greke.

Veba 1: (2) N apravite klasu s m e to d o m main( ) koja generie izuzetak klase Exception u n u ta r bloka try. D odelite k o n stru k to ru za Exception arg u m en t tip a String. U hvatite izuzetak u o red b i catch i o d tam p ajte arg u m e n t tip a String. D od ajte o d re d b u finally i
o d ta m p a jte p o ru k u da biste dokazali da ste bili gde treba.

352

Misliti na Javi

Veba 2: (1) D efiniite referen cu n a o b je k at i in icijalizujte je vre d n o u null. P okuajte da pozo v ete m e to d u p o m o u te reference. S ada sm estite k o d u b lo k try--catch d a biste
u h v atili izuzetak.

Veba 3: (1) N apiite k o d za g en erisan je i hv atan je izuzetka tip a ArrayIndexOutOfBoundsException (indeks n iza izvan d o zv o ljen ih g ran ica). Veba 4: (2) N ap rav ite so p stv en u k lasu izuzetaka k o rien jem rezervisane rei extends. N apiite k o n stru k to r za tu klasu koji p rih v a ta a rg u m e n t tip a String i sm eta ga u objekat s referencom tip a String. N ap iite m e to d u koja ta m p a sau vani znakovni niz. N apravite b lo k try-catch da biste isp ro b ali n o v izuzetak. Veba 5: (3) N ap rav ite o b ra d u izuzetka p o m o d e lu nastavljanja. U p o treb ite p etlju while
koja se p o navlja sve d o k n e p resta n e generisan je izuzetka.

Izuzeci i zapisivanje
Izlaz biste m o gli i da zapiete (engl. log) p o m o u m e to d a klase java.util.logging. Iako je zapisivanje d etaljn o o b jan je n o u d o d a tk u n a lokaciji h ttp ://M indV iew .net/B ooks/B ettcrJava, e le m e n ta rn o zapisivanje je to lik o je d n o sta v n o da ga ve ovde m o em o u p o treb iti.
//: iz u z e c i/ Z a p isiva n je lz u z e ta k a .ja va // Izuzetak k oji se p r i j a v l j u j e preko Zapisnika. import ja v a .u t il.lo g g in g .* ; import j a v a . io . * ; c la s s Z ap isivan jelzu zetak a extends Exception { p riv a te s t a t i c Logger zapisnik = L o g g e r.g e tL o g g e r("Z a p is iv a n je Iz u z e tk a "); p u b lic Z a p is iv a n je lz u z e tk a () { S trin g W r ite r trag = new S t r in g W r it e r ( ) ; printStackTrace(new P r i n t W r i t e r ( t r a g ) ) ; z a p is n ik .s e v e r e (t r a g .t o S t r in g O );

1 }
p u b lic c la s s Z ap isivan je lzu zetak a { p u b lic s t a t i c void m a in (S trin g [] args) { try { throw new Z a p is iv a n je lz u z e tk a (); } c a tc h (Z a p isiva n je Iz u z e tk a e) { S y s te m .e r r.p r in tln ("U h v a tio " + e );

}
try { throw new Z a p is iv a n je lz u z e tk a ( ) ; } c a tc h (Z a p isiva n je Iz u z e tk a e) { S y ste m .e rr.p rin t1 n (''U h v a tio " + e ) ;

} }
} /* Is p is : (85% podudarnih podataka) Aug 30, 2005 4:02:31 PM Z a p isivan je lz u z e tk a <init>

Poglavlje 12: Obrada greaka pomou izuzetaka

353

SEVERE: Z ap isivan jelzu zetk a a t Z ap isivan je Iz u z e ta k a .m a in (Z a p isiva n je Iz u z e ta k a .ja va :1 9 ) Uhvatio LoggingException Aug 30, 2005 4:02:31 PM Z ap isivan jelzu zetk a <init> SEVERE: Z ap isivan jelz u z etk a a t Z ap isiva n je Iz u z e ta k a .m a in (Z a p isiva n je Iz u z e ta k a .ja va :2 4 ) Uhvatio Z a p isivan jelz u z etk a

* ///:S tatin a m e to d a Logger.getLogger( ) p ra v i zap isn ik (engl. logger), tj. ob jek at tip a Logger sa a rg u m e n to m tip a String (o b in o im e n o m p ak eta i klase u k o jim a je n asta la greka) koji svoj izlaz alje u System.err. U za p isn ik se najlake u p isu je ta k o d a se pozove m e to d a p rid ru e n a niv o u p o ru k e k o ju treb a zapisati; ovde je u p o tre b ljen a m e to d a sev ere( ). Za prav ljen je zn a k o v n o g niza p o ru k e za zapisnik, hteli b ism o d a u p o tre b im o ispis steka (engl. stack trace) na m e stu gde je izu zetak baen , ali m eto d a printStackTrace( ) p o d ra z u m e v a n o n e daje String. D a b ism o d o b ili String, m o ra m o u p o tre b iti p rek lo p ljen u printStackTrace( ) koja kao a rg u m e n t p rim a o b jek at java.io.PrintWriter. (Sve e to b iti d e taljn o o b ja n je n o u poglavlju Javin ulazn o -izla zn i sistem .) U koliko PrintWriter k o n s tru k to r u p re d a m o o b jek at tip a java.io.StringWriter, izlaz se m oe p re tv o riti u String m e to d o m to S trin g ( ). Iako je p ris tu p u p o tre b lje n u klasi Zapisivanjelzuzetka veo m a k o m fo ra n , zato to svu in fra s tru k tu ru zapisivanja u g ra u je u sam izuzetak i stoga rad i a u to m a tsk i b ez ikakve interven cije p ro g ra m e ra klijenta, ee se deava d a hvatate i zapisujete tu e izuzetke, pa p o ru k u za zap isn ik m o ra te d a generiete u b lo k u za o b ra d u izuzetaka:
//: iz u z e c i/ Z a p is i va n jelzu zetak a2 .java // Z a p is iv a n je uhvaenih izuzetaka. import ja v a .u t il.lo g g in g .* ; import j a v a . io . * ; p u b lic c la s s Zapisivanjelzu zetaka2 { p r iv a te s t a t i c Logger zapisnik = Lo gger.getLo gg er("Zapisi va n je lz u z e ta k a 2 "); s t a t i c void z a p isiIz u z etak (Ex cep tio n e) { S trin g W r ite r trag = new S t r in g W r it e r ( ) ; e .p rin tS tack T race(n e w P r in tW r it e r ( t r a g ) ) ; zapi sni k . s e v e r e (tr a g .t o S tr i ng( ) ) ;

}
p u b lic s t a t ic void m a in (S trin g [] args) { try { throw new Nul1Po in terE x cep tio n ( ) ; } c a tc h (N u llPointerEx ception e) { z a p is ilz u z e ta k (e );

} }
} /* Is p is : (90% podudarnih podataka) Aug 30, 2005 4:07:54 PM Z apisivan jelzu zetaka2 z a p isiIz u z etak

354

Misliti na Javi

SEVERE: java .la n g .N u l 1PointerException a t Z a p isivan je Iz u z etak a2 .m ain (Z ap isivan jeIz u z etak a2 .ja va :1 6 )

* ///:P o stu p ak pravljenja so p stvenog izu zetk a m o e se p ro iriti. M oete da d o d a te sopstvene k o n stru k to re i lanove:
//: izuzeci/D o d atn iElem enti.java // Dodatno r a z v ija n je k lasa izuzetaka. import s t a t ic n e t.m in d v ie w .u til. P r i n t . * ; c la s s MojIzuzetak2 extends Exception { p riv a te in t x; p u b lic M ojIzuzetak2() {} p u b lic M o jIzuzetak2 (Strin g poruka) { su per(po ruka); p ub lic M o jIzuzetak2 (Strin g poruka, in t x) { super(poruka); t h is .x = x;

}
p ub lic in t vred n o st() { retu rn x; } p u b lic S trin g getMessage() { return "D etaljn a poruka: " + x + " "+ super. getM essage();

} }
p u b lic c la s s DodatniElementi { p ub lic s t a t ic void f ( ) throws MojIzuzetak2 { p rin t("Iz b acu jem MojIzuzetak2 iz metode f ( ) 1 1 ); throw new M o jIzu zeta k2 ();

}
pu b lic s t a t ic void g () throws MojIzuzetak2 { p rin t("Iz b acu jem MojIzuzetak2 iz metode g( ) " ) ; throw new M ojIzuzetak2("Nastao u metodi g ( ) " ) ;

}
p ub lic s t a t ic void h () throws MojIzuzetak2 { p rin t("Iz b acu jem MojIzuzetak2 iz metode h ( ) " ) ; throw new M ojIzuzetak2("Nastao u metodi h ( ) " , 47);

}
pu b lic s t a t ic void m a in (S trin g [] args) tr y { f0; } catch(M ojIzuzetak2 e) { e.p rin tS ta c k T ra c e (S y s te m .o u t); {

}
try { g(); } catch(M ojIzuzetak2 e) { e .p rin tS ta c k T ra c e (S y s te m .o u t);

}
try { h () ;

Poglavlje 12: Obrada greaka pomou izuzetaka

355

} catch(M ojIzuzetak2 e) { e .p rin tS ta c k T ra c e (S y s te m .o u t); S y s te m .e r r.p r in tln ("e .v re d n o s t() = " + e .v r e d n o s t ());

} }
} /* Is p is : Izbacujem MojIzuzetak2 iz metode f ( ) MojIzuzetak2: D etaljna poruka: 0 null at D o d atn iElem en ti.f(D o d atn iElem en ti.java :2 2 ) at Dodatni E lem e n ti. mai n(DodatniE1em enti. java :3 4 ) Izbacujem MojIzuzetak2 iz metode g () MojIzuzetak2: D etaljna poruka: 0 Nastao u metodi g () a t D od atn iElem en ti.g(D odatniElem en ti. java :2 6 ) a t DodatniElem enti.m ain(Dodatni Elem en ti. java :3 9 ) Izbacujem MojIzuzetak2 iz metode h () M ojIzuzetak2: D etaljna poruka: 47 Nastao u metodi h () at D odatn iElem enti.h (D o datn iElem enti.java:30 ) at D odatniElem enti,m ain(D odatniElem enti.java:44) e .v re d n o s t() = 47

* ///:Klasi izuzetka d o d a to je polje x, uz m e to d u koja ita v re d n o st i d o d a tn i k o n stru k to r koji je postavlja. P ored toga, red efin isan a je m e to d a Throwable.getM essage( ) d a b i se d obila zanim ljivija d etaljn a p o ru k a . M eto d a getM essage( ) lii na m e to d u to S trin g ( ) za klase izuzetaka. Poto je izuzetak sam o vrsta objekta, p o stu p a k p ro iren ja klasa izuzetaka m o ete jo da nastavite. Ipak, im ajte u v id u da p ro g ra m e ri k lijenti koji k oriste vae pak ete m o d a nee ni p rim e titi ulepavanje, p o to e v ero v atn o oekivati generisanje izuzetka i n ita vie. (Tako se k oristi veina izuzetaka u Javinim b ib lio tek am a.) V eba 6: (1) N ap rav ite dve nove klase izuzetaka koje sam e a u to m a tsk i zap isu ju svoje izuzetke. D ok aite d a o n e rade.

Veba 7: ( 1) Izm enite vebu 3 tako d a o d re d b a catch zapisuje izuzetke.

Specifikacija izuzetaka
U Javi bi tre b alo da o bav estite p ro g ra m e ra k lijen ta koji poziva vau m e to d u o izuzecim a koje o n a m o e da generie. To je utivo, p o to o n aj koji poziva m e to d u ta d a ta n o zna kako d a u h vati sve m ogu e izuzetke. N aravno, ako je d o stu p a n izvorni k o d , p ro g ra m e r k lijent bi m ogao da ga pregleda i p ro n a e sve n a red b e throw, ali se b iblioteke esto ne isp o ru u ju sa izv o rn im k o d o m . Da bi se spreilo pojavljivanje p ro b lem a , Java o b ezb e u je sin ta k su (i prim orava vas da je k o ristite) koja o m o g u u je da utivo kaete p ro g ra m e ru k lijen tu koje izuzetke g enerie o d re e n a m eto d a. To je specifikacija izuzetaka i pojavljuje se n ak o n spiska arg u m e n a ta , u deklaraciji m etode. Specifikacija izuzetaka k o risti d o d a tn u rezerv isan u re throws, iza koje sledi spisak svih m o g u ih tipov a izuzetaka. D efinicija vae m e to d e m ogla b i d a izgleda ovako:
void f ( ) throws P r e v e lik i, Prem ali, DeljenjeNulom { / / . . .

356

Misliti na Javi

M e u tim , ako napiete: void f () { / / . . . to znai da m e to d a ne generie nikakve izuzetke (sem izuzetaka n asle d en ih o d klase R u n -

timeException koji se i bez specifikacije izuzetka m o g u gen erisati b ilo gde, to e b iti o p isan o kasnije). N e m o ete d a laete u specifikaciji izuzetka. U k oliko k o d u n u ta r vae m e to d e p ro u z ro k u je izuzetke, a m eto d a ih n e o b rad i, p rev o d ilac e to o tk riti i sao p tie v a m d a m o ra te o b ra d iti izu ze tak ili specifikacijom izu zetk a n azn aiti d a ga vaa m e to d a m o e g enerisati. P o d rav an jem specifikacija izuzetaka o d d n a d o v rh a , Java je m i d a e o d re e n nivo isp ra v n o sti izuzetaka b iti g a ran to v an to k o m prevoenja. O je d n o j stvari m o ete lagati: m o ete tv rd iti d a se izu zetak generie, iako se to zap rav o ne deava. P revodilac vam veru je i p rim o ra v a k o risn ik e d a s v ao m m e to d o m ra d e kao da o n a zaista generie taj izuzetak. T im e se p o stie u tisa k da je m e to d a m esto n a sta n k a izuzetka, p a kasn ije zaista m o ete d a gen eriete izuzetak n e m en ja ju i p o sto jei kod. To je va no i za p rav ljenje apstraktnih o sn o v n ih klasa i interfejsa ije e izvedene ldase ili realizacije m o d a gen erisati izuzetke. Izuzeci koji se p roveravaju i n a m e u u v rem e p rev o en ja, naziv aju se provereni izuzeci.

Veba 8: (1) N apiite klasu s m e to d o m koja g enerie izuzetak tip a d efin isan o g u vebi 4.
P ok uajte da je prevedete bez sp ecifik a je izuzetka i pog led ajte ta e vam rei prevodilac. D o d a jte o d g o v a raju u specifikaciju izuzetka. Isp ro b ajte svoju klasu i njen izu zetak u o d redbi try-catch.

Hvatanje bilo kog izuzetka


M ogue je n a p ra v iti b lok koji hvata bilo koji tip izuzetka. To se rad i h v atan jem o sn o v n o g tip a izuzetka klase E x c ep tio n (p o sto je i d ru g i tip o v i o sn o v n ih izuzetaka, ali je o sn o v a Exc e p tio n p rim e n ljiv a na skoro sve p ro g ra m e rsk e ak tiv n o sti):
catch (Ex cep tion e) { Sy ste m .o u t.p rin tln ("U h va en iz u z e ta k " );

} N a ovaj n a in bie uhvaen svaki izuzetak, pa bi ovakav blok tre b a lo staviti na kraj liste za o b ra d u . T im e se izbegava p re u z im a n je n ad len o sti o d blok o v a za o b ra d u k o n k re tn ih izuzetaka, koji b i ev en tu a ln o m ogli da slede. P oto je klasa E x c e p tio n o sn o v a svih klasa izuzetaka koje su p ro g ra m e ru vane, ovim p o stu p k o m ne d o b ija te m n o g o o d re en ijih in fo rm ac ija o izuzetku, ali m o ete da p o zo vete m e to d e njene natklase T h ro w ab Ie : S trin g getM essage( ) S trin g g etL o caIized M essag e() O n e d aju d e ta ljn u p o ru k u ili p o ru k u koja je p rilag o e n a o d re d e n o m lo k aln o m geografsk o m p o d ru ju . S trin g to S trin g ( )

Poglavlje 12: Obrada greaka pomou izuzetaka

357

V raa k ra ta k opis objekta, u k lju u ju i i d e ta ljn u p o ru k u ako p o sto ji. void p rin tS ta c k T ra c e () void p rin tS tack T race(P rin tS tream ) void p rintS tack T race(jav a.io .P rin tW riter) tam p a ju opis o b iek ta i poloaj n astan k a izuzetka n a stek u izvravanja. Stek izvravanja p rik a zu je red osled poziva m eto d a koje su dovele d o m e sta gde je n a stao izuzetak. Prva verzija k o risti sta n d a rd a n izlazni to k za greke, a d ru g a i tre a o m o g u u ju d a izab erete to k p o d a ta k a (u poglavlju faviti ulazno-izlazni sistem o b jasn ie m o zato su p o d r a n e dve vrste to k a ).

ThrowabIe fillInStackTrace()
In fo rm a c ije o tekue m sta n ju slojeva steka belei u o b je k tu tip a Throwable. O vo je korisn a funk cija u sluajevim a kad a aplikacija p o n o v o izbacuje p o sto je u greku ili izu zetak (u sk o ro e b iti vie rei o ovom e). P o red toga, iz tip a Object koji je o sn o v n i tip svih o b jek ata, p a i o b jek ata tip a Throwable, d o b ija ju se i neke d ru g e m eto d e. Jedna k o ja b i m o g la d a b u d e k o risn a za izuzetke jeste getC lass( ), ija je v red n o st o b jek at koji p red stav lja k lasu d a to g o b jek ta. O n d a m o ete d a ispitate o b jek at tip a Class i p o m o u m e to d e getN am e( ) sazn ate im e klase i p ak eta, ili m eto d o m getSim pleN am e( ) saznate sam o im e klase. Evo p rim e ra koji ilustru je korienje o sn o v n ih m e to d a klase Exception:
//: izuzeci/M etodeK1aseException.java // Prikaz metoda klase MetodeKlaseException. import s t a t ic n et.m in d view .u til . P r in t . * ; p u b lic c la s s MetodeKlaseException ( p ub lic s t a t ic void main (S t r in g [] args) try {

throw new Exception("Evo iz u z e tk a ''); } catch(Exception e) { p rin t("U h v a tio sam iz u z e ta k "); p r i n t ( "getM essage(): " + e.g etM essag e()) ; p rin t("g e tLo c a liz e d M e s sa g e (): " + e .g etL o ca lized M essa g ef)); p r in t("to S tr in g (): " + e ) ; p r in t (" p r in t S t a c k T r a c e (): " ) ; e .p rin tS ta c k T ra c e (S y ste m .o u t);

} }
}/ * Is p is : Uhvatio sam izuzetak getMessage( ) : Evo izuzetka getLo calized M essag e(): Evo izuzetka t o S t r in g ( ) : j a v a . 1ang.Exception: Evo izuzetka p rin tS ta c k T r a c e (): j a v a .1ang. Exception: Evo izuzetka at MetodeKlaseException.m ain(M etodeKlaseException.ja v a :8 )

* ///:-

358

Misliti na Javi

V idite d a m e to d e re d o m o b e zb e u ju sve vie info rm acija, je r je svaka n a d sk u p p re th o d n e .

Veba 9: (2) N ap rav ite tri nova tip a izuzetaka. N ap rav ite klasu s m e to d o m koja generie sva tr i tipa izuzetaka. U m eto d i m a in ( ) p o zo v ite m e to d u , ali u p o tre b ite sam o je d n u odre d b u catch koja e u h v atiti izuzetke sva tr i tipa.

Poloaj nastanka izuzetka na steku izvravanja


In fo rm a ja m a koje daje printStackTrace( ) m o e se p ristu p iti i d ire k tn o , m e to d o m getStackTrace( ). O n a vraa n iz elem en ata steka, o d ko jih svaki p red stav lja p o jed an njegov sloj. E le m en t b ro j n u la je n a v rh u steka i pred stav lja p o sled n ji p oziv m e to d e u to m n iz u (tak u u kojoj je ovaj o b jek at izuzetka n a p rav ljen i baen ). Poslednji ele m e n t niza, o n a j koji je n a d n u steka, jeste p rv i p oziv m e to d e u to m n izu. N ared n i p ro g ra m daje p ro stu ilustraciju:
//: izuzeci/KoJeZvao.java // Programski pristup podacima o poloaju nastanka izuzetka // na steku izvravanja.

p u b lic c la s s KoJeZvao { s t a t i c void f ( ) { // G enerii izuzetak da bi na stek iz vr a v a n ja upisao njegov // poloaj nastanka try { throw new E x ce p tio n (); ) catch (Exception e) { for(StackTraceElem ent ste : e .g e tS ta c k T ra ce O ) System .ou t.p rintln (ste.getM etho dN am e()) ;

) }
s t a t i c void g () { f ( ) ; } s t a t ic void h () { g ( ) ; } p u b lic s t a t ic void main ( S t r i ng[] args)

f 0;
System.out.printl n ("------------------------------------- " ) ;
g();

S y s te m .o u t.p rin tln ("-------------------------------- " ) ; h ();

}
} /* Is p is : f mai n f

9
mai n f

Poglavlje 12: Obrada greaka pomou izuzetaka

359

h mai n

* ///:O vde sm o ispisali sam o im e m eto d e, ali vi m o ete d a ispiete ceo StackTraceElement koji sadri d o d a tn e p o datk e.

Ponovno generisanje izuzetka


P o n ek ad ete poeleti d a p o n o v o generiete izuzetak koji ste u p rav o uhvatili, n a ro ito ako k o ristite klasu Exception za hvatan je svih izuzetaka. Poto ve im ate referen cu n a aktueln i izuzetak, sam o je p o n o v o izbacite:
catch (Exception e) ( System.out.println("Generisan je izuzetak"); throw e;

} P o n o v n im g en erisan jem , izuzetak se p ro sled u je b lo k o v im a za o b ra d u izuzetaka u sledeem niv ou o k ru en ja. Sve kasnije n ared b e catch za isti b lo k try i dalje se z an e m a ru ju . P o re d toga, uvaju se svi pod aci o izuzetku, pa b lo k u viem o k ru e n ju koji hvata o d re en i tip izuzetka m oe da ih izdvoji iz tog objekta. A ko p o n o v o generiete tekui izuzetak, in fo rm acije koje tam p a te o n je m u u m eto d i printStackTracef ) p rip a d a e m estu porekla izuzetka, a ne m e s tu na k o jem ga p o n o v o generiete. N ove inform acije o sta n ju steka m o ete da up iete p o zivan jem m e to d e filllnStackTrace( ) koja vraa objek at izuzetka, n a p rav ljen tako to su in fo rm acije o tek ue m sta n ju steka stavljene u stari objek at izuzetka. Evo kako to izgleda:
/ / : izuzeci/Ponovnogenerisanje.java // Prikaz metode f i 11In Stack T race() p u b lic c lass Ponovnogenerisanje ( pu b lic s t a t ic void f ( ) throws Exception { S y ste m .o u t.p rin tln ("P ra v im izuzetak u metodi f ( ) ) ; throw new Exception("Izbacen iz f ( ) " ) ;

}
p u b lic s t a t ic void g () throws Exception { tr y {
f0;

} catch(Exception e) { S y s te m .e r r.p r in tln ("U metodi g ( ) , e .p r in tS ta c k T r a c e ()" ) ; e .p rin tS ta c k T ra c e (S y ste m .o u t); throw e;

} }
p ub lic s t a t ic void h () throws Exception { try {

f0 ;
} catch(Exception e) {

360

Misliti na Javi

System.err.println("U metodi h(), e.printStackTraceO"); e.printStackTrace(System.out); throw (Exception)e.fillInStackTrace();

} }
public static void main(String[] args) try { {

g () ;
} catch(Exception e) { System.out.println("Uhvatio izuzetak u metodi main: printStackTrace()"); e.printStackTrace(System.out);

}
try { h(); } catch(Exception e) { System.out.println("Uhvatio izuzetak u metodi main: printStackTraceO"); e.printStackTrace(System.out);

} }
} /* Ispis: Pravim izuzetak u metodi f() U metodi g(), e.printStackTrace() java.lang.Exception: Izbacen iz f() at Ponovnogenerisanje.f(Ponovnogenerisanje.java:7) at Ponovnogenerisanje.g(Ponovnogenerisanje.java:11) at Ponovnogenerisanje.main(Ponovnogenerisanje.java:29) Uhvatio izuzetak u metodi main: printStackTrace() java.lang.Exception: izbacen iz f() at Ponovnogenerisanje.f(Ponovnogenerisanje.java:7) at Ponovnogenerisanje.g(Ponovnogenerisanje.java:11) at Ponovnogenerisanje.main(Ponovnogenerisanje.java:29) Pravim izuzetak u metodi f() U metodi h(), e.printStackTrace() java.lang.Exception: Izbacen iz f() at Ponovnogenerisanje.f(Ponovnogenerisanje.java:7) at Ponovnogenerisanje.h(Ponovnogenerisanje.java:20) at Ponovnogenerisanje.main(Ponovnogenerisanje.java:35) Uhvatio izuzetak u metodi main, printStackTrace() java.lang.Exception: Izbacen iz f() at Ponovnogenerisanje.h(Ponovnogenerisanje.java:24) at Ponovnogenerisanje.main(Ponovnogenerisanje.java:35)

* ///:Red gde se poziva m eto d a fillIn S ta c k T ra c e () p o staje novo m esto p o rek la izuzetka. M ogue je p o n o v o gen erisati izu zetak koji je razliit o d o n o g koji je uhvaen. To p ro uzro k u je isti efekat k ao k o rien je m e to d e fillIn S ta ck T rac e ( ): g u b e se in fo rm acije o izv o rn o m p o rek lu izuzetka, a p re o sta ju in fo rm a c ije koje p rip a d aju n o v o m generisanju:

Poglavlje 12: Obrada greak<i, oomou izuzetaka

361

//: izuzeci/PonovnogenerisanjeNovog.java // Ponovno generisanje objekta koji se razlikuje or5 '^ivaenog. class IzuzetakJedan extends Exception { public IzuzetakJedan(String s) { super(s); }

}
class IzuzetakDva extends Exception { public IzuzetakDva(String s) { super(s); }

}
public class PonovnogenerisanjeNovog { public static void f ( ) throws IzuzetakJedan { System.out.println("Pravim izuzetak u metodi f ( ) ) ; throw new IzuzetakJedan("izbaen iz f ( ) " ) ;

}
public static void main(String[] args) try { try { f(); } catch(IzuzetakJedan e) { System.out.println( "Uhvaen u unutranjem bloku try, e.priri: ackTrace()"); e.printStackTrace(System.out); throw new IzuzetakDva("iz unutranjeg bloka uy"); {

}
} catch(IzuzetakD va e) { System .out. pri n tln ( "Uhvaen u spoljnom bloku t r y , e . p r i n t S t r i k lr a c e ( ) " ) ; e .p rin tS ta c k T ra c e (S y s te m .o u t);

} }
} /* Is p is : Pravim izuzetak u metodi f ( ) Uhvaen u unutranjem bloku t r y , e . p rin tS tack T race( IzuzetakJedan: izbaen iz f ( ) at PonovnogenerisanjeNovog.f(Ponovnogenerisan .N ovog.java:15) at PonovnogenerisanjeNovog.main(Ponovnogene. .jeNovog.java:20) Uhvaen u spoljnom bloku t r y , e .p rin tS ta c k T ra c e () IzuzetakDva: iz unutranjeg bloka t r y at PonovnogenerisanjeNovog.main(Ponovnogenc .njeNovog.java:25)

* ///:K onaan izuzetak zna sam o da je stigao iz u n u tra n je g b lo k a try , a ne iz f ( ). N e m o ra te u o pte da b rin e te o b risa n ju p re th o n o g izu zetk a ili bilo kog d ru g o g izuzetka. Ti su ob jekti n a p rav ljen i o p e ra to ro m nevv u d in am ik o j m em o riji, pa ih sakuplja sm ea a u to m atsk i brie.

362

Misliti na Javi

Ulanavanje izuzetaka
esto je p o tre b n o u h v atiti je d a n izu zetak i g en erisati d ru g i, ne izgubivi p o d a tk e o p rv o m - to se naziva ulanavanje izuzetaka. P re objavljivanja JD K -a 1.4, p ro g ra m e ri su sam i m o rali da p iu k o d koji e ouvati p o d a tk e o p rv o b itn o m izu zetk u , ali sada sve p o tk lase o d Throvvable im aju o p ciju d a u k o n s tru k to ru p rim e o b jek at uzrok (engl. cause). Predviden o je d a uzrok b u d e p rv o b itn i izuzetak, ijim p ro sle iv an jem zadravate poloaj prv o g izuzetka na steku, iako p rav ite i g en eriete d ru g i izuzetak. Z anim ljivo je p rim e titi d a jed in e p o tk la se o d Throwable koje u k o n s tru k to ru p rim a ju a rg u m e n t uzrok jesu tr i o sn o v n e klase izuzetaka: E rror (p o m o u koje JVM prijavljuje sistem ske greke), Exception i RuntimeException. A ko h o ete da u lan ite d ru g e tipove izuzetaka, u ra d ite to m e to d o m in itC au se( ), a n e k o n stru k to ro m . Evo p rim e ra koji o m o g u av a d in am i k o d o d av an je p o lja o b je k tu tip a DinamickaPolja u v rem e izvravanja:
//: iz u zeci/D in am ick aPo lja.java // Klasa koja sebi dinamiki dodaje p o lja . // Ilu s t r u je ulanavanje izuzetaka. import s t a t ic n e t.m in d v ie w .u til. P r i n t . * ; c lass IzuzetakD inam ickihPolja extends Exception {} p ublic c la s s Dinam ickaPolja { p riv a te O b ject[] [] p o lja ; p ub lic D in a m ick a Po lja(in t p o cetn aV elicin a) { p o lja = new O b je c t[p o c e tn a V e lic in a ][2 ]; f o r ( i n t i = 0; i < p o cetn aV e licin a; i++) p o l j a [ i] = new O b je c t[] { n u ll, n u ll } ;

}
p ublic S trin g to S trin g O { S trin g B u i1der re z u lta t = new S t r in g B u i1d e r ( ) ; f o r (0 b je c t [] obj : p o lja ) { re z u lta t.a p p e n d (o b j[ 0 ] ) ; r e z u lt a t . append( " : " ) ; re z u lta t.a p p e n d (o b jl[ 1 ] ) ; re z u lta t.a p p e n d ( \ n " ) ;

}
return r e z u lt a t . t o S t r i n g ( ) ;

}
p riv a te in t h a s F ie ld (S trin g id ) { f o r ( in t i = 0; i < p o lja .le n g th ; i++) if ( i d . e q u a ls ( p o l j a [ i ] [ 0 ] ) ) return i ; return -1;

}
p riv a te in t getFieldNum ber(String id ) throws NoSuchFieldException { in t b ro jP o lja = h a s F ie ld (id ) ; if ( b r o jP o lj a = = -1)

Poglavlje 12: Obrada greaka pomou izuzetaka

363

throw new N o Su ch Field Ex cep tio n (); return b ro jP o lja ;

}
p riv a te in t m akeField (Strin g id ) { f o r ( i n t i = 0; i < p o lja .le n g th ; i++) if (p o l j a [ i ] [0] = = n u ll) { pol j a [ i ] [0] = id ; return i ;

}
// Nema praznih p o lja . Dodaemo jedno: O b je c t [][] tmp = new 0 b je ct[p o l ja .le n g th + 1] [2 ]; f o r ( i n t i = 0; i < p o lja .le n g th ; i++) tm p [i] = p o l j a f i ] ; f o r ( i n t i = p o lja .le n g th ; i < tm p.length; i++) tm p [i] = new 0 b je c t[] { n u ll, nu ll } ; p o lja = tmp; // Rekurzivan poziv s proirenim poljim a: return m a k e F ie ld (id );

}
pu b lic Object g e t F ie ld (S tr in g id ) throws NoSuchFieldException { return p o lja[g e tFie ld N u m b e r(id )] [1 ];

}
p u b lic Object s e t F ie ld (S tr in g id , Object vrednost) throws IzuzetakD inam ickihPolja { if(v re d n o s t = = n u ll) { // Veina izuzetaka nema konstruktor k o ji bi primio argument "cau se". // U tim sluajevim a morate u p o tre b iti in itC a u s e (), // dostupnu u svim potklasama od Throwable. IzuzetakD inam ickihPolja idp = new Iz u z e ta k D in a m ic k ih P o lja (); id p.in itC au se(new Nul 1Po in te rE x c e p tio n ( ) ) ; throw idp;

}
in t b ro jP o lja = h a s F ie ld (id ) ; if ( b r o jP o lj a = = -1) b ro jP o lja = m a k e F ie ld (id ); Object re z u lta t = n u l1; try { re z u lta t = g e t F ie l d ( i d ) ; // Uzmi staru vrednost } catch(NoSuchFieldException e) { // Upotrebi konstruktor k o ji prima "cau se": throw new Runtim eException(e);

}
pol ja [b ro jP o l ja ] [1] = vrednost; return r e z u lta t;

}
p ub lic s t a t ic void m a in (S trin g [] args) { Dinam ickaPolja dp = new D inam ickaPolja(3 ); p ri n t(d p );

364

Misliti na Javi

try { d p .s e tF ie ld ("d " , "Neka vrednost za d " ) ; d p .s e t F ie ld (" b r o j" , 47); d p .s e tF ie ld ("b r o j2 , 48); p r in t ( d p ) ; d p .s e tF ie ld ("d " , "Nova vrednost za d " ) ; d p .s e tF ie ld ("b r o j3 ", 11); p rin t("d p : " + d p ); p rin tC 'd p .g e tF ie ld (\ ''d \ ") : 1 1 + d p . g e t F ie ld ( " d " )) ; Object p o lje = d p .s e t F ie ld (" d " , n u l l ) ; // Izuzetak ( catch(NoSuchFieldException e) { e .p rin tS ta c k T ra c e (S y s te m .o u t); } catch(Izuzetak D in am ick ihPolja e) { e .p rin tS ta c k T ra c e (S y s te m .o u t);

} }
} /* Is p is : n u l l : n ull n u ll: n u ll n u l l : n ull d: Neka vrednost za d b ro j: 47 broj2 : 48 dp: d: Nova vrednost za d b ro j: 47 broj2: 48 broj3: 11 d p .g e tF ie ld ("d ") : Nova vrednost za d IzuzetakDi nam ickihPolja a t D in a m ick a Po lja .se tFie ld (D in a m ick a P o lja .ja v a :6 4 ) at D inam ickaPolja.m ain(Dinam ickaPolj a .j a v a :94) Caused by: j a v a . 1ang.Nul1PointerException at D in a m ic k a P o lja .s e tF ie ld (D in a m ic k a P o lja .ja v a :66) . . . 1 more

* ///:Svaki o b jek at tip a DinamickaPolja sari niz p arova Object-Object. Prvi o b jek at je id e n tifik a to r p o lja (tip a String), a d ru g i v re d n o st polja koja m oe b iti bilo kojeg tip a sem p ro sto g (bez o m o ta a ). Kada p rav ite taj objekat, p ravite zasn o v an u p retp o stav k u o to m e koliko v am p o lja treba. im pozovete setFie!d( ), o n a e p ro n ai p ostojee, tako nazv an o p olje ili e n a p ra v iti novo, i staviti u njega vau v red n o st. U koliko joj p o n e sta n e p ro sto ra , d o d ae ga p rav ljen jem niza d u eg za je d a n i k o p iran jem starih elem en ata u njega. Ako p o k u ate d a u n u tr a stavite v re d n o st null, generisae IzuzetakDinamickihPolja tako to e ga n ap rav iti i m e to d o m in itC au se( ) u m e tn u ti NullPointerException kao uzrok. Kao p o v ra tn u v re d n o st, m e to d a setField( ) pribavlja i sta ru v red n o st na m estu tog polja m e to d o m getField( ) koja m o e generisati izuzetak NoSuchFieIdException. Ako

Poglavlje 12: Obrada greaka pomou izuzetaka

365

p ro g ra m e r klijen t pozove getField( ), o n je o d g o v o ra n za o b ra d u izuzetka NoSuchFieldException, ali ukoliko taj izuzetak b u d e g en erisan u n u ta r m e to d e setF ield( ), to je p ro g ra m sk a greka, pa se NoSuchFieldException p o m o u k o n stru k to ra koji p rim a a rg u m e n t cause p retv a ra u RuntimeException. M o d a ste p rim e tili d a to S trin g ( ) u p o treb ljav a StringBuilder za p rav ljen je svog rezu ltata. O klasi StringBuilder saznaete vie u poglavlju Z n a k o vn i nizovi , ali zasad je d o voljno z n ati d a je treb a u p o treb ljav ati k ad g o d p iete u toS tring( ) koja o b u h v a ta k ru e n je u petlji, kao ovde.

Veba 10: (2) N aprav ite klasu s dve m eto d e, f ( ) i g ( ). U m e to d i g ( ) g en eriite izu ze tak n o vog tip a koji ete definisati. U m eto d i f ( ) p o zo v ite m e to d u g ( ), u h v atite n je n izu zetak i u o d re d b i catch g eneriite d ru g aiji izu zetak (d ru g o g tip a koji ete ta k o e sam i defin isati). T estirajte k o d u m e to d i m a in ( ). Veba 11: ( 1) P o n ovite p re th o d n u vebu, ali u n u ta r o d re d b e catch o m o ta jte izu zetak m e to d e g ( ) u RuntimeException.

Standardni izuzeci u Javi


Javina klasa Throwable opisu je sve to se m o e g en erisati kao izuzetak. P ostoje dve opte vrste o b jek ata tip a Throwable (vrsta o b jek ta = n asleivanje iz). Error p red stav lja sistem ske greke i greke to k o m p rev o en ja koje nije n e o p h o d n o h v atati (o sim u sp ecijaln im slu ajev im a). Exception je p ro st tip izuzetka koji m o e n astati u b ilo kojoj m e to d i klase sta n d a rd n e Javine biblioteke, kao i u m e to d a m a koje sam i p rav ite i a k c id e n tim a p rilik o m izvravanja. P rem a to m e, p ro st tip koji zan im a p ro g ra m e ra u Javi jeste Exception. N ajbolji n ain za pregled izuzetaka jeste itan je H T M L d o k u m e n ta c ije Javinog razvojn o g o k ru e n ja (JD K ). To vredi u ra d iti sam o je d n o m da bi se stekla p red stav a o razliitim izuzecim a, ali b rzo ete shvatiti da se izuzeci ne razlik u ju n i p o em u b itn o m , osim po im en u . Takoe, broj izuzetaka u Javi staln o raste i b esm islen o je da se ta m p a ju u knjizi. Svaka n ov a bib lio tek a koju n abavite o d n ezav isn o g au to ra v ero v atn o e sad ra ti i sopstvene izuzetke. V ano je razu m eti ta su izuzeci i ta se s n jim a m oe u rad iti. O sn o v n a zam isao je da im e izuzetka p red stav lja p ro b lem koji se desio, tj. d a im e b u d e relativ n o oigled n o. N isu svi izuzeci d efinisani u b iblioteci java.lang; neki su n ap rav ljen i rad i p o d rk e d ru g im b ib lio tek am a kao to su util, net i io, to se m oe zaklju iti iz p u n o g im e n a n jih o v ih klasa ili im ena klase iz koje su izvedeni. Na p rim er, svi u lazn o /izlazn i izuzeci izvedeni su iz klase java.io.IOException.

Specijalan sluaj klase RuntimeException


Prvi p rim e r u ovo m poglavlju bio je:
i f(t = = n u l1) throvv new Nul 1P o in te rE x c e p tio n ();

Bilo bi stra n o da m o ra te p roveravati svaku referencu koja se p ro sle u je m eto d i, kako bi se u tv rd ilo da li im a v red n o st n u ll (p o to ne zn a te da li je m e to d i p ro sle e n a isp rav n a referenca). Sreom , to ne m o ra te da radite, je r je ovaj p o stu p a k deo s ta n d a rd n e provere

366

Misliti na Javi

to k o m izvravanja k o ju Java sp ro v o d i u m esto vas. Ako je m e to d a po zv an a preko reference null, Java e a u to m a tsk i g en erisati izuzetak tip a NullPointerException. Z bog toga su g ornji red ovi k oda uvek suvini, iako bi treb alo o b av iti d ru g e p rovere d a se n e bi p ojavio izuzetak NullPointerException. P ostoji itava g ru p a tip o v a izuzetaka koji p rip a d a ju ovoj k ategoriji i n jih Java uvek gen erie au to m a tsk i, p a n e m o ra te d a ih n av o d ite u sp e fik aciji svojih izuzetaka. Z g o d n o je i to to su svi o n i g ru p isan i u n u ta r iste o sn o v n e klase RuntimeException koja p redstavlja savren p rim e r nasleivanja: uspo stav lja p o ro d ic u tip o v a d elim in o zajednikih o so b in a i p o n a an ja. Takoe, n ik ad a n ije p o tre b n o d a piete specifikaciju izuzetka koja e sao p tav ati d a bi m e to d a m ogla gen erisati izu zetak tip a RuntimeException (ili b ilo kojeg tip a n asleeno g o d RuntimeException), p o to su to neprovereni izuzeci (engl. unchecked exceptions). Poto RuntimeException u k azu je n a greke u p ro g ra m ira n ju , ovakav izuzetak u o p te n e m o ra te hvatati, je r se o n o b ra u je a u to m atsk i. K ada biste m o ra li d a traite izuzetke tip a RuntimeException, k o d b i p o stao p rili n o n ep reg led an . ak i ako o b i n o ne h v atate izuzetke tip a RuntimeException, m o d a ete o d lu iti da u svojim p ak etim a generiete n e k u v rs tu ovakvog izuzetka. ta se deava ako ne u h v atite ovakav izuzetak? Poto prev o d ilac ne zahteva specifikaciju takvog tip a izuzetaka, zvui ra z u m n o d a bi izuzetak RuntimeException m o g ao d a se p ro iri sve d o m eto d e m a in ( ), a da n e b u d e uhvaen. Da b ism o videli ta e se desiti u to m sluaju, p o g led ajm o sledei p rim e r: //: izuzeci/NeHvataSe.java // Zanemarivanje izuzetaka tipa RuntimeExceptions. // {ThrowsException} public class NeHvataSe { static void f() { throw new RuntimeException("Iz metode f()"); } static void g() {
f();

} public static void main(String[] args) {


g();

} } ///:Ve v id ite da je izuzetak tip a RuntimeException (i sve to je iz njega izvedeno) specijalan sluaj, p oto prevodilac ne zahteva specifikaciju ovakvih vrsta izuzetaka. Izlaz u sistem skom to k u greaka (System.err) izgleda ovako: Exception in thread "main" java.1ang.RuntimeException: Iz metode f() at NeHvataSe.f(NeHvataSe.java:7) at NeHvataSe.g(NeHvataSe.java:10) at NeHvataSe.main(NeHvataSe.java:13)

Poglavlje 12: Obrada greaka pomou izuzetaka

367

D akle, odg o vor glasi: ako izuzetak tipa RuntimeException stigne sve d o m eto de m a in () a da ne b u d e uhvaen, p rilik o m izlaska iz p ro g ram a poziva se m eto d a printStackTrace( ) za taj izuzetak. Im ajte n a u m u da u svom k o d u sm ete d a zan e m a ru jete sam o izuzetke tip a RuntimeException, p o to za o stale izuzetke prevo dilac zahteva o b ra d u . Izuzetak tip a RuntimeException z an e m a ru je te zato to p redstavlja greku u p ro g ra m ira n ju :

1 . G reka k o ju n e m o ete da o b ra d ite (p rim e ra rad i, p rim a n je reference null k o ju m eto d i p ro sle u je p ro g ra m e r k lijent). 2. G reka k oju bi, kao p ro g ra m er, treb alo da p ro v erite u svom k o d u (np r. izuzetak tip a ArrayIndexOutOfBoundsException znai d a je treb alo o b ra titi p a n ju n a d u in u n iza). Izu zetak koji n astaje na m estu 1, esto p o staje p ro b le m n a m e stu 2. Sada v am je ja sn o koliko je k o risn o p o sto jan je izuzetaka u o vo m sluaju - olakavaju p o s tu p a k p ro n a la en ja i o tk la n jan ja greaka. Z anim ljivo je p rim e titi d a se Javina teh nik a o b rad e izuzetaka ne m oe klasifikovati kao alatka specifine n am en e . O n a je projek to vana za rad s n ezgo dn im grekam a to k o m izvravanja koje se deavaju usled delovanja inilaca van d om aaja vaeg koda, ali je o d presu d n e van o sti i za o d re e n e vrste p ro g ram ersk ih greaka koje prevodilac n e m oe da otkrije.

Veba 12: (3) Izm en ite p ro g ra m unutrasnjeklase/Sekvenca.java tako d a generie od g o v araju i izu ze tak ako p o k u a te d a u m e tn e te previe elem enata.

ienje odredbom fin ally


C esto p ostoie delovi k o d a koje elite da izvrite bez o bzira na to da li je u n u ta r isp itn o g b lo ka n asta o izuzetak. To je o b in o sluaj u n ek im o p eracijam a koje ne predstavljaju o p o ravak m e m o rije (p o to se o to m e b rin e sakuplja sm ea). Da bi se p ostigao ovaj efekat, koristi se o d re d b a fin a lly 1 posle svih blokova za o b ra d u izuzetaka. P rem a to m e , ko m p letan o d eljak za o b ra d u izuzetaka izgleda ovako:
Try { // uvana o b la s t: Opasne ak tivn o s ti // koje mogu da generiu A, B i l i C } catch(A a l ) { // Procedura za obradu s it u a c ije A } catch (B b l) // Procedura za obradu s it u a c ije B } catch (C c l) // Procedura za obradu s it u a c ije C } fin a lly {

// A k tiv n o sti koje se uvek deavaju

}
4 O b r a d a iz u z eta k a u je z ik u C + + n e m a o re b u c n ja o b a v lja ti d e s tr u k to ri.

finally, p o to se o s la n ja n a to d a e o v a k v u v rs tu i-

368

Misliti na Javi

D a biste se uverili d a se o d re d b a fin a lly uvek izvrava, p o k re n ite sledei p rog ram :
//: iz u z e c i/ P rim e rZ a F in a lly .ja v a // Odredba f i n a l l y se uvek iz v r a v a . c lass IzuzetakTri extends Exception { } p ub lic c la s s Prim e rZ a Fin a lly { s t a t ic in t broj = 0; p ub lic s t a t ic void m a in (S trin g [] args) { w h ile (tru e ) { try { // Sufiksno uveavanje p rvi put daje vrednost nula: if(broj+ + = = 0) throw new Iz u z e ta k T ri( ) ; S y ste m .o u t.p rin tln ("B e z iz u z e ta k a "); } catch (Izu zetak T ri e) { S y s te m .o u t.p rin tln ("Iz u z e ta k T ri" ) ; } fin a lly { Sy s te m .o u t.p rin tln ("U bloku f i n a l l y " ) ; if ( b r o j = = 2) break; // za vrsi p e tlju w hile

} } }
} /* Is p is : IzuzetakTri U bloku f i n a l l y Bez izuzetka U bloku f i n a l l y

* ///:Iz rezultata vidite d a se o d re d b a fin ally uvek izvrava, bez o b z ira n a to da li se izuzetak generie ili ne. Na osno v u ovog p ro g ra m a m o ete zakljuiti kako da se iz b o rite s in jen ico m d a izuzeci u Javi n e dozvoljavaju vraan je n a m esto n astan k a izuzetka, kao to je ran ije p o m en u to . A ko b lok t r y sm cstite u n u ta r p etlje, m oete da uvedete uslov koji m o ra da b ud e zadovoljen pre nego to se n astavi izvravanje p ro g ra m a . M oete da d o d a te i statiki b ro ja ili neki drugi m eh an izam koji e o m o g u iti petlji da isp ro b a nekoliko p ristu p a pre nego to o d u stan e. Na taj n ain vai p ro g ra m i m o gu da p o sta n u rob usniji.

emu slui odredba finally?


U jeziku koji n em a sakuplja sm ea ni au to m atsk e pozive d e stru k to ra ,5 o d re d b a finally je vana zato to p ro g ra m e ru o m o g u u je da g aran tu je o slo baan je m em o rije, bez o bzira na to ta se deava u blok u try . M e u tim , Java im a sakuplja sm ea, pa o slo baan je m em orije
5 D estru k to r je funkcija koja se poziva kad god neki objekat prestan e da se koristi. Uvek se zna tano gde i kada d estru k to r biva pozvan. U jeziku C + + d e stru k to r se poziva autom atski, a i C# (koji je m no g o sliniji Javi) u m e da au to m atsk i un iti n ep o treb n e objekte.

Poglavlje 12: Obrada greaka pomou izuzetaka

369

gotovo n ik ad nije pro b lem . Uz to, n e m a d e stru k to re koji b i bili pozivani. D akle, k ad a u Javi treb a k o ristiti o d re d b u finally? O d re d b a lin a lly je n e o p h o d n a k ad a u p rv o b itn o stan je m o ra te d a v ra tite n eto to nije m e m o rija . To m oe b iti zatv aran je o tv o ren e d ato tek e ili rask id veze s m reo m , b risan je neega to ste n acrta li n a e k ra n u , ili ak p ritisk a n je n ek o g prek id aa iz sp o ljn o g sveta, kao to je m o d e lo v an o u sledeem p rim e ru :
//: iz u z e c i/ P a liG a s i.ja v a import s t a t i c n e t.m in d v ie w .u til. P r i n t . * ; p u b lic c la s s Prekidac { p riv a te boolean s ta n je = f a ls e ; p u b lic boolean re a d () { retu rn s ta n je ; } p u b lic void u k lju c e n () { s ta n je = tru e ; p r i n t ( t h i s ) ;} p u b lic void is k lju c e n () { s ta n je = f a ls e ; p r i n t ( t h i s ) ; } p u b lic S trin g to S trin g O { retu rn s ta n je ? "u k lju ce n " : " is k lju c e n " ; }

} III-/ / : iz u z e c i/ Iz u z e ta k P a liG a s i1.ja va


p u b lic c la s s Iz u z e ta k P a liG a s i1 extends Exception { } / / / : //: i z u z e c i/Iz u z e ta k P a liG a s i2 .ja v a p u b lic c la s s Iz u z e ta k P a liG a s i2 extends Exception { } ///://: iz u z e c i/ P re k id a c P a liG a s i. ja va // emu slu i odredba f i n a l l y ? p u b lic c la s s Prekid a c P a liG a s i { p riv a te s t a t ic Prekidac pr = new P re k id a c O ; p ub lic s t a t ic void f ( ) throws Iz u z etak P aliG asi 1, Iz u z e ta k P a liG a s i2 {} p u b lic s t a t ic void m a in (S trin g [] args) { try { p r .u k lju c e n (); // Kod k o ji moze da generie iz u z e tk e ... f0; pr. is k l j u c e n ( ) ; } c a tc h (Iz u z e ta k P a liG a s il e) { S y s te m .o u t.p rin tln ("Iz u z e ta k P a liG a s i1 "); pr. i skl jucen ( ) ; } c a tc h (Iz u z e ta k P a liG asi 2 e) { S y s te m .o u t.p rin tln ("Iz u z e ta k P a liG a s i2 "); p r . is k lj u c e n ( ) ;

} }
} /* Is p is : ukljucen is k l jucen

* ///:-

370

Misliti na Javi

Cilj je o sig u rati d a p rek id a b u d e iskljuen k ad a se zavri izvravanje m eto d e m a in ( ), p a se pr.iskljucen( ) stavlja n a kraj isp itn o g b loka i n a kraj svakog b lo ka za o b ra d u izuzetaka. M e u tim , m og u e je d a n astan e izu zetak koji ovde nije uhv aen, p a bi m e to d a pr.isk lju cen ( ) bila preskoena. P o m o u o d re d b e fmally, k o d za ienje m oete d a sm estite u n u ta r isp itn o g b lok a n a sam o je d n o m m estu :
// : iz u z e c i/ U z F in a lly .ja v a // F in a lly garantuje i e n je . pu b lic c la s s U z F in a lly { s t a t ic Prekidac pr = new P r e k id a c (); p ub lic s t a t ic void m a in (S trin g [] args) { try { pr.u kl ju c e n ( ) ; // Kod k o ji moe da generie iz u z e tk e ... P r e k id a c P a liG a s i.f () ; } c a tc h (Iz u z e ta k P a liG a s i1 e) { S y s te m .o u t.p rin tln ("Iz u z e ta k P a liG a s i1 "); } catc h (Iz u z e ta k P a liG a si2 e) { S y s te m .o u t.p rin tln ("Iz u z e ta k P a liG a s i2 "); } fin a lly { p r . is k l ju c e n ( ) ;

; }

} /* Is p is : ukl jucen is k lju c e n

* ///:O v de je m e to d a pr.iskljucen( ) p rem eten a n a m esto gde e se sig u rn o izvravati bez o b zira na to ta se desi. ak i u sluajevim a kada se izuzetak ne u hvati u tek u em sk u p u blokova c a tc h , finally e se izvriti p re nego to m eh an izam za o b ra d u izuzetaka n astavi da trai b lo k za o b rad u na sledeem nivou:
//: iz u z e c i/ U v e k F in a lly .ja v a // F in a lly se uvek iz vr a v a . import s t a t ic n e t.m in d v ie w .u til. P r i n t . * ; c la s s Iz u z e ta k C e tiri extends Exception { } p u b lic c lass U ve k Fin ally { p ub lic s t a t ic void m a in (S trin g [] args) Sy ste m .o u t.p rin tln ( "Ulazak u p rvi is p it n i b lo k "); tr y { S y s te m .o u t.p rin tln ( "Ulazak u drugi is p it n i b lo k "); try {

Poglavlje 12: Obrada greaka pomou izuzetaka

371

throw new Iz u z e ta k C e tiri( ) ; } fin a lly { S y s te m .o u t.p rin tln ( " F in a lly u drugom ispitnom b lo k u ");

}
} c a tc h (Iz u z e ta k C e tiri e) { S y s te m .o u t.p rin tln ( "Uhvaen Iz u z e ta k C e tiri u prvom ispitnom b lo k u "); } f in a lly { S y s te m .o u t .p r in tln ("F in a lly u prvom ispitnom b lo k u ");

} }
} /* Is p is : Ulazak u prvi is p it n i bolk Ulazak u drugi is p it n i bolk F in a lly u drugom ispitnom bloku Uhvaen Iz u z e tak C e tiri u prvom ispitnom bloku F in a lly u prvom ispitnom bloku

} ///:N aredba finally e se izvravati i u situ acijam a u k o jim a u estv u ju n a re d b e break i continue. O b ra tite p a n ju na sledee: u z o zn aen break i ozn aen continue, fmally u kida p o tre b u za n are d b o m goto u Javi.

Veba 13: (2) P ro m en ite vebu 9 d o d a v an je m o d re d b e finally. U verite se d a se b lo k finally izvrava, ak i ako je g enerisan izuzetak NullPointerException. Veba 14: (2) Pokaite da p rek id a PrekidacPaliGasi.java m o e d a se p o k v ari tako to ete generisati izuzetak RuntimeException u n u ta r b loka try. Veba 15: (2) Pokaite da UzFinally.java ne zakazuje tak o to ete g en erisati izuzetak RuntimeException u n u ta r b lo k a try.

Upotreba bloka finally tokom povratka iz metode pomou rezervisane rei return
Poto se blok fin ally uvek izvrava, m o g u je p o v ra ta k s vie taaka m eto d e , a d a se i dalje m oe jem iti da e v ano ienje b iti obavljeno:
//: i zuzeci/Vi sePovratakalzM etode.java import s t a t ic n e t.m in d vie w .u ti1. P r i n t . *; pu b lic c la s s VisePovratakalzM etode { pu b lic s t a t ic void f ( i n t i ) { p r i n t ( " I n i c i j a l i z a c i j a koja zahteva i e n je " ); try { p rin t("T a k a 1 "); if(i = = 1) retu rn ; p rintC 'T aka 2 " ); if (i = = 2) retu rn ;

372

Misliti na Javi

p rin t("T a k a 3 " ) ; if (i = = 3) retu rn ; p r in t C 'K r a j " ); re tu rn ; } fin a lly { p rin t("O b a v lja n je i e n ja '1 );

} }
p u b lic s t a t i c void m a in (S trin g [] args) { f o r ( i n t i = 1; i < = 4; i++) f(i);

}
} /* Is p is : I n i c i j a l i z a c i j a koja Taka 1 O b avljan je i e n ja I n i c i j a l i z a c i j a koja Taka 1 Taka 2 O b avljan je i e n ja I n i c i j a l i z a c i j a koja Taka 1 Taka 2 Taka 3 O b avljan je i e n ja I n i c i j a l i z a c i j a koja Taka 1 Taka 2 Taka 3 Kraj O b avljan je i e n ja zahteva i e n je

zahteva i e n je

zahteva i e n je

zahteva i e n je

* ///:Iz re z u ltata v idite da nije v an o gde se v raate iz klase koja sadri o d re d b u finally.

Veba 16: (2) P rep rav ite p ro g ra m ponovnaupotreba/CADSystem.java tako da pokaete da se p ra v iln o ienje jem i ak i kada se p o v ra ta k rezerv isan o m reju return obavi iz sred in e b lo k a try-finally. Veba 17: (3) P reprav ite p ro g ra m polimorfizam/Zaba.java tako d a p rav iln o ienje jem i b lo k o m try-finally, i p o k aite d a to rad i ak i kada se rezerv isan o m reju return v ratite iz sre d in e b lok a try-finally.

Mana: izgubljeni izuzetak


R ealizacija izu ze tak a u Javi je izv an red n a, ali, n aalost, im a p ro p u sta . Iako izuzeci ukazuju na slabu tak u u p ro g ra m u i ne b i n ik ad a sm eli da se ig n o riu , m o g u e je da se izuzetak zagubi. To m o e d a se desi kada se k o risti o d re d b a finally:

Poglavlje 12: Obrada greaka pomou izuzetaka

373

// : izu zeci/Zag ub ljen aPoruka.java // Kako se izuzetak moe z a g u b iti. c la s s VeomaVazanlzuzetak extends Exception { p u b lic S trin g to S tr in g () { return "Veoma vaan iz u z e ta k !";

} }
c la s s T riv ija la n lz u z e ta k extends Exception { p u b lic S trin g to S trin g O { return " T r iv ija la n iz u zeta k";

} }
p u b lic c la s s ZagubljenaPoruka { void f ( ) throws VeomaVazanlzuzetak { throw new VeomaVazanlzuzetakO;

}
void p o c is t iO throws T riv ija la n lz u z e ta k { throw new T r iv ija la n lz u z e t a k ( ) ;

}
p u b lic s t a t ic void m a in (S trin g [] args) { try { ZagubljenaPoruka lm = new Z agu bljenaPoru ka(); try { lm .f ( ) ; } fin a lly { lm .p o c is ti ( ) ;

}
} catch (Exception e) { System .out .pri n t l n ( e ) ;

} }
} /* Is p is : T r iv ij a l a n izuzetak

* III-V idite d a n e m a n azn ak a o p risu stv u izuzetka tip a VeomaVazanlzuzetak koji je zam enjen izuze tkom tip a Trivijalanlzuzetak u o d red b i finally. To je p rilin o o zb iljan n e d o sta tak, je r znai da izuzetak m oe p o tp u n o da se izgubi i to na n ain koji je m n o g o tee o tk riti nego u g o rn je m p rim e ru . N a su p ro t to m e, u jeziku C + + se situacija, kad a se d ru g i izuzetak generie pre nego to je prvi o b ra e n , sm a tra velikom grekom u p ro g ra m ira n ju . M o d a e u b u d u im verzijam a Jave ovaj p ro b lem b iti ispravljen (s d ru g e stra n e , m e to d u koja generie izuzetak, kao to je p o cisti( ) u g o rn jem p rim e ru , o b i n o ete sm estiti u n u ta r o d re d b e try-catch).

374

Misliti na Javi

Jo jed n o sta v n iji n a in d a izgu b ite izu ze tak jeste o b ian p o v ra ta k (p o m o u rezervisane rei return) iz b lo k a finally:
//: iz u z e c i/ P rig u s iv a c lz u z e ta k a .ja v a public c la s s P rig u sivaclzu zetak a { p ub lic s t a t i c void m a in (S trin g [] args) { try { throw new R untim eException(); ) fin a lly { // Upotreba rez ervisan e re i 'r e t u r n ' u bloku f i n a l l y // svaki baeni izuzetak in i nemim. re tu rn ;

) } } /// = A ko p o k re n e te ovaj p ro g ra m , v ideete d a nita n e ispisuje n a izlazu, iako se generie jed an izuzetak.

Veba 18: (3) D o d a jte d ru g i nivo g u b itk a izuzetaka u ZagubljenaPoruka.java d a b i Trivijalanlzuzetak sam sebe z a m e n io treim izuzetkom . Veba 19: (2) Reite p ro b le m u p ro g ra m u ZagubljenaPoruka.java tak o to e se poziv u vati u b lo k u finally.

Ogranienja izuzetaka
Kada redefiniete m e to d u , m o ete da generiete sam o izuzetke koji su zadati u verziji m eto d e iz o sn o v n e klase. To je k o risn o o g ran ien je, p o to znai d a e k o d koji ra d i sa osnovn o m klaso m a u to m a tsk i ra d iti i sa sv ak om klasom izved en o m iz nje (to je, naravno, o snov n i k o n c e p t o b jek tn o o rije n tisa n o g p ro g ra m ira n ja ), u k lju u ju i i izuzetke. O vaj p rim e r ilu stru je v rste o g ra n i e n ja koja se u v rem e p rev o en ja n am e u izuzecim a:
//: i zuzeci/Storm yInning.ja va // R edefinisane metode mogu da generiu samo izuzetke navedene u njihovim // osnovnim klasama, i l i izuzetke izvedene iz izuzetaka osnovne klase. c la s s B aseb al1Exception extends Exception {( c la s s Foul extends Basebal1Exception { ) c la s s S t r ik e extends BaseballEx ceptio n {} ab s tra ct c la s s Inning { p u b lic In n in g () throws Basebal1Exception {} p u b lic void event () throws B asebal1Exception { // Zapravo ne mora da generie nikakav izuzetak

}
p ub lic a b s tra ct void a t B a t () throws S t r ik e , F o u l; p ub lic void w alk () { } // Ne generie izuzetke koje prevodilac proverava

Poglavlje 12: Obrada greaka pomou izuzetaka

375

c la s s StormException extends Exception {} c la s s RainedOut extends StormException { } c la s s PopFoul extends Foul {} in te rfa c e Storm { p u b lic void e v e n t() throws RainedOut; p u b lic void ra in H ard () throws RainedOut;

}
p u b lic c la s s StormyInning extends Inning implements Storm { // Dozvoljeno j e dodavati nove izuzetke za konstruktore, a l i morate da se // pozabavite izuzecima konstruktora osnovne klase: p u b lic Storm yInning() throws RainedOut, BaseballEx ception { } p u b lic Sto rm yIn n in g (Strin g s) throws F o u l, B aseballEx cep tion { } // Standardne metode moraju da se prilagoavaju osnovnoj k la s i: // ! void w a lk () throws PopFoul { } //Greka tokom prevodenja // N IJE DOZVOljENO dodavanje izuzetaka // postojeim metodama iz osnovne klase: //! p u b lic void eve n t() throws RainedOut { } // Ako metoda ve ne postoji u osnovnoj k la s i, izuzetak se moe dodati: p u b lic void ra in H ard () throws RainedOut { } // Moete o d lu iti da uopte ne g en eriete izuzetke, // ak i ako to radi osnovna klasa: p u b lic void e v e n t() {} // R edefinisane metode mogu da generiu nasledene izuzetke: pu b lic void a t B a t () throws PopFoul {} p u b lic s t a t ic void m a in (S trin g [] args) { try { StormyInning si = new S to rm yIn n in g (); s i,a tB a t(); } catch(PopFoul e) { S y ste m .o u t.p rin tln ("P o p fo u l'1 ); } catch(RainedOut e) { System .out. p r in t ln ( " R a ined o u t " ) ; } catch (Base b allEx cep tio n e) { S y s te m .o u t.p rin tln ("G e n e ric baseball ex c e p tio n ");

}
// S it u a c ija koja n ije gen erisala izuzetak u izvedenoj v e r z i j i . try { // ta se deava pri k o n ve rz iji tip a u o p t ij i? Inning i = new Sto rm yIn n in g (); i . a tB a t( ) ; // Morate da u h va tite izuzetke // v e r z ije metoda u osnovnoj k la s i: } c a tc h (S tr ik e e) {

376

Misliti na Javi

System .out.print1 n ( " S t r i k e " ) ; } catch(Foul e) ( S y s te m .o u t.p rin tln ("F o u l" ) ; } catch(RainedOut e) { Sy ste m .o u t.p rin tln ("R a in e d o u t " ) ; } c atch (B aseb al1Exception e) { Sy ste m .o u t.p rin tln ( "Generic baseball e x c e p tio n ");

} } } ///:V idite kako u klasi Inning i k o n s tru k to r i m e to d a e v e n t( ) tv rd e d a e g en erisati izuzetak, ali to n e rade. To je isp rav n o zato to o m o g u u je d a p rim o ra te k o risn ik a d a u h v ati sve izuzetke koji m o g u d a n asta n u u red efin isan im v erzijam a m e to d e ev en t( ). Isto vai i za a p stra k tn e m eto d e, kao to se v id i u a tB a t( ). Interfejs Storm je zan im ljiv je r sadri je d n u m e to d u - e v e n t( ) - k oja je d efin isan a u Inning, i je d n u m e to d u koja nije definisana. O b e m e to d e g e n eriu n o v tip izuzetka, RainedO ut. Kada klasa Storm yInning nasledi Inning i realizuje Storm , vid eete d a m eto d a event( ) interfejsa Storm ne inoe da p ro m e n i specifikaciju izuzetka m e to d e event( ) u klasi Inning. I o vde to im a sm isla, inae n ik ad a ne b iste znali d a li ste u hvatili p rav i izuzetak kada rad ite sa o sn o v n o m klasom . N aravno, u koliko se m e to d a o p isan a u in terfejsu ne nalazi u o sn o v n o j klasi, p o p u t ra in H a rd ( ), o n a m o e d a g en erie so p stv en e izuzetke. O g ranien ja za izuzetke ne p rim e n ju ju se n a k o n stru k to re . U Storm yInning vidite da k o n stru k to r m oe da generie ta go d eli, b ez ob zira n a to ta generie k o n stru k to r osnovne klase. M e u tim , p oto k o n stru k to r o sn o v n e klase uvek m o ra da se nekak o pozove (ovde se p o d ra zu m e v a n i k o n stru k to r poziva a u to m atsk i), k o n stru k to r izvedene klase m o ra da deklarie sve izuzetke k o n stru k to ra o sn o v n e klase u svojoj sp e fik aciji izuzetaka. K o n stru k to r izvedene klase ne m oe da u hvati izuzetke koje je g en erisao k o n stru k to r osno v n e klase. StormyInning.walk( ) nee b iti prev ed en zato to gen erie izuzetak, to nije sluaj s m e to d o m Inning.w alk( ). Kada bi to bilo d ozvoljeno, m ogli b iste da n ap iete k o d koji bi pozivao Inning.w alk( ) i ne bi u o p te m o ra o da o b ra u je izuzetke, ali bi se tad a , nakon zam e n e o bjekta klase koja je izvedena iz klase Inning, g enerisali izuzeci i k o d bi zap ao u p ro b lem e. P rim o rav an jem m eto d a izvedene klase da p o tu ju specifikacije izuzetaka m eto d a o sn o v n e klase, o m o g u u je se zam e n jiv o st objek ata. R edefinisana m e to d a event( ) p o k azu je da verzija m e to d a izvedene klase m o e o d lu iti da n e generie nikakve izuzetke, ak i ako to ini verzija o sn o v n e klase. I ovo je u red u zato to ne p ro u z ro k u je p ro b lem e u k o d u koji je ve n ap isan , p o d p re tp o sta v k o m da verzija o sn o v n e klase generie izuzetke. Slina logika se p rim e n ju je i na m e to d u atB a t( ) koja generie izuzetak tip a PopFouI izveden iz izuzetka tip a Foul koji g enerie verzija m e to d e atBat( ) o sn o v n e klase. Ako n eko nap ie k o d koji rad i s klaso m Inning i poziva m e to d u atBat( ), m o ra e da hvata izuzetak tip a Foul. Poto je izu zetak tip a PopFoul izveden iz Foul, blo k za o b ra d u izuzetaka e hvatati i PopFoul.

Poglavjje 12: Obrada greaka pomou izuzetaka

377

Poslednje to nas z a n im a je m eto d a m a in ( ). U njoj m o ete d a vidite sledee: ako rad ite sa o b je k to m tip a Storm yInning, p rev o d ilac vas p rim o ra v a d a hvatate sam o izuzetke koji su specifini za tu klasu, ali ako k o n v ertu jete o b je k at u p ro s t tip , p rev od ilac vas s p ra v o m p rim o ra v a da hvatate izuzetke o sn o v n o g tip a. Sva ova o g ran ie n ja ine te h n ik u za o b ra d u izuzetaka m n o g o sn a n ijo m .6 K orisno je u v id eti sledee: iako p rev od ilac to k o m n asle iv an ja p o d r av a specifikacije izuzetaka, o n e n isu d eo p o tp isa m eto d e koji se sastoji sam o o d im e n a m eto d e i tip o v a arg u m e n a ta. Stoga ne m o ete d a p rek lo p ite m e to d e p re m a s p e fik a ja m a izuzetaka. P ored toga, sam o zato to specifikacija izuzetka p o sto ji u verziji m e to d e iz o sn o v n e klase, ne znai d a o n a m o ra d a p o sto ji i u verziji m eto d e u izvedenoj klasi. O vo se p rili n o razlikuje o d p rav ila nasleivanja, k ad a m e to d a o sn o v n e klase m o ra d a p o sto ji i u izvedenoj klasi. D ru g im reim a, interfejs specifikacije izuzetka" za o d re d e n u m e to d u m oe d a se suzi to k o m nasleivanja i redefin isan ja, ali se nee p ro iriti, to je u p rav o su p ro tn o p ra v ilu za in terfejs klase to k o m nasleivanja.

Veba 20: (3) P ro m e n ite Storm yInning.java d o d a v a n je m izu zetk a tip a UmpireArgum ent i m e to d a koji ga gen eriu . T estirajte iz m e n je n u h ijerarh iju .

Konstruktori
P rilikom pisanja koda sa izuzecim a, p o se b n o je v an o d a se uvek pitate: Ako n a sta n e izuzetak, hoe li o b jek at biti p ra v iln o poien? U veini sluajeva sve je u red u , ali se u k o n stru k to rim a pojavljuje p ro b lem . K o n stru k to r stavlja o b jek at u b e z b e d n o p o e tn o stanje, ali m oe d a izvodi neke o p eracije, npr. o tv a ran je d ato te k a , koje se n e b riu sve d o k korisn ik ne zavri rad sa o b je k to m i pozove sp ecijaln u m e to d u za ienje. A ko generiete izuzetak u n u ta r k o n stru k to ra , ienje se m o d a nee p ra v iln o izvriti. To znai da m o ra te biti p o seb n o o b azriv i d o k sastavljate k o n stru k to r. Poto ste u p rav o savladali o d re d b u finally, m o d a m islite d a je o n a pravo reenje. Ipak, nije toliko jed n o sta v n o , p o to fin ally svaki p u t izvrava k o d za ienje. U koliko k o n s tru k to r b u d e p re k in u t to k o m svog izvravanja, to m oe zn aiti da nije usp eo d a n apravi deo o b jek ta koji e biti poien u b lo k u finally. U sledeem p rim e ru pravi se klasa U la z n a D a to te k a koja o tv a ra d a to te k u i o m o g u u je itan je p o je d n o g reda. O n a k oristi klase F ile R ea d er i B u ffe re d R e a d e r iz sta n d a rd n e Javine U /I b iblioteke. O n jim a e biti rei u poglavlju Javin ula zn o -izla zn i sistem , ali o n e su toliko jed n o sta v n e da ete bez p ro b lem a ra z u m e ti n jih o v o o sn o v n o korienje:
//: izuzeci/lllazn aD atoteka.java // O b ra tite panju na izuzetke u konstruktorim a. import j a v a . io . * ; p u b lic c la s s UlaznaDatoteka { p riv a te BufferedReader in ;

U ISO sta n d a rd za C + + d o d ata su slina ogranienja kojim a se zahteva d a izuzeci izvedenih m eto da b u d u isti kao izuzeci koje generie m etoda osnovne klase, ili izvedeni iz njih. To je jed ini sluaj kada je C + + zaista u stan ju da proverava specifikacije izuzetaka toko m prevoenja.

378

Misliti na Javi

pu b lic U laznaD atoteka(String imeDatoteke) throvvs Exception { try { in = new BufferedReader(new File R e a d e r(im e D a to te k e )); // Jo koda k o ji bi mogao da generie izuzetke } catch(FileNotFoundException e) { System .o u t.p rin tln ("N isam naao " + im eDatoteke); // N ije otvorena, pa nemojte ni da j e z a tva rate throw e; } catch(Exception e) { // Svi o s t a li izuzeci moraju da je zatvore try { in .c lo s e (); } catch(IO Exception e2) { S y s te m .o u t .p r in tln ("in .c lo s e () neuspean");

}
throw e; // G enerii ponovo } fin a lly { // Nemojte j e z a tv a ra ti ovde! ! !

} }
S trin g c it a jR e d () { S trin g s; try { s = in .r e a d L in e (); } catch(IO Exception e) { throw new R u n tim eEx cep tio n ("cita jR e d () neuspena");

}
return s;

}
p ub lic void c is c e n je () { try { in .c lo s e O ; S y s te m .o u t.p rin tln ("c is c e n je () u speno"); } catch(IO Exception e2) { throw new RuntimeException( " i n . c l o se() neuspena");

} } } ///= K o n stru k to r klase UlaznaDatoteka p rim a a rg u m e n t tip a String, a to je im e dato tek e koju elite d a o tv o rite. U n u tar bioka try p rav i se o b jek at klase FileReader p o m o u im en a te d atoteke. O vakav o b jek at nije p o seb n o k o ristan sve d o k ga n e u p o tre b ite za p ravljenje o b je k ta tipa BufferedReader s kojim m o ete i da k o m u n icira te. Jedna o d p re d n o sti klase UlaznaDatoteka jeste to to o b jed in ju je ove dve operacije. A ko k o n stru k to r klase FileReader ne uspe da obavi zadatak, g enerisae izuzetak FileNotFoundException. Taj izuzetak se m o ra u h v atiti zasebno, je r je to je d in i sluaj kada ne elite d a zatv o rite d ato te k u , p o to o n a nije ni o tv o ren a. Svi drugi b lokovi catch m o ra ju da zatvore d a to tek u , p o to je bila o tv o ren a u v rem e n a stan k a izuzetka. (N arav n o , to je k o m plikovanije ako nekoliko m e to d a m oe d a g enerie izuzetak FileNotFoundException.

Poglavlje 12: Obrada greaka pomou izuzetaka

379

U to m sluaju, m od a ete p o d e liti k o d u nekoliko blokova try.) M eto d a close( ) m ogla bi d a g enerie izuzetak, p a se o n isp itu je i h vata ak i ako je g en erisan u n u ta r b lo k a d ru g e o d re d b e catch - za Javinog p rev o d io ca, to je jo sam o je d a n p a r v itiastih zagrada. N ako n izvoenja lo k aln ih o peracija, p o n o v o se generie izuzetak. To je u red u , po to k o n stru k to r nije b io uspean, a m e to d a k oja poziva n e bi tre b alo da p re tp o sta v i kako je ob jek at p ra v iln o n ap rav ljen i ispravan. U o v o m p rim e ru , u k o m e se n e ko risti p re th o d n o p o m e n u ta te h n ik a in d ik ato ra , o d re d b a finally sasvim sig u rn o ttije m esto za zatv aranje d ato tek e m e to d o m close( ), p o to bi se u to m sluaju zatvarala k ad g o d se izvri k o n stru k to r. elim o d a d ato tek a b u d e otvo re n a to k o m k o risn o g iv o tn o g veka o b jek ta ldase UlaznaDatoteka. M eto d a citajRed( ) v raa objek at klase String koji sadri sledei red datoteke. O n a p o ziva m e to d u readLine( ) koja m oe d a generie izuzetak, ali se taj izuzetak hvata, pa citajR e d ( ) n e generie nikakav izuzetak. P ri p ro jek to v an ju izuzetaka, p ro b lem je to to se ne zna da li izuzetak treba p o tp u n o o b rad iti n a ov o m niv o u, d a li ga treb a o b ra d iti sam o delim in o i isti (ili drugaiji) izuzetak p ro sled iti dalje, ili ga valja p ro sle d iti bez ikakve o brade. Prosleivanje, kada je odgovarajue, sig u rn o m o e da p o jed n o sta v i pisan je koda. U ovoj situaciji, m e to d a citajRed( ) pretvara izuzetak u RuntimeException d a bi ukazala n a p ro g ra m sk u greku. K orisnik m o ra da pozove m e to d u ciscenje( ) k ad a zavri s ko rien jem o bjekta klase UlaznaDatoteka. N a taj n ain se m o g u o slo b o d iti sistem ski resu rsi (npr. id en tifik ato ri d a to te k a ) koje koriste o b jek ti tip a BufferedReader i/ili FileReader. To n e bi treb a lo da radite d o k ne zavrite s k o rien jem objekta H ase UlaznaDatoteka. M o d a n am erav ate da stav ite tu fu nkciju u m e to d u finalize( ), ali kao to je p o m e n u to u poglavlju Inicijalizacija i ienje, ne m oete uvek b iti sig u rn i da e m eto d a finalize( ) biti p o zv an a (ak i ako ste sig u rn i da e biti pozvana, n e zn ate kada e se to d esiti). To je je d a n o d n e d o sta ta k a Jave: n ije d n o ienje, osim b risan ja m em o rije, ne izvrava se a u to m a tsk i. Z ato p ro g ra m e ra klijenta m o rate obavestiti d a je o n o d g o v o ran za ienje. N ajbezbedniji nain korienja klase koja m o e g en erisati izuzetak to k o m k o n stru k c ije i koja zahteva ienje jeste u p o tre b a u g n e en ih b lokova try:
//: iz u z e c i/ C isc e n je .ja va // Zajemeno p ra viln o i e n je resursa. p ub lic c la s s C iscenje ( pu b lic s t a t ic void m a in (S trin g [] args) { try { UlaznaDatoteka in = new U la z n a D a to te k a ("C is c e n je .ja v a "); try { S trin g s; in t i = 1; w h ile ((s = in .g e tl_ in e ()) != n u ll) ; // Ovde obradi red po r e d ... } catch(Exception e) { S y ste m .o u t.p rin tln ("U h va tio izuzetak u metodi m ain "); e .p rin tS ta c k T ra c e (S y ste m .o u t);

380

Misliti na Javi

} finally { in.ciscenjeO;

}
} catch(Exception e) {

S y s te m .o u t.p rin tln ("N ije uspela k o n stru k cija U lazneD atoteke");

} }
} /* Ispis: ciscenje() uspeno

* ///:Paljivo ra z m o trite u p o tre b lje n u logiku: k o n stru k cija o b jek ta tip a UlaznaDatoteka su tin sk i je u so p stv en o m b lo k u try. A ko ta k o n s tru k ja ne u sp e, ulazi se u sp oljni b lo k catch, a ciscenje( ) se n e poziva. M e u tim , uk o lik o k o n stru k c ija usp e, m o ra te se p o sta ra ti da o b jek at b u d e poien, p a n e p o sre d n o n ak o n k o n stru k c ije p rav ite n o v b lo k try. O d re d b a finally koja obavlja ienje p rid ru e n a je u n u tra n je m b lo k u try; zato se b lo k finally nee izvriti ako k o n stru k c ija n e u sp e, a izvrava se uvek k ad a k o n stru k c ija uspe. O vaj o pti n a in ienja treb a p rim e n jiv a ti i k ad a k o n s tru k to r u o p te ne generie izuzetke. O sn o v n o pravilo glasi: n e p o sre d n o n a k o n p rav ljen ja o b jek ta koji zahteva ienje, o tv o rite b lo k try-finally:
//: iz u ze c i/N a c in C isc e n ja .ja va // Iza svakog objekta k o ji treba p o is t it i mora s le d it i blok t r y - f i n a ll y c la s s T re b a Je P o c is titi { // K o n stru k cija ne moe da ne uspe p riv a te s t a t ic long brojac = 1; p riv a te f in a l long id = brojac++; p u b lic void c is c e n je () { S y s te m .o u t.p rin tln ("T re b a Je P o c is titi + id + 1 1 p o i e n a ");

} }
cla s s IzuzetakTokomKonstrukcije extends Exception {} c la s s T re b a Je P o c is tit i 2 extends T r e b a Je P o c is titi { // Ko n stru k cija moe da ne uspe: p u b lic T r e b a Je P o c is titi2 () throws IzuzetakTokomKonstrukcij e {}

}
p u b lic c la s s N acinCiscenja { p u b lic s t a t i c void m a in (S trin g [] args) { // Deo 1: T r e b a Je P o c is titi ncl = new T r e b a Je P o c is titi ( ) ; try {

/ /
} fin a lly { n c l. c i s c e n j e ( ) ;

Poglavlje 12: Obrada greaka pomou izuzetaka

381

// Deo 2: // Ako k o n stru k cija ne moe da ne uspe, objekte moete da grup iete: T r e b a Je P o c is titi nc2 = new T r e b a Je P o c is t it i( ) ; T r e b a Je P o c is titi nc3 = new T r e b a Je P o c is t it i( ) ; try (

//
} fin a lly ( n c 3 .c is c e n je (); // Obrnut redosled u odnosu na konstru kciju n c 2 .c is c e n je ();

}
// Deo 3: // Ako k o n stru k cija moe da ne uspe, morate u vati svaku od n jih : try { T r e b a Je P o c is titi2 nc4 = new T r e b a J e P o c is t it i2 () ; try { T re b a Je P o c is titi2 nc5 = new T r e b a Je P o c is t it i2 (); tr y {

/ /
} fin a lly { n c 5 .c is c e n je ();

}
} catch(IzuzetakTokom Konstrukcije e) { // konstruktor nc5 S y s te m .o u t .p r in tln (e ); } fin a lly { nc4 .ci s c e n j e () ;

}
} catch(IzuzetakTokom Konstrukcije e) { // konstruktor nc4 S y s te m .o u t .p r in tln (e );

} }
} /* Is p is : T r e b a Je P o c is titi T r e b a Je P o c is titi T r e b a Je P o c is titi T r e b a Je P o c is titi T r e b a Je P o c is titi 1 3 2 5 4 poiena poiena poiena poiena poiena

* ///:U m e to d i m a in ( ), d eo 1 je lako razu m eti: iza o b jek ta koji treb a p o istiti sledi b lo k tryfinally. U k o lik o k o n stru k cija o b jek ta ne m o e da ne uspe, b lo k catch nije p o tre b a n . U d elu 2, v id ite da ob jek ti ija k o n stru k c ija ne m oe da ne u sp e m o g u b iti g ru p isan i i za k o n stru k c iju i za ienje. D eo 3 p o k a z u je kako se rad i sa o b jek tim a ija k o n stru k c ija m oe d a n e u sp e i koji zahtevaju ienje. P raviln a o b ra d a ove situacije znai pravljenje kra, je r svaku k o n stru k ciju m o ra te da o b u h v a tite so p stv en im b lo k o m try-catch, a n ak o n njega m o ra da sledi blok try-finally za zaje m en o ienje. R unoa k od a za o b ra d u izuzetaka u o v o m sluaju, p redstavlja ja k a rg u m e n t za p ravljenje k o n s tru k to ra koji ne m o g u da ne usp eju , m ad a to nije uvek m ogue.

382

Misliti na Javi

P ovedite ra u n a o sledeem : ako ciscenje( ) m o e d a g enerie izuzetak, m o d a e vam b iti p o tre b n i d o d a tn i blokovi try. D akle, m o ra te paljivo d a ra z m o trite sve m o g u n o sti i d a o bezbedite svaku o d njih.

Veba 21: (2) P okaite d a k o n s tru k to r izvedene klase n e m oe d a hvata izuzetke koje generie k o n stru k to r o sn o v n e klase.

Veba 22: (2) N ap rav ite klasu N euspesanK onstruktor iji k o n stru k to r m o e d a ne uspe d a zavri k o n stru k c iju i ta d a g en erie izuzetak. U m e to d i m a in ( ) napiite k o d koji praviln o titi o d takvog n eu sp eh a.

Veba 23: (4) U p re th o d n o j vebi, d o d a jte klasu s n ek o m m e to d o m ciscenje( ). Izm enite NeuspesanKonstruktor tak o d a k o n s tru k to r p rav i je d a n o d o b jek ata koji zahtevaju
ienje kao o b jek at lan, n a k o n ega k o n s tru k to r generie izuzetak, a o n d a p rav i d ru g i o b jek at lan koji zahteva ienje. N ap iite k o d koji p ra v iln o titi o d n e u sp eh a, a u m eto d i m a in ( ) dok aite d a ste se obezb ed ili o d svih m o g u ih n eu sp eh a.

Veba 24: (3) Klasi N euspesanK onstruktor d o d a jte n e k u m e to d u ciscenje( ). N apiite


k o d koji p ra v iln o u p o treb ljav a tu klasu.

Pronalaenje slinih izuzetaka


Kada se generie izuzetak, sistem za o b ra d u izuzetaka tra i ,,najblie blokove p o redosled u kojim su pisan i. K ada p ro n a e o d g o v araju i, sm a tra se d a je izuzetak o b ra en , a traenje prestaje. P ron alaen je slinih izuzetaka ne zah tev a d a izu zetak i b lo k za o b ra d u savreno odgov araju je d a n d ru g o m . O b jek tu izvedene klase o d g o v arae b lo k za o sn o v n u klasu, kao to je p rik azan o u o v om p rim e ru :
//: izuzeci/C ovek.java // Hvatanje h ije r a r h ija izuzetaka. c lass Dosada extends Exception {} cla s s K ija n je extends Dosada {} p u b lic c la s s Covek { p ublic s t a t ic void m a in (S trin g [] args) ( // Uhvati taan tip try { throw new K i j a n j e ( ) ; } c a tc h (K ija n je s) { S y s te m .o u t.p rin tln ("U h v a tio K i j a n j e " ) ; } catch(Dosada a) { System .out. pri n tln ("U h v a ti o Dosadu");

} }
} /* Is p is : Caught Sneeze Caught Annoyance

) III--

Poglavlje 12: Obrada greaka pomou izuzetaka

383

Izu zetak tip a Kijanje uhv atie p rv a o d re d b a catch kojoj odg ov ara, a to je, n arav n o , p rva. M e u tim , ako u k lo n ite p rv u o d re d b u catch i ostavite sam o o d re d b u catch za klasu Dosada, k o d e i dalje rad iti, je r b lo k hvata o sn o v n u klasu klase Kijanje. D ru g im reim a, catch (Dosada e) u h vatie izu ze tak tip a Dosada i sve klase koje su izvedene iz tog tipa. To je k o risn o : ako o d lu ite d a m eto d i d o d a te vie izv edenih izuzetaka, k o d p ro g ra m e ra klije n ta nee m o ra ti da se m en ja sve d o k klijen t o b ra u je izuzetke o sno v n e klase. A ko p o k u a te d a ,,m askirate izuzetke izvedene klase ta k o to n a prvo m esto stavite o d re d b u catch za h v atan je o sn o v n e klase, kao ovde:
try { throw new K i j a n j e ( ) ; } catch(Dosada a) {

/ /
} c a tc h (K ija n je s) {

/ / } p rev o d ilac e p rik a zati greku, p o to vid i d a o d re d b a za h vatanje Kijanje nije d o stu p n a .

Veba 25: (2) N ap rav ite h ije ra rh iju izu zetaka u tr i nivoa. P o to m n ap rav ite o sn o v n u klasu A s m e to d o m koja g enerie izuzetak u o sn o v i h ijerarhije. Izvedite klasu B iz A i redefiniite m e to d u tako d a g enerie izu zetak u d ru g o m n iv o u h ijerarh ije. P ono vite p o stu p a k i izvedite klasu C iz klase B. U m eto d i m a in () n ap rav ite o b jek at klase C i svedite ga navie n a tip A, a zatim p o zo v ite m e to d u .

Drugi pristupi
Sistem za o b ra d u izuzetaka predstavlja skrivena v rata koja p ro g ra m u o m oguavaju da nap u sti izvravanje n o rm a ln e sekvence n ared be. Skrivena v rata se up otrebljavaju kad a n astu pi izuzetan uslov, p o p u t o n o g a kada n o rm a ln o izvravanje vie nije m ogue ili poeljno. Izuzeci predstavljaju uslove koje teku a m eto d a ne m o e d a o b rad i. Sistem i za o b ra d u izuzetaka su razvijeni zato to je p ristu p koji je p o d ra z u m e v a o o b ra d u svih m og uih greka izazvanih svakim p ozivom svake funkcije bio previe teg o ban , pa ga p ro g ram eri n isu sleili. Stoga su greke ignorisali. Vredi p rim etiti da je olakavanje p ro g ram ira n ja o b ra d e greaka bilo glavna m otivacija za izum izuzetaka. Jedna o d v anih sm e rn ic a u o b ra d i izuzetaka glasi: ,,Ne hvataj izuzetak s kojim n e zna ta da u ra d i. U stvari, je d a n o d van ih cilje v a o b ra d e izuzetaka bio je d a s e k o d za o b ra d u greaka o d m a k n e o d take gde je greka n astala. T im e d o b ijate m o g u n o st da se u je d n o m delu svog k oda u sred sred ite na o n o to h o ete da u rad ite , a d a se u d ru g o m , zasebn om d elu bavite p ro b le m im a . Stoga glavni deo vaeg k o d a nee b iti z atrp a n logikom za o b ra d u greaka, pa e b iti m n o g o razum ljiviji i lake e se odravati. O b ra d a izuzetaka o b in o sm a n ju je o b im koda za o b ra d u greaka, je r je d a n b lo k za o b ra d u m oe d a o b ra d i greke nastale na m n o g o m esta.

384

Misliti na Javi

Proveren i izuzeci donekle k o m p lik u ju ovaj scenario, p o to vas p rim o ra v a ju da stavljate o d re d b e c a tc h i n a m esta na k o jim a n iste sp re m n i da o b ra d ite o d re e n u greku. To p ro u z ro k u je p ro b le m izaziva oteenja ako se p ro g u ta :
try {

II . . . radi se neto korisno } catch(0bavezanlzuzetak e) { } // Progutano!

P ro g ra m e ri (m e u k o jim a i ja, u p rv o m izd an ju ove knjige) o b i n o u ra d e o n o n ajjedn o stavn ije i sam o ,,prog u taju izuzetak - esto n e n a m e rn o . Kada to u ra d e, p rev o ilac je zadovoljan, p a uk oliko se ne sete da p o n o v o p ro u k ro z k o d i isprave ga n a to m m estu , taj izu zetak e b iti izgubljen. Izu zetak se pojavljuje, ali i nestaje, je r o d m a h biva p ro g u ta n . P oto vas p rev o dilac prisiljava d a o d m a h p iete k o d za o b ra d u izuzetka, izgleda k ao d a je ovo najlake reenje, iako je v ero v atn o n ajg o re o d svega to m o ete d a u rad ite. K ada sam shv atio d a sam i ja to u ra d io , zgrozio sam se p a sam u d ru g o m izd an ju ,,reio p ro b le m tak o to sam ispisao slojeve steka u b lo k u za o b ra d u (to se jo v id ikako i treb a - u vie p rim e ra u o v o m p oglavlju). M ad a je to k o risn o za p ra en je p o n a a n ja izuzetaka, i dalje se vid i da n a to m m estu zap rav o n e zn ate ta d a ra d ite s tim izu zetk o m . U o vom o d eljk u u vas u p o z n a ti s n ek im e lem en tim a i k o m p lik a cijam a koje n a sta ju zbog p ro v eren ih izuzetaka, i s m o g u n o stim a da ih reite. T em a izgleda jed n o stav n a, ali ne sam o d a je k o m p lik o v an a, nego i sp o rn a . Im a Ijudi koji k ru to za stu p aju su p ro tstav ljen a stan o v ita i s m a tra ju d a pravi o d g o v o r (n jih o v ) n ap ro sto b o d e oi. M islim da je razlog za je d n o o d tih m iljenja jasn a p re d n o s t koja se vidi p rilik o m prelaska iz jezika u kojem se tip o v i ne prov erav aju , k ak av je b io C p re ANSI stan d ar a, u jezik u k ojem se tip o v i obavezno prov erav aju i statiki zad aju (tj. p roveravaju ve to k o m p re v o e n ja ), kao to su C + + i Java. K ada obavite taj prelazak (kao ja), p re d n o sti su toliko d ra m a ti n e pa izgleda da je statika p ro v e ra tip o v a uvek najb o lji o d g o v o r na veinu p ro b lem a. Pokuau da vam p ren esem m ali d eo evolucije m o g stan o v ita. P o su m n jao sam u apsolutnu v re n o st statike p rovere tipova; ja sn o je da je o n a najee v eo m a korisna, ali p o sto ji nejasn a g ran ica n a k o n koje o n a p o staje sm etn ja. ( Jedan o d m o jih om iljen ih citata je: Svi m od eli su netani. Neki su korisni. ).

Istorija
O b ra d a izuzetaka je n astala u sistem im a PL/1 i M esa. K asnije se pojavljivala u jezicim a CLU, Sm alltalk, M o d u la-3 , Ada, Eiffel, C + + , P y th o n , Java, kao i u jezicim a R uby i C # nastalim n a k o n Jave. U to m p o g led u Java je slina C ++-U , sem na m e stim a gde su pro jek ta n ti Jave sm atra li da p ristu p iz C + + -a p ro u z ro k u je p ro b lem e. D a bi se p ro g ra m e rim a p ru io sistem za o b ra d u i o p o ra v a k o d greaka koji e im ati vie izgleda da b u d e u p o treb ljav an , jeziku C + + je o b ra d a izuzetaka d o d a ta relativ n o kasno u p ro cesu stand arizacije. N ju je zagovarao B jarne S tro u stru p , a u to r jezika. M odel izuzetaka u jezik u C + + p rv en stv en o p o tie iz C LU -a. M e u tim , tad a su posto jali i d ru g i jezici koji su p o drav ali o b ra d u izuzetaka: A da, Sm alltalk (im ali su izuzetke, ali ne i specifikacije izuzetaka) i M o d u la-3 (koja je im ala i izuzetke i specifikacije).

Poglavlje 12: Obrada greaka pomou izuzetaka

385

U svom p lo d o n o sn o m lan k u 7 o toj tem i, Liskova i S nyder p rim e u ju d a je glavni n ed o sta ta k jezika p o p u t C -a koji greke p rijav lju ju u p ro la zu sledei: . iza svakogpoziva tnora slediti uslovno ispitivanje da bi se utvrdio ish o d p o ziva . O vaj za h tev vodi ka program im a koji se teko itaju, a verovatno su i neefikasni, p a obeshrabruju program ere da se bave prijavljivanjem i obradom izuzetaka. D akle, jed an o d p rv o b itn ih m otiva za o b ra d u izuzetaka b io je d a se sprei ovaj zahtev, ali uz p roverene izuzetke u Javi esto d o b ijam o u p rav o tu vrstu koda. U nastavku, a u to ri kau: ... za h te v da tekst bloka za obradu b u d ep riklju en pozivu k o jije izazvao izu zeta k vodi ka neitljivim program im a u kojim a se izrazi p rekid a ju blokovim a za obradu. Sledei p ristu p C LU -a p rilik o m p ro jek to v an ja C + + izuzetaka, S tro u stru p kae d a je cilj b io sm an je n je koliine k o d a p o tre b n o g za o p o ra v a k o d greaka. M islim d a je o p azio kako p ro g ra m e ri n a C -u o b in o n e p iu k o d za o b ra d u greaka, p o to su k o liin a i poloaj to g k o da bili zastrauju i i z b u n ju ju i. Z ato su se navikli d a to ra d e k ao u C -u , tj. d a ig n o riu greke u k o d u i da za o tk riv an je i o tk lan ja n je p ro b le m a u p o tre b ljav a ju dibagere. D a b i ti C p ro g ra m e ri upotreb ljav ali izuzetke, tre b a lo ih je u b e d iti d a p iu d o d a tn i" k o d koji inae n isu pisali. D akle, da b i se p ro g ra m e ri zainteresovali za b o lju o b ra d u greaka, koliin a k o d a koju je treb alo d a ,,dodaju nije sm ela d a b u d e prevelika. N e zab o rav ite n a taj cilj kada b u d e m o ra zm atrali posledice p ro v e ren ih izuzetaka u Javi. C + + je o d CLU -a preu zeo jo je d n u ideju: specifikacije izuzetaka p o m o i ko jih se u p o tp isu m e to d e p ro g ram sk i navode izuzeci koji m o g u n a sta ti z b o g poziva te m eto d e. Specifikacija izuzetaka zapravo im a dve svrhe. O n a m oe rei: O vaj izu ze tak je n asta o u m o m k o d u ; ti ga obradi". Ali m oe rei i: ,,Ja u ig n o risati ovaj izuzetak koji m oe n astati kao posledica m o g koda, a ti ga o b ra d i. D o k sm o raz m a tra li m e h a n iz m e i sin ta k su izuzetaka, bili sm o u sred sre en i na d eo ,,ti ga o b ra d i, ali ovde m e p o se b n o z a n im a in jen ica da izuzetke esto ignoriem o, a specifikacija izu zetk a to m o e da u tv rd i. U C + + -u , specifikacija izuzetka nije deo p o d a ta k a o tip u funkcije. T okom p rev o en ja proverava se sa m o to da li su specifikacije izu zetak a dosledne; na p rim e r, ak o fu n k cija ili m e to d a g en eriu izuzetke, o n d a i njihove p rek lo p ljen e ili izvedene verzije m o ra ju g en erisati iste izuzetke. Za razliku o d Jave, to k o m p re v o en ja se n e p rov erav a da li fu n k cija/m eto d a zaista generie taj izuzetak, niti da li je specifikacija izuzetaka p o tp u n a (tj. da li ta n o o p isu je sve izuzetke koji m ogu b iti g en erisan i). Ta pro v era se obavlja, ali sa m o u v rem e izvravanja. U koliko se generie izuzetak koji kri specifikaciju izuzetaka, C + + p ro g ra m e pozvati fu n kciju u n e x p e c te d ( ) (neoekivan) iz svoje s ta n d a rd n e biblio tek e. Z anim ljiv o je p rim e titi da se specifikacije izu zetak a u o p te n e u p o treb ljav aju u stan d a rd n o j b ib lio teci C + + -a , zato to se koriste ab lo n i. U Javi p o sto je o g ra n i e n ja n ain a u p o tre b e g enerik ih tipova u specifikacijam a izuzetaka.

B a rb a ra L iskov i A lan Snyder, Exception H andling in CLU, IEEE T ra n s a c tio n s o n S o fh v are E n g in e e rin g , Vol. SE -5, No. 6, N o v e m b e r 1979. O v o g la n k a n e m a n a I n te r n e tu . P o to je d o s tu p a n s a m o u o d ta m p a n o m o b lik u , p o tra ite ga u nek oj b ib lio te c i.

386

Misliti na Javi

Perspektive
Prvo, vredi primetiti da su tvorci Jave zapravo izumeli proverene izuzetke (oigledno inspirisani specifikacijama izuzetaka u C++-u i injenicom da se C++ programeri obino ne baku s njima). Meutim, taj eksperiment nije ponovljen ni u jednom kasnijem jeziku. Drugo, provereni izuzeci izgledaju kao oito korisni u uvodnim primerima i malim programima. Reeno je da se suptilne razlike pojavljuju kada programi postanu veliki. Naravno, do tolike veliine obino ne dolazi preko noi; ona nastaje neosetno. Jezici koji nisu podesni za velike projekte, upotrebljavaju se za male. Ti mali rastu i od nekog trenutka shvatamo da su od ,,upravljivih postali teko upravljivi". Smatram da to moe biti posledica i preteranog proveravanja tipova; konkretno, provere izuzetaka. Izgleda da je veliina programa znaajna. To je problem, poto se u veini rasprava za ilustraciju upotrebljavaju mali programi. Jedan od projektanata C#-a kazao je sledee:
Ispitivanje malih programa navodi na zakljuak da zahtevanje specifikacija izuzetaka moe poveati produktivnostprogramera i kvalitet koda, ali iskustvo s velikim softverskim projektima namee drugaiji zakljuak - smanjuje se produktivnost a kvalitet koda se poveava malo ili nimalo.8

Autori CLU-a su kazali sledee o neuhvaenim izuzecima:


Stnatrali smo da tiije realno zahtevati od programera da pie blok za obradu u situacijam a kada se ne m oepreduzeti nita smisleno.9

Kada objanjava zato deklaracija funkcije bez specifikacije znai da ona moe generisati bilo koji izuzetak, a ne da uopte ne moe generisati izuzetak, Stroustrup kae:
,,To bi zahtevalo specifikacije izuzetaka za gotovo sve funkcije, predstavljalo bi znaajan uzrok ponovnog prevoenja i spreilo bi saradnju sa softverom napisanim na drugitn jezicima. To bi navclo programere da sabotiraju tnehanizme za obradu izuzetaka i da piu maskirni kdd koji sakriva izuzetke. Stoga bi Ijudi koji nisu uoili izuzetak stekli lano oseanje bezbcdnosti w

Vidimo da se ba takvo ponaanje - sabotiranje izuzetaka - deava s proverenim izuzecima u Javi. Martin Fovvler (autor knjiga UML Distilled , Refactoringi Analysis Patterns) napisao mi je sledee:
,,...sve u svemu, smatram da su izuzeci korisni, ali Javini provereni izuzeci vie kotaju nego to vrede."

Sada smatram da je vaan doprinos Jave bio to to je objedinila model prijavljivanja greaka, tako da se sve greke prijavljuju pomou izuzetaka. Toga nije bilo u C++-u, zato to je, zbog kompatibilnosti sa C-om, jo uvek bio na raspolaganju stari model prostog ignorisanja greaka. Ali ako imate dosledno prijavljivanje pomou izuzetaka, moete ih koristiti ako hoete, a ako ne, oni e se popeti na najvii nivo (konzolu ili drugi kontejnerski
* h ttp ://d is c u s s .d e v e lo p .c o m /a r c h iv e s /w a . ex e?A 2 = in d0 01 1 A & L = D O T N E T & P = R 3 2 8 2 0 9 Exception H andling in CLU, L iskov & Snyder. 10 B ja rn e S tr o u s tr u p , The C + + P rogram m ing Language, trec izdanje (A d d iso n -W e sle y , 1997), s. 376.

Poglavlje 12: Obrada greaka pomou izuzetaka

387

p ro g ra m ). Kada je Java m odifikovala C + + -o v m o d el tako da su izuzeci postali jed in i n a in prijavljivanja greaka, d o d a tn o pojaanje u v id u pro v erenih izuzetaka m o d a je p o stalo m anje p o treb n o . R anije sam vrsto verovao d a su i p ro v ereni izuzeci i statik a pro v e ra tip o v a n e o p h o d n i za razvoj ro b u sn ih p ro g ra m a. M e u tim , i an eg d o tsk o i n e p o sre d n o isk u stv o 1 1 s jezicim a koji su vie din am ik i n ego statiki, dovelo m e je d o stan o v ita d a velike p re d n o s ti zapravo p o ti u od: 1. O b jed in jen o g m o d ela prijavljivanja g reaka p rek o izuzetaka, bez o b z ira n a to d a li prevo d ilac p rim o ra v a p ro g ra m e ra d a ih o b rad i. 2 . Provere tipo v a, b ez o b zira n a to kada se o d igravala. D ru g im reim a, d o k le g o d se sp ro v o d i p ro p isn a u p o tre b a tipova, esto nije v ano d a li se o n a od ig rav a u v rem e p rev o en ja ili u v rem e izvravanja. P o v rh svega toga, sm an je n je o g ran ien ja k o jim a je p ro g ra m e r izv rg n u t to k o m p re v o enja d o n o si v eo m a zn aajn o poveanje p ro d u k tiv n o sti. tavie, za k o m p e n z a c iju p revie ograniavajue statike d o d ele tipova, p o tre b n i su refleksija i generiki tipovi, kao to ete v ideti u b ro jn im p rim e rim a u celoj knjizi. Ve su m i rekli d a je to to sam u pravo n ap isao svetogre i d a u tim e u n ititi svoju rep u tac iju i p ro u zro k o v a ti p ro p a st civilizacija i n e u sp e h veeg p ro c en ta p ro g ra m sk ih p ro jek ata. V eruje se d a prev o d ilac m o e spasiti p ro je k a t u koliko ukae n a greke to k o m p rev o en ja, ali je jo vanije shvatiti o g ran ien ja to ga to p rev o d ilac m o e d a u ra d i; u d o d a tk u koji ete nai na http://M indV iew .net/B ooks/B etterJava, naglaavam v re d n o st a u to m atsk o g p o stu p k a p rev o en ja i pak o v an ja p ro g ra m a , i te stira n ja sa @ U nit, k o ji vam m n o g o vie p o m a u nego kada sve p retv orite u sin tak sn u p o g rek u . Ne zaboravite: Dobar program ski je zik je onaj koji p om a e program erim a da piu dobre programe. N ijedan program ski je zik ne m oe spreiti da se na n je m u p i u loi program i.'2 U svakom sluaju, n ez n a tn a je verovatnoa d a e prov eren i izuzeci ikada b iti izbaeni iz Jave. To bi bila previe rad ik aln a p ro m e n a jezika, a u S u n u im a m n o g o zago v o rn ik a p ro v ere n ih izuzetaka. Sun je o d u v ek sp rovodio p o litik u ap so lu tn e v ertik aln e k o m p a tib ilnosti - da b iste stekli oseaj za razm ere toga, g otovo sav Sunov softver m oe d a se izvrava n a svom S un o vom h ard v eru , koliko god bio star. M e u tim , u koliko shvatite da vam neki proveren i izuzeci sm etaju ili n aro ito ako oseate d a vas p rim o ra v a ju d a hvatate izuzetke s ko jim a ne znate ta da rad ite, m oe se i drugaije.

Prosleivanje izuzetaka na konzolu


U jed n o stav n im p ro g ram im a, kakvi su m nogi u ovoj knjizi, najlaki nain da sauvate izuzetke, a da ne piete go m ilu koda, jeste prosleivanje izuzetaka iz m eto d e m a i n ( ) na ko n zolu. Na p rim er, ako hoete da otv o rite d ato tek u za itanje (o e m u ete sve p o jed in o sti
'' 12 In d ir e k tn o , isk u stv o s je z ik o m S m a llta lk k ro z ra z g o v o re s m n o g im is k u s n im p ro g r a m e rim a ; d ire k tn o isk u stv o s je z ik o m P y th o n ( www.Python.org). Kees K oster, p ro je k ta n t je z ik a C D L , ije rei n a v o d i B e r tra n d M eyer, p ro je k ta n t je z ik a Eiffel,

www. clj. c o m /d j/v l/ n 1/bm /righ t/.

388

Misliti na Javi

saznati u poglavlju Javin u la zno -izla zn i sistem ), m o ra te o tv o riti i zatv o riti FilelnputStream koji generie izuzetke. U je d n o s ta v n o m p ro g ra m u m o ete u ra d iti ovo (isti p ristu p je u p o treb ljen u m n o g im p rim e rim a u ovoj knjizi):
//: izuzeci/M ain lzu zetak.java import j a v a . io . * ; p u b lic c la s s M ainlzuzetak { // Proslediemo sve izuzetke na konzolu: p ub lic s t a t ic void m a in (S trin g [] args) throws Exception { // O tvaranje datoteke: Filelnp u tStream datoteka = new F ile In p u tS tre a m ("M a in Iz u z e ta k .ja v a "); // Upotreba datoteke . . . // Z atvo ri datoteku: d a to te k a .c lo s e ();

} } lll- ~ Im ajte u v id u d a je i m a in ( ) m e to d a k oja m o e im ati svoju specifikaciju izuzetaka, a ovde je tip izuzetka Exception, k o re n sk a klasa svih p ro v e re n ih izuzetaka. A ko ih p rosledite n a konzolu, p o te en i ste p isan ja b lo k o v a try-catch u n u ta r m e to d e m a in ( ). (N aalost, U /I d ato tek a je z n a tn o slo en ija n eg o to izgleda n a o sn o v u ovog p rim e ra , pa n e m o jte previe da se u z b u u je te d o k n e p ro ita te poglavlje Javin ulazn o -izla zn i sistem ).

Veba 26: (1) Izm en ite zn ak o v n i n iz im en a d ato tek e u p ro g ra m u Mainlzuzetak.java tako


da glasi na im e nep osto jee d ato tek e. P o k ren ite p ro g ra m i p o g led ajte ta e se desiti.

Pretvaranje proverenih izuzetaka u neproverene


G en erisan je izuzetka iz m e to d e m a in ( ) p o d e sn o je kada piete je d n o sta v n e p ro g ra m e za vlastitu u p o tre b u , ali u o p te m slu aju nije k o risn o . Pravi p ro b lem nastaje kada piete telo obin e m e to d e pa pozivate d ru g u m e to d u i sh v atite sledee: N em a m p o jm a ta da rad im sa ov im izuzetkom ovde, a neu n i da ga p ro g u ta m n iti da ispisujem n ek u b a n aln u p o ru ku. Uz u lan a n e izuzetke, n u d i se je d n o novo i je d n o sta v n o reenje. P rosto ,,om otajte proveren izuzetak u RuntimeException tak o to ga p ro sled ite k o n stru k to ru klase RuntimeException. To se radi ovako: tr y {
_ ra d ite korisne s tv a ri // _ } catch(NeZnamStaDaRadimSaOvimproverenim Izuzetkom e) { throw new Runtim eException(e);

} Izgleda d a je ovo idealno reenje k ad a ho ete da ,,iskljuite p ro v eren izuzetak - nije p ro g u tan , n e m o ra te da ga stavljate u specifikaciju izuzetaka svoje m e to d e, a zbog ulanavanja izuzetaka n e g ubite in fo rm ac ije iz o rig in a ln o g izuzetka.

Poglavlje 17: Obrada greaka pomou izuzetaka

389

O va teh n ik a o m og u av a ig n o risan je izuzetka; o n e se sam p o p e ti p o steku poziva m eto d a , a d a n e m o ra te da piete blokove try -c a tc h n iti specifikacije izuzetaka. M e u tim , i dalje m oete m e to d o m g e tC a u s c ( ) u h v atiti i o b ra d iti p o je d in e izuzetke, kao ovde:
// : iz u z e c i/ Is k lju c iv a n je P ro v e r e .ja v a // " I s k lju iv a n je " provere izuzetaka. import j a v a . io . * ; import s t a t ic rie t.m ind view .u til . P r i n t . * ; c la s s Omotajproverenlzuzetak { void throwRuntim eException(int t i p ) { try { swi t c h ( t i p) { case 0: throw new FileN o tFo u n d Ex cep tio n (); case 1: throw new I0 E x c e p tio n (); case 2: throw new RuntimeException("Gde sam?1 1 ); d e fa u lt: re tu rn ;

}
} catch(Ex ception e) { // Prilag o e n je za neproverene: throw new Runtim eException(e);

} }

cla s s NekiDrugiIzuzetak extends Exception {} pu b lic c la s s Is k lju c iv a n je P r o v e re { p u b lic s t a t i c void m a in (S trin g [] args) { Omotajproverenlzuzetak opi = new O m otajproverenlzuzetak(); // Moete pozvati throwRuntimeException() bez bloka t r y // i o s t a v it i RuntimeException da napusti metodu: o p i.throw Runtim eException(3); // A moete o d lu iti i da hvatate izuzetke: f o r ( i n t i = 0 ; i < 4 ; i++) tr y { i f ( i < 3) o p i.th ro w R u n tim eE x cep tio n (i); el se throw new N e k iD ru g iIz u z e ta k (); } catch(N ekiD rugiIzuzetak e) { p rin t("N e k iD ru g iIz u z e ta k : " + e ); } catch(Runtim eException re) { try { throw re .g e tC a u s e O ; } catch(FileN otFoundException e) { p r i n t ( " F i 1eNotFoundException: " + e ) ; } catch(IO Exception e) { p rin t("IO E x c e p tio n : " + e ) ;

390

Misliti na Javi

} catch(Throwable e) { p rin t("T h ro w ab le: 1 1 + e );

} } }
} /* Is p is : F i 1eNotFoundException: j a v a . i o . F i 1eNotFoundException IOException: ja v a .io .IO E x c e p tio n Throwable: j a v a . 1ang.Runtim eException: Gde sam? NekiDrugiIzuzetak: Neki DrugiIzuzetak

* ///:-

OmotajproverenIzuzetak.throwRuntimeException( ) sadri k o koji g enerie izuzetke razliitih tipova. Oni se h v ataju i o m o tav aju u Runtim eException objekte, p a p o sta ju ,,uzrok (engl. cause ) tih izuzetaka. U p ro g ra m u IskljucivanjeProvere v id ite da je m o g u e po zv ati throwRuntimeExceptio n ( ) bez b loka try, p o to ta m e to d a n e g en erie p ro v ere n e izuzetke. M e u tim , k ad a ste sp re m n i za hvatan je izuzetaka, i dalje m o ete d a u h v atite svaki izu zetak koji ho ete sam o nap iite k o d u n u ta r b lo k a try. P oinjete o d svih izuzetaka za koje izriito zn ate da se m o g u pojaviti iz k o da u v aem b lo k u try - u o v o m sluaju, p rv o se hv ata NekiDrugiIzuzetak. N a k raju , hvatate RuntimeException i g en eriete (rezervisan a re throw) rezu ltat m e to d e getC ause( ) (o m o ta n i izu zetak ). T im e se izdvajaju p rv o b itn i izuzeci koji se p o to m m o g u o b ra iv ati u so p stv en im b lo k o v im a catch. T ehniku o m o tav an ja p ro v eren o g izuzetka u RuntimeException u p o treb ljav a e m o u o statk u knjige k ad god b u d e u m esno . D ru g o reenje je da n ap ra v ite so p stv en u p otld asu o d RuntimeException. N a taj n ain ne m o ra te vi d a hvatate izuzetak, a m oe d a ga u hvati ko b u d e hteo. Veba 27: (1) P repravite vebu 3 tako da se izuzetak p re tv ara u RuntimeException. Veba 28: (1) P repravite vebu 4 tako da n a m en sk a klasa izuzetaka nasledi RuntimeException i pokaite da tad a prev o d ilac dozvoljava da izostavite b lo k try. Veba 29: (1) P repravite sve tipove izuzetaka u p ro g ra m u Storm yInning.java tako da pro ire (rezervisana re extend) RuntimeException, i pok aite d a tad a n isu p o tre b n e ni specifika je izuzetaka niti blokovi try. U k lo n ite k o m e n ta re //! i po kaite kako se m etod e
m og u prevesti b ez specifikacija.

Veba 30: (2) P repravite p ro g ra m Covek.java tako da izuzeci naslede RuntimeException. Izm en ite m a in ( ) tako da se teh n ik a iz p ro g ra m a IskljucivanjeProvere.java u po treb ljav a
za o b ra d u razliitih tipova izuzetaka.

Uputstva za izuzetke
K oristite izuzetke da: 1. O b ra d ite p ro b lem e na o d g o v araju em nivou. (Izbegavajte h v atan je izuzetaka s kojim a ne zn ate ta da rad ite.) 2. Reite p ro b lem i p o n o v o po zo v ete m e to d u koja je p ro u zro k o v ala izuzetak.

Poglav|je 12: Obrada greaka pomou izuzetaka

391

3 . Z ak rp ite greku i nastav ite ra d b ez p o n o v n o g isp rob avan ja m etode.


4 . Izrau n ate altern a tiv an re zu ltat u m e sto o n o g a koji je m eto d a tre b alo d a vrati. 5 . U rad ite sve to m o ete u tek u em o k ru e n ju i p o n o v o generiete isti izuzetak ka vi-

em o k ru en ju .
6

. U rad ite sve to m o ete u te k u e m o k ru e n ju i p o n o v o generiete drugaiji izuzetak k a viem o k ru en ju .

7 . Z avrite p ro g ram .

8 . P ojed n o stav ite k o d . (A ko n ain n a koji generiete izuzetke jo vie k o m p lik u je kod, o n d a ga nije lako i p o eljn o k o ristiti.)
9 . Poveate p o u z d a n o st b ib lio tek e i p ro g ra m a . (O vo je k ra tk o ro n o ulag an je kad je u

p ita n ju u k lanjanie greaka, a d u g o ro n o u laganje u sn ag u aplikacije.)

Saetak
Izuzeci su n ezao bilazan d eo p ro g ra m ira n ja n a Javi; ako n e zn ate d a rad ite s n jim a, neete ba m n o g o postii. Z ato sm o izuzetke p red stav ili n a ovom m estu - im a m n o g o bib lio tek a (k ao ran ije sp o m e n u ta b ib lio tek a U /I) koje u o p te n e m o ete d a u p o treb ljav ate b ez p o znav an ja izuzetaka. Jed n a o d p re d n o sti o b ra d e izuzetaka jeste sledea: om o g u av a d a se n a je d n o m m estu u sred sred ite n a p ro b lem koji p o k u av ate d a reite, a d a se n a d ru g o m m e stu bavite grek am a u to m k o du. I m a d a se izuzeci p o p rav ilu sm atraju alatk am a koje om o g u av a ju p rijavljiva nje i oporavak od greaka u v re m e izvravanja, p ita m se koliko esto se aspekat oporavka" realizuje, pa ak i koliko esto je to u o p te m ogue. Po m o m oseaju, to je m an je o d 10 p osto v rem en a, p a ak i tad a se v ero v atn o svodi na otpetljav an je steka d o p o z n a to g stab iln o g stan ja, a ne d o stv arn o g nastavljanja rada. Bila to istina ili ne, sm a tra m da o sn o v n a v re d n o st izuzetaka lei u njih o v o j fu n k ciji ,,prijavljivanja. in jen ica d a u Javi svegrek e valjap rijav ljiv ati u o b lik u izuzetaka d a je jo j veliku p re d n o st n ad jezicim a p o p u t C + + -a , koji dozvoljavaju prijavljivanje greaka n a vie razliitih naina, a m o ete i da ih u o p te ne prijavite. D osledan sistem za prijavljivanje greaka znai da se vie ne m o rate pitati: Da li su m i negde p ro cu rile greke? n a k o n pisanja svakog pareta k o d a (ukoliko ne p ro g u ta te izuzetke, narav n o !). Kao to ete videti u sledeim p oglavljim a, u koliko reite ovo p itan je - ak i ako to u rad ite g en eriui R u n tim e E x c e p tio n svoj tr u d p ri p ro jek to v an ju i realizaciji m oete u sred sred iti na zanim ljivije i tee p ro b lem e.
R eenja o d a b r a n ih vebi d a ta su u e le k tro n sk o m d o k u m e n tu The Thinking in Java Annotated Solu-

tion Guide , koji se m o e k u p iti n a lokaciji www.BruceEckel.com.

Znakovni nizovi
T vrd i se d a o b ra d a z n a k o v n ih n iz o v a sp ad a m eu n a i e e a k t i v n o s t i u

p ro g ra m ira n ju . To je p o g o to v o ta n o u W eb sistem im a, gde se Java m n o g o k oristi. U o v o m po g lavlju detaljn ije em o ra z m o triti najee k o rien u klasu to g jezika, String, k ao i neke n je n e p ratee i p o m o n e m eto d e.

Nepromenljivi znakovni nizovi


O b jek ti klase String su n ep ro m en ljiv i. U JDK d o k u m e n ta c iji klase String, vid eete da svaka n je n a m e to d a k oja naizgled in en ja znak o v n i niz, zapravo p rav i i vraa p o tp u n o n o v o b jek a t tip a String koji sadri m o d ifik aciju . O rig in a ln i String se ostavlja n eizm en jen . Pogledajte sledei kod:
//: znakovninizovi/Neprom enlji v .ja v a import s t a t i c n e t.m in d view .u til . P r i n t . * ; p u b lic c la s s Nepromenljiv { p u b lic s t a t i c S trin g v e ls lo v a (S t r in g s) { return s . tollpperCase ( ) ;

}
p u b lic s t a t i c void m a in (S trin g [] args) { S trin g q = "zd ravo"; p r i n t ( q ) ; // zdravo S trin g qq = u p case(q ); p ri n t (q q ); // ZDRAVO p r i n t ( q ) ; // zdravo

}
} /* Is p is : zdravo ZDRAVO zdravo

* ///:K ada se q p ro sled i m eto d i v e ls lo v a (), zapravo se p ro sle u je kopija reference o d q. O bjek at s k o jim je ta referenca povezana ostaje na istom fizikom m estu . Reference se kopiraju p rilik o m prosle ivan ja. P o g led am o li definiciju m e to d e v e ls lo v a (), v ideem o da je p ro sle en a referenca nazvana s i da p o sto ji sam o d o k se izvrava telo m eto d e v e ls lo v a (). Kada se izvravanje m eto d e v e ls lo v a () zavri, lo k aln a referenca s iezava. M etoda v e ls lo v a () vraa rezu ltat, a to je o rig in a ln i zn ak o v n i niz u kojem su sva slova p retv o ren a u velika. N aravno, o n a zapravo vraa referen cu to g rezultata. Ali ispostavlja se da je referenca koju vraa povezana s n ovim o b je k to m , a d a je o rig in aln i q ostavljen na m iru . Takvo p o n a a n je o b i n o ho em o . P retp o stav im o da kaete:
S trin g s = "a s d f"; S trin g x = N e p ro m e n ljiv .v e ls lo v a (s );

Poglavjje 13: Znakovni nizovi

393

D a li zaista elite da m e to d a v e ls lo v a () izm en i svoj arg u m en t? O n o m e k o ita k o d , arg u m e n t o b i n o izgleda kao p o d a ta k d at m e to d i n a znanje, a n e n eto to e b iti izm en jeno. O vo je van a garancija, je r se zbog nje k o d lake ita i razum e.

Poreenje preklapanja operatora + i StringBuildera


P oto su o b jek ti tip a S trin g n ep ro m en ljiv i, m o ete n a p ra v iti pro izvo ljan b ro j p se u d o n im a (alijasa) o d re e n o g znak o v n o g niza. S o b z iro m n a to da je svaki znako vn i n iz sam o za itan je, n e p o sto ji m o g u n o st da e je d n a referenca izm eniti n eto to e u tic ati n a d ru g e reference. N ep ro m e n ljiv o st m o e d a u tie n a efikasnost. P rim e r za to je o p e ra to r + , koji je p re k lop ljen za objekte tip a S trin g . P rek lap an je znai d a je o p eraciji d a to d ru g o zn aen je kad a se p rim e n i n a o d re e n u klasu. ( + i + = za zn ak ov ne nizove je d in i su p re k lo p lje n i o p e ra to ri u Javi, koja ne dozvoljava p rekJapanje d ru g ih .)1 O p e ra to r + o m ogu av a nadovezivanje zn a k o v n ih nizova:
// : znakovninizovi/N adovezivanje.java p u b lic c la s s Nadovezivanje { p u b lic s t a t ic void m a in (S trin g [] args) { S trin g mango = "mango"; S trin g s = "abc" + mango + "d e f" + 47; S y s te m .o u t .p r in tln (s );

)
} /* Is p is : abcmangodef47

* ///:P ok uajte da zam islite kako bi to moglo da radi. S trin g o bjek at ,,abc bi m o g ao da im a m e to d u a p p e n d ( ) koja pravi n o v o b jek at tip a S trin g koji sadri ,,abc n ad o v ezan na sadraj o b je k ta m a n g o . N ovi o b jek at tip a S tr in g b i zatim n a p rav io d ru g i no vi S tr in g koji bi d o d a o ,,def itd. To bi svakako radilo, ali bi zahtevalo pravljenje m n o g o S trin g o bjekata sa m o d a bi se sastavio no v znakov n i niz, i ostalo bi m n o g o p o m o n ih zn ak o v n ih nizova koje treb a sa k u p iti k ao sm ee. P retp o stav ljam d a su p ro je k ta n ti Jave n ajp re isprob ali taj p ris tu p (to je lekcija iz p ro jek to v an ja softvera - o sistem u zapravo ne znate n ita d o k ga n e isp ro b ate u k o d u i n e n a te ra te d a rad i). P retp o stavljam i d a su videli kako su p e rfo rm a n se takvog p ristu p a neprihvatljive. Da biste videli ta se zaista deava, gornji ko d m oete prevesti unazad alatkom ja v a p koja se isp o ru u je kao deo razvojnog o kruenja JDK. O vako treba da izgleda k o m a n d n a linija:
javap -c Nadovezivanje
' C + + d o z v o lja v a p ro g r a m e rim a d a p re k la p a ju o p e ra to re k a k o g o d h o e. P o to je to e s to k o m p lik o v a n p o s tu p a k (v id e ti po g lav lje 10 k n jig e Misliti najeziku C + + , M ik ro k n jig a , 2 0 0 3 ), p ro je k ta n ti Jave su ga p ro g la s ili ,,lo im i z a b ra n ili u Javi. T oliko lo e b a i n ije, p o to su to na k ra ju i s a m i u ra d ili, a d a iro n iia b u d e vea, p re k la p a n je o p e ra to ra bi u Javi b ilo m n o g o lake n e g o u C + + - u . To se m o e v id e ti u P v th o n u (v id e ti uH'u'.lvthon.org) i C # -u , koji im a ju s k u p lja n je i u k la n ja n je s m e a te je d n o s ta v n o p re k la p a n je o p e ra to ra .

394

Misliti na Javi

In d ik a to r -c daje b a jtk o d o v e JV M -a. K ada o d b acim o delove koji nas n e z an im aju i m alo u re d im o ostatak , evo k ako glase relev an tn i b ajtkodovi:
public static void m a in (j av a. la ng .S tr ing [] ); Code: Stack =2, Locals=3, Args size=l 0: 2: 3: 6: 7: 10: 12: 15: 16: 19: 21: 24: 26: 29: 32: 33: 36: 37: 40: ldc #2; //String mango astore 1 new #3; //class StringBuilder dup invokespecial invokevirtual aload_l invokevirtual #6; //StringBuilder.append:(String) ldc #7; //String def invokevirtual #6; //StringBuilder.append:(String) bipush 47 invokevirtual #8; //StringBuilder.append:(I) invokevirtual astore_2 getstatic #10; //Field System.out:PrintStream; aload_2 invokevirtual #11; // PrintStream.println:(String) return #9; / / S t r i ng Bu il de r. to Str in g:() #4; / / St ri ng Bu il de r. "< ini t> ":() #6; //StringBuilder.append:(String) ldc #5; //String abc

U koliko ste rad ili sa asem b le ro m , ovo b i treb alo da vam izgleda p o zn ato n a re d b e p o p u t dup i invokevirtual p rip a d a ju a sem b leru Javine v irtu e ln e m aine (JV M ). Ako prv i p u t u iv otu v id ite asem bler, ne u p u ta jte se - vano je u oiti d a p revodilac uvodi klasu java.lang.StringBuilder. U izv o rn o m k o d u se StringBuilder nije sp o m in jao , ali je prevodilac o d lu io d a ga ipak u p o tre b i, zato to je m n o g o efikasniji. U ovom sluaju, p revodilac pravi o bjek at tip a StringBuiler da bi n apravio znakovni niz s i etiri p u ta poziva a p p e n d ( ), p o je d n o m za svaki o d delova. N ajzad, poziva to S tring ( ) da bi nap rav io rezultat, koji sp rem a (asem blerskom n ared b o m astore_2) kao s. Pre nego to zakljuite d a je d o v o ljno svu da u p o treb ljavati objekte tipa String i p rep u stiti p rev o d io cu da se sta ra za efikasnost, p o g led ajm o ta rad i prevodilac. Evo p rim e ra koji p rav i String na dva n ain a : k oristei objekte tip a String, o d n o sn o ru n im p ro g ram iran je m p o m o u klase StringBuilder:
//: zn ak ov ni ni zo vi/KudaStringBuilder.java public class Ku da St ri ng Bu il de r { public String implicitno(String[] String rezultat = for(int i = 0; i < pol j a . 1ength; rezultat += pol j a [ i ] ; return rezultat; i++) polja) {

Poglavlje 13: Znakovni nizovi

395

p u b lic S trin g ekspl ic it n o (S t r in g [ ] p o lja ) { S trin g B u ild e r re z u lta t = new S t r in g B u ild e r ( ) ; f o r ( i n t i = 0; i < p o lja .le n g th ; i++) re z u lta t.a p p e n d (p o lja [i]) ; retu rn r e z u lt a t .t o S t r in g O ;

} } ///:A ko sad a p o k re n e te javap -c W itherStringBuilder, videete (p o jed n o stav ljen ) k o d te dve razliite m e to d e . Prvo, im p licitn o():
pub lic ja v a .la n g .S tr in g im p lic it n o (j a v a .la n g .S t r in g [ ]) ; Code 0 ld c #2; // S trin g 2 astore_2 3 ic o n s t O 4 is to re _3 5 i 1oad_3 6 a lo a d l arraylength 7 8 i f _ i cmpge 38 1 new #3; // c la s s S trin g B u ild e r 14 dup 15 invokespecial #4; // S t r in g B u ild e r ." < in it > " :() 18 aload_2 19 in v o k e virtu a l #5; // S trin g B u ild e r.a p p e n d :() 22 alo ad_l 23 iload_3 24 a a load 25 in v o k e virtu a l #5; // S trin g B u ild e r.a p p e n d :() 28 in v o k e virtu a l #6; // S t r in g B u iId e r .t o S t r in g :() 31 astore_2 32 i inc 3, 1 35 goto 5 38 aload_2 39 areturn

O b ra tite p an ju na redove 8: i 35: koji zajedno ine petlju. Red 8: sadri n are d b u integer co m p are g reater th an o r equal to (celobrojnog p o red en ja vee ili jed n ak o ) o p eran a d a na steku i skae na 38: kada petlja zavri rad. Red 35: (n ared b a goto) je v raanje n a p o eta k petlje, red 5:. Vano je p rim etiti da se konstrukcija StringBuildera odigrava u n u ta rte petlje, to znai da ete d o b iti n o v objekat tip a StringBuilder svaki p u t kada p ro ete kroz petlju. O vo su b ajtk o d m e to d e eksplicitno():
p u b lic ja v a .la n g .S tr in g e k s p lic it n o (ja v a .la n g .S t r in g [ ] ) ; Code: 0: new #3; // c la s s S trin g B u ild e r 3: dup 4: invokespecial #4; // S trin g B u i1d e r . < in it> ": () 7: astore 2

396

Misliti na Javi

8: 9: 10: 11: 12: 13: 16: 17: 18: 19: 20: 23: 24: 27: 30: 31: 34:

iconst_0 isto re _3 iload_3 alo ad _l arraylength if_icm pge 30 aload_2 alo ad _l iload_3 aaload in v o k e virtu a l #5; // S trin g B u ild e r.a p p e n d :() pop iin c 3, 1 goto 10 aload_2 in v o k e virtu a l #6; // S t r in g B u ild e r . t o S t r in g :() areturn

N e sam o d a je p etlja k raa i jed n o stav n ija, n eg o m eto d a p rav i sa m o jed a n o b jek at tip a

StringBuilder. E ksplicitno p rav ljen je StringBuildera o m o g u av a d a u n a p re d zadate njegovu veliinu u koliko zn ate koliki treb a d a b u d e, tak o da ne m o ra uvek iznova da pravi sebi bafer. D akle, kada pravite m e to d u to S trin g ( ), ako su o p eracije jed n o stav n e pa prevodilac m o e sam d a ih shvati, p o p ra v ilu p re p u stite n jem u d a rezu ltat izrau n a na razb o rit nain . Ali ako p ro g ra m o b u h v a ta k ru e n je k ro z p etlju , u m e to d i to S trin g ( ) treb a eksplicitn o da n avedete StringBuilder, ovako:
//: zn ak o v n in iz o vi/U z S trin g B u ild e r.ja va import j a v a . u t i l .* ; p u b lic c la s s U zS trin g B u ild e r ( p ub lic s t a t i c Random slu ca ja n = new Random(47); p ublic S trin g to S tr in g () { S t r in g B u i1der re z u lta t = new S t r in g B u i1d e r ( " [ " ) ; f o r ( i n t i = 0; i < 25; i++) { re z u lta t.a p p e n d (s lu c a ja n .n e x tln t(100)) ; r e z u lt a t . append( " , " ) ;

}
r e z u lt a t . d e le te (r e z u lt a t.le n g t h ()- 2 , r e z u lt a t .1ength( ) ) ; re z u lta t.a p p e n d C ']" ) ; return r e z u lt a t .t o S t r in g O ;

}
p ublic s t a t ic void m a in (S trin g [] args) { U zS trin g B u ild e r usb = new U z S trin g B u i1d e r ( ) ; S y s te m .o u t.p rin tln (u s b );

}
} /* Is p is : [58, 55, 93, 61, 61, 29, 68, 0, 22, 7, 88, 28, 51, 89, 9, 78, 98, 61, 20, 58, 16, 40, 11, 22, 4]

* ///:-

Poglavlje 13: Znakovni nizovi

397

O b ra tite p a n ju n a to d a se svaki d eo rezu ltata d o d aje p o je d n o m n a re d b o m

a p p e n d ( ). A ko p o k u ate p re ico m i n ap iete n eto kao append(a + " + c), um eae se p rev o d ilac i p o n o v o p o e ti d a p rav i nove o b jek te tip a StringBuilder. U k oliko n iste sig u rn i koji p ristu p da o d ab erete, uvek m o ete da p o k re n e te javap i tak o
saznate. Iako StringBuilder im a sve p o tre b n e m eto d e , m e u k o jim a su in s e rt( ), replace( ), sub strin g (), p a ak i reverse( ), najee ete u p o treb ljav ati ap p en d ( ) i toString(). O b ra tite p a n ju n a u p o tre b u m e to d e d elete( ) za u k lan jan je p o sled n jeg zareza i razm ak a p re d o d av a n ja zavrn e uglaste zagrade. Klasa StringBuUder je uv ed en a u Javi SE5. Pre nje, Java je im ala k lasu StringBuffer, koja se sta rala za b e z b e d n o p a raleln o izvravanje (v id eti poglavlje Paralelno izvravanje) i stoga je bila z n a tn o sku p lja. Z a to bi o p eracije sa zn a k o v n im n izov im a u Javi SE5/6 treb alo d a b u d u z n a tn o bre.

Veba 1: (2) A nalizirajte m e to d u P rskalica.toString( ) u p ro g ra m u ponovnaupotreba/ Prskalica.java i ispitajte d a li b i se m e to d o m to S trin g ( ) sa ek sp licitnim p rav ljen jem klase StringBuilder u ted elo na p rav ljen ju o b jek ata tip a StringBuilder.

Nenamerna rekurzija
Poto su Javini sta n d a rd n i k o n tejn eri, kao i sve d ru g e klase, nasleeni iz klase Object, oni sadre m e to d u to S trin g ( ). O n a je redefinisana d a b i k o n tejn eri, kao i o bjekti koje sadre, m o g li d a se predstav e u oblik u zn akovnog niza, tj. tip a String. U n u ta r klase ArrayList, n a p rim er, m e to d a to S trin g ( ) prolazi kroz elem en te k o n tejn era i poziva m e to d u toS tring( ) svakog elem en ta.
//: z n a k o v n i_ n iz o v i/ Is p is iv a n je A rra y L is te .ja v a import g e n e ric s .c o ffe e .* ; import j a v a . u t i l .* ; p u b lic c la s s Is p is iv a n je A rr a y L is te ( p u b lic s t a t i c void m a in (S trin g [] args) { ArrayList< Coffee> kafe = new A rra y List< C o ffe e > (); fo r(C o ffe e c : new C o ffe eG en erato r(lO )) k a fe .a d d (c ); System .out. p r in t l n ( k a f e ) ;

}
} /* Is p is : [Americano 0, L a tte 1, Americano 2, Mocha 3, Mocha 4, Breve 5, Americano 6, L a tte 7, Cappuccino 8, Cappuccino 9]

* ///:Z am islim o d a elite d a vaa m eto d a to S trin g ( ) ispie ad resu ove klase. in i se da im a sm isla p o zv ati this:
//: znakovni_nizovi/BeskonacnaRekurzija.ja va // S lu a jn a re k u rz ija . // {RunByHand}

398

Misliti na Javi

import j a v a . u t i l .* ; pu b lic c la s s BeskonacnaRekurzija { p ub lic S trin g to S trin g O { return " Adresa objekta klase BeskonacnaRekurzija: 1 1 + th is + "\ n ";

}
public s t a t ic void m a in (S trin g [] args) { List<BeskonacnaRekurzija> v = new A rrayList< Beskon acn aRekurzija> (); f o r ( i n t i = 0; i < 10; i++) v.add(new B eskon acn aReku rzijaO ); S y s te m .o u t.p rin tln (v );

III-.-

A ko n ap ra v ite objek at klase BeskonacnaRekurzija i zatim ga ispiete, d o b iete besk raja n n iz izuzetaka. To e se d esiti i ako stavite o b jek te klase BeskonacnaRekurzija u n ek i k o n tejn er klase ArrayList i ispiete taj k o n te jn e r k ao to je o vde p rik a z an o . Z apravo se deava au to m a tsk a konverzija tip a u String. K ada kaete:
"Adresa objekta klase BeskonacnaRekurzija: " + th is

prevodilac vidi String za kojim sledi + i n eto to nije String, p a p o k u av a d a konvertuje this u String. Konverzija se obavlja pozivom m eto d e toS tring( ) koja daje rekurzivan poziv. A ko zaista elite da ispiete ad resu o b jek ta u o v o m sluaju, reenje je da se pozove m eto d a O bject.toString( ), koja to radi. D akle, u m esto this, treb a n a p isa ti super.toString( ). V eba 2: (1) Popravite program B eskonacnaR ekurzija.java.

Operacije sa znakovnim nizovima


O vo su neke o d osn o v n ih m eto d a za String objekte. P rek lo p ljen e m e to d e su o p isan e u isto m red u tabele:
Metoda Konstruktor Argumenti, preklapanje Preklopljene. podrazumevani, String. StringBuilder, StringBuffer char nizovi, byte nizovi. Upotreba Pravljenje String objekata.

lengthf) charAtf) getCharsf), getBytes() toCharArray() equalsf), equalslgnoreCase[) String za poredenje. int Indeks Poetak i kraj podniza koji treba kopirati, niz u koji treba kopirati, indeks mesta u odredinom nizu.

Broj znakova u objektu tipa String Znak (char) na zadatom mestu znakovnog niza. Kopiranje objekata tipa char ili byte u spoljni niz. Pravi char(] sa znakovima koje sadri String Ispitivanjejednakosti sadraja dva znakovna niza.

Poglavlje 13: Znakovni nizovi

399

Metoda compareTo|)

Argumenti, preklapanje String za poreenje.

Upotreba Rezultat je negativan, nula, ili pozitivan, u zavisnosti od leksikografskog uredenja Stringa i argumenta. Velika i mala slova nisu jednaka! Rezultat je true ako String sadri argument. Rezultat je true ako se argument tano podudara. Rezultat je true ako su sadraji jednaki; zanemaruje se veliina slova. Rezultat tipa boolean pokazuje da li se dato podruje podudara.

contains)) contentEquals() equalslgnoreCase() regionMatches()

Objekat tipa CharSequence koji se trai. Objekat tipa CharSequence ili StringBuffer za poreenje. String za poredenje Pomeraj od poetka ovog Stringa, drugi String i pomeraj od njegovog poetka, i duina koja se poredi. Preklapanjem se dodaje zanemarenje veliine slova. String s kojim eventualno poinje. Preklapanje dodaje pomeraj (ofset) u argument. String koji bi mogao biti sufiks od String Preklopljen: char char i poetni indeks, String. String i poetni indeks.

startsWith()

Rezultat tipa boolean pokazuje da li String poinje argumentom. Rezultat tipa boolean pokazuje da li je argument neki sufiks. Vraa -1 ako se argument ne pronae u ovom Stringu; u protivnom, vraa indeks poetka argumenta. lastlndexOf( ) pretrauje unazad, od kraja. Vraa nov objekat tipa String koji sadri dati skup znakova. Vraa nov objekat tipa String koji sadri znakove originalnog Stringa i zatim znakove argumenta. Vraa nov objekat tipa String sa sprovedenim zamenama. Vraa stari String ako ne nade ono to treba da zameni. Vraa nov objekat tipa String sa svim malim, odnosno velikim slovima. Vraa stari String ako nema ta da izmeni. Vraa nov objekat tipa String bez belina na oba kraja. Vraa stari String ako nema ta da izmeni.

endsVMthJ) indexOf(), lastlndexOf()

substring() (takode i subSequence()) concat( 1

Preklopljen: poetni indeks; poetni indeks + zavrni indeks. String koji treba nadovezati.

replace()

Stari znak koji se trai. novi znak s kojim ga treba zameniti. Moe zameniti ijedan CharSequence drugim

toLowerCase() toUpperCase()

trimf)

valueOf|)

Preklopljen: Object, char[], char[J i pomeraj od poetka i broj znakova, boolean, char, int. long, float, double

Vraa String koji sadri tekstualni prikaz argumenta. Pravi tanojednu String referencu za svakujedinstvenu sekvencu znakova.

internf )

40 0

Misliti na Javi

V id ite d a svaka String m e to d a vraa n o v String ob jekat k ad a tre b a d a iz m e n i sadraj zn a k o v n o g niza. Im ajte u v id u i sledee: u koliko sadraj ne treb a m e n jati, m e to d a p ro sto v ra a referen cu n a o rig in aln i String. T im e se tedi m e m o rija i d ru g i resursi. U n astavk u poglavlja ob jasn iem o m e to d e klase String koje rad e s regularnim izrazim a.

Formatiranje izlaza
Jed n a o d d u g o prieljkivanih m o g u n o sti koja se n ajzad pojavila u Javi SE5 jeste fo rm atira n je izlaza u stilu C -ove n a re d b e p r in tf( ). N e sam o d a to p o jed n o stav lju je izlazni k o d , neg o p ro g ra m e rim a u Javi daje m o n u alatk u za o d re iv an je fo rm a tira n ja i p o rav n av an ja izlaza.2

Metoda printff)
C -o v a fun k cija p rin tf( ) n e sastavlja nizove kao Java, n ego p rim a zn a k o v n i n iz za fo r m a tiranje i u njega um ee v red n o sti, fo rm a tira ju i u h o d u . U m esto d a za n ad o v eziv an je teksta i p ro m e n ljiv ih u n u ta r n av o d n ik a u p o treb lja v a p rek lo p ljen o p e ra to r + (koji u C -u nije p re k lo p ljen ), p rin tf ( ) u p o tre b lja v a p o se b n e o znake koje p o k a z u ju g d e tre b a u m e tn u ti p o d a tk e . Iza n jih sledi lista a rg u m e n a ta koji se u m eu u zn ak o v n i n iz za fo rm a tira n je , m e u s o b n o od v ojenih zarezim a. Na p rim er:
p rin tf("R e d 1: [%d % f]\ n ", x, y ) ;

P rilik o m izvravanja, v re d n o st o d x se u m ee u % d , a v re d n o st o d y u % f. Te oznake se nazivaju specifikatori fo rm a ta i, sem to k azuju gde treb a u m e tn u ti v re d n o st, o n e sa o p tav aju i v rstu prom en ljiv e k o ju treb a u m e tn u ti i kako je treb a fo rm a tira ti. Na p rim er, g o rn je % d kazuje da je x celi b ro j, a % f kazuje da je y neki bro j zapisan u fo rm a tu p o k re tn o g zareza (flo at ili d o u b le ).

System.out.format()
U Javu SE5 je uv ed en a m eto d a fo rm a t( ), d o stu p n a o b je k tim a tip a P rin tS tre a m ili P rin tW riter (o kojim a ete vie saznati u poglavlju Javin ulazno-izlazni sistem ), koja o b u h v ata sta n d a rd n i izlazni to k System.out. M eto d a fo rm a t( ) je n ap rav ljen a p o u z o ru na C -o v u fu n k ciju p rin tf( ). Za nostalgiare su n ap rav ili ak i m e to d u p rin tf ( ) koja sam o poziva m e to d u fo rm a t( ). Evo je d n o sta v n o g p rim era :
/ / : zn ak ovninizovi/JednostavnoForm atiranje.java p u b lic c la s s JednostavnoForm atiranje { p u b lic s t a t ic void m a in (S trin g [] args) { in t x = 5; double y = 5.332542;

U p is a n ju o v o g o d e ljk a , k a o i o d e ljk a S k e n ira n je u la z a , p o m o g a o m i je M a rk W elsh.

Poglavlje 13: Znakovni nizovi

401

// S t a r i nain: Syste m .o u t.p rin tln ("R e d 1: [" + x + " 1 1+y + // Novi nain: System .out.form at("Red 1: [%d % f]\ n ", x, y ) ; // i l i Sy ste m .o u t.p rin tf("R e d 1: [%d % f]\ n ", x, y ) ;

}
} /* Is p is : Red 1: [5 5.332542] Red 1: [5 5.332542] Red 1: [5 5.332542]

* ///:V idite da su fo rm at( ) i p rin tf( ) ekvivalentne. U o ba sluaja p o sto ji sa m o je d a n znakovni niz za fo rm atiran je, iza kojega sledi p o je d a n a rg u m e n t za svaki specifikator fo rin ata .

Klasa Formatter
Svu n o v u Javinu fu n k cio n a ln o st za fo rm a tira n je sadri klasa Form atter iz p ak e ta java.util. Klasu Form atter m o ete sm a tra ti prev o d io cem koji zn ak o v n i n iz za fo rm a tira n je i p o d a tk e p retv ara u eljeni rezu ltat. Kada p rav ite o b jek at tip a Formatter, n jeg o v o m k o n s tru k to r u prosledite p o d a ta k gde da poalje rezultat:
// : znako vn in izovi/K ornjaca.java import j a v a . io . * ; import j a v a . u t i l .* ; p u b lic c lass Kornjaca ( p riv a te S trin g ime; p riv a te Formatter f ; p ub lic K o rn jaca(Strin g ime, Formatter f ) th is .im e = ime; th i s . f = f ;

}
p u b lic void pom eriNa(int x, in t y) { f . form at("Kornjaa %s je na (%d,%d)\n", ime, x, y ) ;

}
p u b lic s t a t ic void m a in (S trin g [] args) { PrintStream nadimakZalzlaz = System .out; Kornjaca tom = new Kornjaca("Tom ", new Fo rm atte r(Syste m .o u t)); Kornjaca teo = new K o rn jaca("T eo ", new Form atter(nadim akZaIzla z )) ; tom .pom eriNa(0,0); teo.p o m eriN a(4 ,8 ); tom .pom eriNa(3,4); teo.p om eriN a(2 ,5 ); tom .pom eriNa(3,3); te o .po m eriN a(3 ,3 );

402

Misliti na Javi

} /* Is p is : Kornjaa Tom Kornjaa Teo Kornjaa Tom Kornjaa Teo Kornjaa Tom Kornjaa Teo

je je je je je je

na na na na na na

(0,0) (4,8) (3,4) (2,5) (3,3) (3,3)

* ///:Sav izlaz za k o rn jau tom ide na System.out, a o n aj za k o rn ja u teo n a n a d im a k za System.out. K o n stru k to r je p reklopljen tak o da p rim a vie izlazn ih lokacija, ali n ajk o risn ije su PrintStream (kao gore),O utputStream i File. O n jim a ete sazn ati vie u poglavlju Ja-

vin ulazno-izlazni sistem.

Veba 3: (1 ) Prepravite p ro g ram Kornjaca.java tak o d a sav izlaz alje na System.err.


U p re th o d n o m p rim e ru u p o treb ljen je n o v sp ecifik a to r fo rm a ta , % s. O n u k azu je n a a rg u m e n t tip a String i p rim e r je n ajjed n o stav n ije v rste specifik ato ra fo rm a ta - o n o g a koji navodi sam o tip konverzije.

Specifikatori formata
Za o dredivan je razm aka i p o rav n av anja p rilik o m u m e ta n ja p o d a ta k a p o tre b n i su sloeniji specifikatori form ata . O vo je n jiho va o p ta sintaksa:
% [i ndeks_argumenta$][i ndi k a t o r i] [ i r i n a ][ . precizn ost]konverzi ja

N ajm an ju veliinu polja zadajete p a ra m e tro m irin a. Form atter jem i da e p o lje biti iroko n ajm an je o re en bro j zn akova tak o to p o p o tre b i d o d a je razm ake. Podaci se p o d ra zu m ev an o poravnavaju ud esn o, ali to m o ete redefin isati stav ljan jem - u odeljak indikatora. P aram e ta r preciznost je su p ro ta n irini , je r zadaje m a k sim u m . Za razliku o d irine koja je p rim enljiv a na sve tipove konverzije p o d a ta k a i jed n a k o rad i za sve, preciznost im a razna znaenja za razliite tipove. Za znako vn e nizove, sp ecifik ato r preciznost zadaje najvei broj znakova objekta tip a String koje treb a ispisati. Z a b rojeve s p o k re tn im zarezom , preciznost zaaje bro j d ecim aln ih m esta koje treb a ispisati (p o d ra z u m e v a se 6); ako decim ala im a previe, Java e rezu ltat zao k ru iti, a ako ih im a p rem alo , d o d a e p ratee nule. Poto celi brojevi n em aju razlom ljen deo, preciznost se n a njih ne m oe p rim e n iti, i ob iete izuzetak ukoliko u p o tre b ite p recizn o st s k o n v erzijo m c elo b ro jn o g tipa. U n a re d n o m p rim e ru , specifikatori fo rm a ta su u p o treb lje n i za ispisivanje ra u n a za kup ov inu :
//: znakovninizovi/Racun.java import j a v a . u t i l .* ; public class Racun { p riv a te double ukupno = 0; p riv a te Formatter f = new Fo rm atter(System .o u t); p ublic void p r i n t T it l e ( ) {

Poglavlje 13: Znakovni nizovi

403

f.form at("%-15s %5s %10s\n", " A r t ik a l " , "K o l", "C en a"); f.form at("%-15s %5s %10s\n", " --- " , " " ......... " ) ;

}
pu b lic void p r in t(S tr in g ime, in t k o l, double cena) { f.form at("%-15.15s %5d % 10.2f\n", ime, k o l, c e n a ); ukupno + = cena;

}
p u b lic void p r in tT o ta l() { f.form at("%-15s %5s %10.2f\n", "P o re z ", ukupno*0.06); f.fo r m a t( %-15s %5s %10s\n", " ---- " ) ; f.form at("%-15s %5s % 10.2f\n", "Ukupno", ukupno * 1.06);

}
p u b lic s t a t ic void m a in (S trin g [] args) { Racun racun = new R acun(); r a c u n .p r in t T it le ( ) ; racu n.p rint("D eko v arobni p a s u lj" , 4, 4 .2 5 ); ra c u n .p r in t("P rin c e z in g raak", 3, 5 .1 ); ra c u n .p rin t("K a a t r i medveda", 1, 14.29); ra c u n .p r in tT o ta l( ) ;

}
} /* Is p is : A rti kal Dekov arobni Prin cezin graa Kaa t r i medved Porez Ukupno *///:Koi 4 Cena 4.25 5.10 14.29 1.42 25.06

3
1

Kao to vidite, F o rm a tte r je m o n a alatka za o d re iv an je razm aka i po rav n av an ja, a im a p rilin o saetu n otaciju. O vde su znakovni nizovi za fo rm a tira n je p ro sto k o p iran i da bi se do bili o d g ovarajui razm aci. V eba 4: (3) P repravite p ro g ra m R a c u n .jav a tak o d a sve irin e o d re u je isti sk u p k o n sta n tn ih v red n o sti. Cilj je o m o g u iti laku p ro m e n u irin e tak o to e se izm eniti sam o jed an broj na je d n o m m estu.

Konverzije klase Formatter


Najee ete sretati sledee konverzije:
Znakovi konverzije d c b Celi broj (napisan decimalno) Unicode znak Logika vrednost (tipa Boolean)

40 4

Misliti na Javi

Znakovi konverzije s f e X h % Znakovni niz Broj s pokretnim zarezom (napisan decimalno) Broj s pokretnim zarezom (napisan u naunoj notaciji) Celi broj (napisan heksadecimalno) K[ju za heiranje (napisan heksadecimalno) Literal %

Evo kako ra d e te konverzije:


//: z n ak o vn in iz o vi/K o n verzija .java import ja va .m a th .*; import j a v a . u t i l .* ; p u b lic c la s s Konverzija { p u b lic s t a t i c void m a in (S trin g [] args) { Form atter f = new Fo rm atter(System .o u t); char u = ' a ' ; S y s te m .o u t.p rin tln ("u = ' a ' " ) ; f.fo r m a t(" s : %s\n", u ); // f.fo r m a t("d : %d\n", u ) ; f.fo r m a t(" c : %c\n", u ); f.fo r m a t("b : %b\n", u ); // f . f o r m a t (" f : % f\n ", u ); // f.fo r m a t("e : %e\n", u ) ; // f.fo r m a t("x : %x\n", u ); f.fo r m a t("h : %h\n", u ); in t v = 121; S y s te m .o u t.p rin tln ("v = 121"); f.fo r m a t("d : %d\n", v ) ; f.fo r m a t(" c : %c\n", v ) ; f,fo r m a t("b : %b\n", v ) ; f .form atC 's: %s\n", v ) ; // f . f o r m a t ( " f : % f\ n ", v ) ; // f.fo r m a t("e : %e\n", v ) ; f.fo r m a t("x : %x\n", v ) ; f.fo r m a t("h : %h\n", v ) ; B ig ln te g e r w = new Biglnteger("50000000000000"); S y s te m .o u t.p rin tln ( "w = new B ig In te g e r( \ " 50000000000000\" ) " ) ; f.fo r m a t("d : %d\n", w ); // f.fo r m a t(" c : %c\n", w ); f.fo r m a t("b : %b\n", w ); f.fo r m a t( s: %s\n", w ); // f .f o r m a t (" f : % f\n ", w );

Poglavlje 13: Znakovni nizovi

405

// f.fo r m a t(" e : %e\n", w ); f.fo r m a t("x : %x\n", w ); f.fo r m a t(" h : %h\n", w ); double x = 179.543; S y s te m .o u t.p rin tln ("x = 179.543"); // f.fo r m a t("d : %d\n", x ); // f.fo rm a tC 'c : %c\n", x ); f.fo r m a t("b : %b\n", x ); f .fo r m a t (" s : % s\n", x ) ; f . f o r m a t ( " f : % f\ n ", x ) ; f.fo r m a t(" e : %e\n", x ) ; // f.fo r m a t("x : %x\n", x ); f.fo r m a t(" h : %h\n , x ); Ko n verzija y = new K o n v e r z ija (); S y s te m .o u t.p rin tln ("y = nova K o n v e r z ija ()" ) ; // f.fo r m a t("d : %d\n", y ) ; // f.fo r m a t (" c : %c\n", y ) ; f.fo r m a t(" b : %b\n", y ) ; f .fo r m a t (" s : %s\n", y ) ; // f . f o r m a t (" f : % f\n ", y ) ; // f.fo r m a t(" e : %e\n", y ) ; // f.fo r m a t("x : %x\n", y ) ; f.fo r m a t(" h : %h\n", y ) ; boolean z = f a ls e ; S y s te m .o u t.p rin tln ("z = f a l s e " ) ; // f.fo r m a t("d : %d\n", z ); // f.fo r m a t(" c : %c\n", z ); f .fo r m a t("b : %b\n", z ) ; f . fo r m a t("s : % s\n ", z ) ; // f . f o r m a t (" f : % f\n ", z ) ; // f.fo r m a t(" e : %e\n", z ); // f.fo r m a t("x : %x\n", z ); f.fo r m a t("h : %h\n", z );

(
} /* Is p is : u = 'a ' s: a c: a b: tru e h : 61 v = 121 d: 121 (prim er)

c: y b: tru e s : 121 x: 79 h : 79 w = new Biglnteger("50000000000000")

406

Misliti na Javi

d: 50000000000000 b: true s: 50000000000000 x: 2d79883d2000 h: 8842ala7 x = 179.543 b: true s: 179.543 f : 179.543000 e: 1.795430e+02 h: lef462c y = nova Konverzija() b: true s: Konverzija@9cabl6 h: 9cabl6 z = false b: false s: false h: 4d5

* ///:R edovi p retv o ren i u k o m e n ta re n isu valid n i za taj tip pro m en ljiv e; n jih o v im izvrav an jem biste izazvali izuzetak. O b ra tite p a n ju n a ko nverziju b, koja ra d i za sve g o rn je pro m en ljiv e. Iako je validna za sve tipove a rg u m en a ta, ne p o n a a se k ao to m o d a pretp o stav ljate. Za p ro ste logike tipove (boolean) i o b jek at tip a Boolean, n jen rezu ltat je true ili false, u zavisnosti o d v red n o sti. M e u tim , za sve d ru g aije arg u m e n te , rezu ltat je uvek true ukoliko tip argum e n ta nije null. ak i n u m erik a v re d n o st nula, koja je u m n o g im jezicim a (i C -u ) sinon im za false, daje true, p a m o ra te b iti o p re zn i kada tu konverziju u p o treb ljav ate s tip o v im a koji n isu logiki. Im a jo tipo v a konverzija i d ru g ih o p cija sp ecifik ato ra fo rm a ta . To je o p isa n o u d o k u m en taciji razvojnog o k ru en ja JDK za klasu Formatter. V e b a 5 : (5) Za sve o sn o v n e tip o v e konverzija iz g o rn je tabele, nap iite najsloeniji m o gui izraz za fo rm atiran je, tj. u p o tre b ite sve m o g u e specifikatore fo rm a ta d o stu p n e za taj tip konverzije.

String.format()
Java SE5 se ugledala i n a C -o v u fu n k ciju s p rin tf( ) k ojom se prave znakovni nizovi. S tring.form at( ) je statin a m eto d a koja p rim a iste arg u m e n te kao Form atterova m etoda fo rm a t( ), ali vraa ob jek at tip a String. P o d esn a je kada m e to d u fo rm a t( ) treb a da zovete sam o je d a n p u t:
// : znakovninizovi/IzuzetakBazePodataka.java pu b lic c la s s IzuzetakBazePodataka extends Exception { pu b lic IzuzetakBazePodataka ( in t ID tra n s a k c ije , in t ID u p ita,
String poruka) {

Poglavlje 13: Znakovni nizovi

407

s u p e r (S trin g .fo rm a t("(t% d , u%d) % s ", ID tra n sa k cije , ID u p ita, p o ru k a));

}
p u b lic s t a t ic void m a in (S trin g [] args) ( try ( throw new IzuzetakBazePodataka(3, 7, "U p isiva n je neuspeno"); } catch (Excep tion e) { S y s te m .o u t.p rin tln (e );

} }
} /* Is p is : IzuzetakBazePodataka: (t3 , u7) U p isiv a n je neuspeno

* ///:-

S trin g .fo rm at( ) sam o n a p ra v i o b jek at tip a Form atter i p ro sledi m u vae arg u m en te, ali k o ristei ovu m e to d u d o b ija te jasn iji i razu m ljiv iji k o d nego da sve to rad ite ru n o .

Alatka za ispisivanje u heksadecimalnom formatu


Kao d ru g i p rim e r, esto se javlja p o tre b a d a ovek pog leda bajtove b in a rn e d ato tek e u h ek sa d e c im a ln o m fo rm a tu . Evo m alo g uslu n og p ro g ra m a koji, koristei S trin g .fo rm at( ), ispisuje b in a rn i n iz b ajtov a u itljivom h ek sad ecim aln o m fo rm a tu :
//: net/m indview /util/H ex.java package n e t.m in d v ie w .u til; import j a v a . io . * ; p u b lic c la s s Hex { p u b lic s t a t ic S trin g fo rm a t(b yte [] podaci) { S t r in g B u i1der re z u lta t = new S t r in g B u ild e r (); in t n = 0; fo r (b y te b : podaci) { i f ( n % 16 = = 0) re z u lta t.a p p en d (S trin g .fo rm at("% 0 5 X : " , n )) ; r e z u lt a t . append(String.form at("%02X " , b ) ); n++; i f ( n % 16 = = 0) re z u lta t.a p p e n d ("\ n ");

}
re z u lta t.a p p e n d ("\ n "); return r e z u lt a t .t o S t r in g O ;

}
p u b lic s t a t ic void mai n (S t r in g [] args) throws Exception { i f ( a r g s . 1ength = = 0) // T e s tira n je isp isivan jem datoteke ove klase: S y s te m .o u t.p rin tln ( fo r m a t(B in a r y F ile .r e a d ("H e x .c la s s " ))); el se Sy stem .o u t.pri n t l n ( fo rm a t(B in a ryF ile .re a d (n e w F i le ( a r g s [ 0 ] ) ) ) ) ;

408

Misliti na Javi

} /* Is p is : (prim er) 00000: CA FE BA BE 00 00010: 00 23 OA 00 02 00020: 00 27 OA 00 28 00030: 00 2C 00 2D 08 00040: 31 08 00 32 OA 00050: 36 00 37 07 00

00 00 00 00 00 38

00 31 22 08 29 OA 2E OA 33 00 OA 00

00 00 00 00 34 12

52 24 02 02 OA 00

0A 07 00 00 00 39

00 00 2A 2F 15 OA

05 25 08 09 00 00

00 OA 00 00 35 33

22 00 2B 30 OA 00

07 26 OA 00 00 3A

* ///:Z a o tv a ra n je i uitavanje b in a rn e d ato tek e u ovom p ro g ra m u u p o tre b lje n a je u slu n a klasa net.mindview.util.BinaryFile k o ja e biti p red stav ljen a u p o glavlju Javin u la zno-izlazni sistem . M eto d a re a d ( ) v raa celu d a to te k u u o b lik u n iza b ajto v a (byte).

V eba 6: (2) N apravite klasu koja sad ri po lja tip o v a int, long, float i double. Z a tu klasu n a p ra v ite m e to d u to S trin g ( ) k oja u p o treb ljav a S tring.form at( ) i p o k aite d a klasa rad i
ispravno .

Regulami izrazi
Regularni izrazi su ve d u g o sastavni deo sta n d a rd n ih U nixovih alatki sed i awk, i jezika Pyth o n i Perl (neki tvrde da su o n i glavni razlog to je Perl tako uspean). U Javi su alatke za o b ra d u znakovnih nizova ranije bile delegirane u klase String, StringBuffer i StringTokenizer. U p o re e n ju s reg u larn im izrazim a, te alatke su im ale relativno sk ro m n e m ogunosti. R egularni izrazi su m o n e i fleksibilne alatke za o b ra d u teksta. O n e o m o g u av a ju p ro gram sk o zadavanje sloenih u zo rak a teksta koji m o g u biti p ro n a e n i u u la z n o m znakovn o m n izu. K ada te uzorke p ro n a e te , m o ete da reagujete kako god hoete. Iako je sin taksa re g u larn ih izraza isp rv a teka, n jih o v saet d in a m i k i jezik m o ete u p o tre b iti za reavanje svih vrsta zadataka u o b ra d i, p ro n a la en ju , izb o ru , u re iv a n ju i p ro v eri znakovn ih nizova, n a p o tp u n o u o p te n nain.

Osnove
R egularan izraz je nain o p isivanja zn ak o v n ih nizova na u o p te n n ain , ta k o d a m oete rei: ,A ko znakovni niz im a ovo u sebi, o n d a od govara o n o m e to tra im . Na p rim er, kako b iste saoptili da isp red b ro ja m o e, ali n e m o ra biti znak m in u s, piete m in u s i iza njega zn ak pitanja:
_?

D a b iste opisali celi broj, kaete d a je to je d n a ili vie cifara. U re g u la rn im izrazim a, cifra (engl. digit ) opisuje se sa \d. U k oliko ste im ali posla s re g u larn im izrazim a u d ru g im jezicim a, o d m a h ete u o iti razlik u u o b ra d i o b rn u tih kosih crta. U d ru g im jezicim a \\ znai: H o u d a u m e tn e m o b i n u (d o slo v n u , literaln u ) kosu c rtu u re g u laran izraz. Ne tre b a jo j p rid a v ati specijalno znaenje. U Javi, \\ znai: U m eem k osu c rtu u regularan izraz, p a sledei znak im a sp ecijaln o zn aen je. N a prim er, re g u laran izraz za cifru je \\d. A ko ho ete da u m e tn e te stv arn u k o su c rtu , piete \\\\. M e u tim , za prelazak u novi red i ta b u la to r pie se sam o jedna kosa crta: \n \t.

Poglavjje 1 3: Znakovni nizovi

409

+ o zn aav ajed an ili vie p re th o d n ih izraza. D akle, d a b iste re k li m o d a zn ak m in u s, a iza njega je d n a ili vie cifara, piete:
-?\\d+

R egu larn i izrazi se n ajjed n o stav n ije k o riste p o m o u fu n k c io n a ln o sti u g ra e n e u klasu S trin g . Na p rim er, u tv rd i e m o d a li se neko liko znakovm h nizova p o d u d a ra (engl. m atch) s g o rn jim reg u larn im izrazom :
// : znakovn in izovi/Pod ud aran jeC elihBrojeva.java p u b lic c la s s PodudaranjeCelihBrojeva { p ub lic s t a t ic void m a in (S trin g [] args) { Sy ste m .o u t.p rin tln ("- 1 2 3 4 ".m a tc h e s("- ?\\ d + ")); S y ste m .o u t.p rin tln ("5 6 7 8 ".m a tc h e s("- ?\\ d + ")); System .o u t.p rin tln ("+ 9 1 1 ".m atch es( - ?\ \ d + ")); Sy ste m .o u t.p rin tl n("+ 9 11 ".m atches("( - 1\\+)?\\d+")) ;

}
} /* Is p is : true tru e fa l se true

* ///:Prva dva izraza o d go v araju , ali trei p o in je zn ak o m +, k jji e sam p o sebi leg itim an , ali ne o d g ovara naem re g u la rn o m izrazu. Z ato n a m treb a n a a ii d a k a e m o : m o e poeti z n ak o m + ili U reg u larn im izrazim a, zag rad e im a ju efekat g a ip is a n ja izraza, a vertik alna crta I znai logiko ili (O R ). D akle izraz (-|W + )? znai da ovaj d eo znak o v n o g niza m oe b iti - ili + ili (zbog zn ak a ?) n i je d n o ni dru go . Poto u reg u larn im izrazim a zn ak + im a sp ecijaln o znaenje, rn o ra se p o m o u \\ p retv o riti u iziaznu sekvencu, da bi se u izrazu pojavio k ao ob ian znak. U klasu S trin g ugraden a je m e to d a s p l i t ( ), ko risn a alatka za reg ularn e izraze koja znai: Iscepaj ovaj znakovni niz n a delove koji se p o d u d a ra ju s datirn reg u larn im izrazom .
//: znakovninizovi/C epanje.java import j a v a . u t i 1 .*; p ub lic c la s s Cepanje { p ub lic s t a t ic Strin g vite z o v i = "Then, when you have found the shrubbery, you must " + "cu t down the m ightiest tre e in the f o r e s t . . . " + " w i t h . . . a h e rrin g !"; pu b lic s t a t ic void s p li t ( S t r in g re g iz ) { System .out. pri ntl n( A rra y s .to S tr i ng(vi te z o v i. s p li t ( r e g i z ) ) ) ;

410

Misliti na Javi

p ub lic s t a t ic void m a in (S trin g [] args) { s p l i t (" " ) ; // Ne mora da sadri s p e c ija ln e znakove spl i t ( 1 1 \\W+") ; // Znakovi k o ji ne mogu b i t i deo rei s p lit("n \ \ W + "); // 'n ' i znakovi k o ji ne mogu b i t i deo re i

}
} /* Is p is : [Then,, when, you, have, found, th e, shrub bery,, you, must, cu t, down, th e, m ig h tie s t, tr e e , in , th e, f o r e s t . . . , w i t h . . . , a, h e rrin g !] [Then, when, you, have, found, th e , shrubbery, you, must, c u t, down, th e, m ig h tiest, tr e e , in , th e, fo r e s t, w ith , a, herring] [The, whe, you have found the shrubbery, you must cut dow, the m ig h tiest tre e i , the f o r e s t . . . w it h . . . a h e rrin g !]

* ///:Prvo, im ajte u v id u d a o b in e znakove m o ete u p o tre b lja v a ti k ao re g u larn e izraze - reg u lara n izraz n e m o ra sad rati specijalne znakove, k ao to v idite u p rv o m p o ziv u m e to d e sp lit( ), koji tek st cepa n a svakom razm ak u . D ru g i i tre i p oziv m e to d e s p lit( ) sadre \W , izraz za zn a k koji n e m o e biti d eo rei (verzija n a p isa n a m alim slovom , \w , obeleava z n a k koji m oe b iti d eo rei). U o ite d a su u d ru g o m sluaju u k lo n jen i znakovi in terp u n k cije. Trei p o ziv m e to d e sp lit( ) kazuje: slovo n i je d a n ili vie zn akova koji ne m o g u b iti d e o rei. Iz rezu lta ta v id ite da se u n je m u n e p o jav lju ju uzorci d efin isan i u re g u la rn o m izrazu. Preklo pljena verzija m e to d e String.split( ) o m o g u av a zadavanje najveeg ozvoljen o g b ro ja cepanja. Poslednja alatka re g u la rn ih izraza u g ra en a u klasu String jeste zam en a. M oete zam e n iti p rv i p o d u d a rn i d eo ili sve njih:
//: znakovninizovi/Zamena.java import s t a t ic n e t.m in d v ie w .u til. P r i n t . * ; p ub lic c la s s Zamena { s t a t ic S trin g s = S p li t t in g . v i t e z o v i; p u b lic s t a t i c void m a in (S trin g [] args) { p r in t(s .r e p la c e F ir s t (" f\ \ w + ", " l o c a t e d " ) ) ; p ri n t ( s . re p la c e A l1("s h ru b b e ry |tre e |h e rri n g ", "banana")) ;

}
} /* Is p is : Then, when you have located the shrubbery, you must cut down the m ightiest tre e in the f o r e s t . . . w it h ... a herrin g! Then, when you have found the banana, you must cut down the m ig h tiest banana in the f o r e s t . . . w it h . . . a banana!

* ///:P rv o m izrazu o d g o v ara slovo f i je d a n ili vie zn akova koji m o g u b iti d eo rei (povedite ra u n a o to m e d a je ovoga p u ta w m alo slovo). Z am en ju je se sam o p rv i p ro n a e n i p o d u d a rn i deo, pa re ,,fo u n d biva zam e n jen a reju ,,located. D ru g o m izrazu od g o v ara bilo koja o d tri rei koje su razdvojene v ertik aln im c rta m a (logikim O R ), i zam e n ju ju se svi p ro n a e n i p o d u d a rn i delovi.

Poglavlje 13: Znakovni nizovi

411

V ideete d a jo m o n ije alatke za z a m e n u im aju re g u larn i izrazi koji se ne u p o treb ljavaju za znakov n e nizove - n a p rim er, m oete p o zivati m e to d e d a o n e obavljaju zam ene. U sluajevim a kada se reg u larn i izraz u p o treb ljav a vie p u ta , z n a tn o su efikasniji reg ularn i izrazi koji se n e u p otreb ljav aju za znakovne nizove.

Veba 7: (5) K oristei d o k u m e n tac iju klase java.util.regex.Pattern kao resu rs, n ap iite i testirajte reg u la ra n izraz koji proverava d a li reenica p o in je velikim slovom i zavrava se takom . Veba 8: (2) Pocepajte znakovni niz Cepanje.vitezovi ta m o gde se n a u rei ,,the ili ,,you. Veba 9: (4) K oristei d o k u m e n taciju klase java.util.regex.Pattern kao resu rs, sve sam oglasnike u Cepanje.vitezovi zam en ite p o d crta m a .

Pravljenje regularnih izraza


K ad uite o re g u larn im izrazim a, m oete p o eti o d p o d sk u p a svih m o g u ih tv o rb i. P o tp u n a lista tv o rb i za pravljenje re g u larn ih izraza nalazi se u d o k u m e n ta c iji razv o jn o g o k ru e n ja JDK za klasu Pattern iz pak eta java.util.regex.
Znakovi B \xhh Nuhhhh \t \n V \f Znak B Znak iji je heksadecimalni kod oxhh Unicode znak iji je heksadecimalni kod oxhhhh Tabulator Prelazak u novi red Vraanje na poetak tekueg reda Prelazak na novi list Znak Esc (izlaz)

\e

Snaga reg u la rn ih izraza najbolje se vidi u d efin isan ju klasa znakova. Evo n ek ih tip i n ih nain a p ravljenja klasa znakova i n ekih u n a p red d efin isan ih klasa:
Znakovi Bilo koji znak fabc] ["abc] [a-zA-Z] [abcfhijj] [a-z&&[hij]J \s \S Bilo koji od znakova a. b ili c (isto to i alblc) Bilo koji znak osim a, b ili c (negacija) Bilo koji znak od a do z ili od A do Z (opseg) Bilo koji od znakova a,b,c, h,i.j (isto to i alblclhlilj) (unija) Bilo koji od znakova h, i ili j (presek) Bilo koji od znakova za belinu (razmak, tabulator, prelazak u novi red, prelazak na novi list, vraanje na poetak tekueg reda) Bilo koji znak sem znakova za belinu (fA\s])

412

Misliti na Javi

Znakovi \d
\D

Numerika cifra [0-9J Bilo koji znak sem znakova za cifre [A0-9] Bilo koji znak koji moe biti deo rei [a-zA-Z_0-9] Bilo koji znak sem znakova koji mogu biti deo rei [A\w]

\w \W

O vde sm o pok azali sam o m ali d eo m o g u ih re g u la rn ih izraza; da b iste lako p ristu p ali svim m o g u im u zo rc im a re g u la rn ih izraza, stra n ic u JD K d o k u m e n ta c ije za ja v a .u til.re g e x .P a tte rn p re tv o rite u obeleiva.
Logiki operatori XY XIY (X)
X,

a potom Y

X ili Y Grupa koju treba pronai. U nastavku izraza, /tu grupu za hvatanje oznaavate sa \i.

Izrazi za granice
A

Poetak reda Kraj reda Granica rei Granica koja ne moe biti deo rei Kraj prethodno pronadene podudarnosti

$ \b \B \G

Kao p rim e r, svi sledei izrazi se p o d u d a ra ju s n izo m znakova ,,R ud olph :


//:

znakovninizovi/Rudolph.java

p ublic c la s s Rudolph { pu b lic s t a t i c void m a in (S trin g [] args) { fo r (S tr in g uzorak : new S t r i n g [ ] { "Rudolph", " [rR ]u d o lp h ", " [ r R ] [ a e i o u ] [ a - z ] o l , "R .* " )) Syste m .o u t.p rin tln ("R u d o lp h ".m a tc h e s(u z o rak )) ;

}
} /* Is p is : true true true true

* ///:N aravno, n e bi tre b a lo pisati najtee shvatljiv reg u laran izraz, nego najjednostavniji koji zavrava p o sao . K ada p o n e te d a piete nove reg u larn e izraze, vero v atn o ete esto u p o treb ljav ati svoj k o d kao referen cu .

Poglavlje 13: Znakovni nizovi

413

Kvantifikatori
K vantifikator o p isu je n a in n a koji u z o ra k a p so rb u je u lazn i tekst: Pohlepno: ukoliko se d ru g a ije ne n a re d i, k v an tifik ato ri su p o h le p n i (engl. greedy). P o h le p an izraz p ro n alaz i sve m o g u e p o d u d a rn o s ti sa u zo rk o m . T ip ian u z ro k p ro b lem a jeste p re tp o sta v k a d a e se izraz p o d u d a riti sam o s p rv o m p o d u d a rn o m g ru p o m znakova, a o n je zap rav o p o h le p a n i nastavie d a tra i sve d o k n e p ro n a e najvei m o g u i p o d u d a rn i zn ak o v n i niz.

Rezervisano: O vaj k v a n tifik a to r se obeleava z n ak o m p ita n ja, a p ro n a laz i m in im a lan b ro j znakova p o tre b a n za p o d u d a ra n je sa u zo rk o m . N aziva se i lenj, tn inim alno podudarajui, nepohlepan. Posesivno: tr e n u tn o d o stu p a n sam o u Javi (u d ru g im jezicim a ne) i n a p re d n iji, p a ga vero v atn o n eete o d m a h u p o treb ljav ati. Kada se re g u laran izraz p rim e n i n a znakovni niz, o n g en erie m n o g o stan ja d a bi m o g ao d a se v ra ti ako n e u sp e d a nae p o d u d a rn o s t. P osesivni k v an tifik ato ri n e uv aju ta m e u sta n ja i tim e spreavaju vraanje. M oete ih u p o tre b iti k ako b iste spreili reg u laran izraz da p odivlja, a i d a bi rad io efikasnije.
Rezervisani X?? X*? X+? X{n}? X(n.}? X{n,m}? Posesivni X?+ X*+ X++ X{n}+ X{n,}+ X(n,m}+ Podudara se sa X, jednom ili nula puta X, nula ili vie puta X, jednom ili vie puta X, tano n puta X, najmanje n puta X, najmanje n puta, ali ne vie od m puta

Pohlepni X? X*

x+ X ( r > }
X(n.) X{n,mj

Im ajte u v id u d a izraz X esto treb a da zatv o rite u zag rad e d a bi rad io o n a k o kako hoete. N a p rim e r:
abc+

m o d a izgleda kao d a se p o d u d a ra s n izo m abc je d n o m ili vie p u ta , i ini se d a ete d o b iti tri p o d u d a ra n ja kada ga p rim e n ite na ulazni zn ak o v n i niz abcabcabc. M e u tim , izraz zapravo kae: P ro n a i ab i iza njega je d n o ili vie slova c. D a b iste p ro n ali ceo znakovni niz abc je d n o m ili vie p u ta , m o ra te napisati:
(abc)+

R eg u larn i izrazi e vas Iako p rev ariti; u p o re en ju s Javom , to je sasvim d ru g aiji jezik.

414

Misliti na Javi

CharSequence
Interfejs CharSequence u tv r u je o p tu defin iciju niza zn ak o v a a p stra h o v a n u iz klasa CharBufFer, String, StringBuffer ili StringBuilder:
in te rfa c e CharSequence { c h a rA t(in t i ) ; le n g th (); subSequence(int pocetak, in t k r a j ) ; t o S t r in g O ;

} O vaj interfejs realizuju p re th o d n o s p o m e n u te klase. M n o g e o p eracije s reg u larn im izrazim a p rim a ju a rg u m en te tip a CharSequence.

Klase Pattern i Matcher


Po pravilu, neete se sluiti relativno o g ran i en im u slu n im m e to d a m a klase String, nego ete prev o diti objekte reg u larn ih izraza. Z a to treb a d a uvezete java.util.regex i zatim prevedete reg ularan izraz stati n o m m e to d o m Pattern.com pile( ). Tako d obijate objek at tip a Pattern, napravljen na o sn o v u njegovog arg u m e n ta tip a String. Pattern u potrebijavate pozivanjem m e to d e m atcher( ) a njoj p ro sle u jete znak o v n i niz koji treb a p retraiti. M eto d a m atcher( ) pravi objek at tip a M atcher k ojem je n a ra sp o lag an ju sk u p operacija (m oete ih videti u JDK d o k u m en taciji ldase ja v a .u til.re g e x .M a tc h e r). Na p rim er, m etoda repIaceAIl( ) svojim a rg u m e n to m z am e n ju je sve p o d u d a rn e delove znak o v n o g niza. K ao p rv i p rim er, sledea klasa se m o e u p o tre b iti za te stira n je re g u larn ih izraza na u lazni znakovni niz. Prvi a rg u m e n t n a k o m a n d n o j liniji je u iazn i zn ak o v n i niz s kojim se re g u larn i izraz p o re d i. Iza njega slede je d a n ili vie re g u la rn ih izraza koje treb a p rim e n iti na ulaz. U U n ix u /L in u x u , reg u la rn i izrazi n a k o m a n d n o j liniji m o ra ju b iti zatv o ren i u navodnike. Ovaj p ro g ra m m o ete u p o treb ljav ati za testiran je to k o m k o n stru isan ja regularnih izraza, kako biste videli da li p ro izv o d e eljeno p o n aa n je u p o g led u p o d u d a ra n ja .
//: zn ak o vn in iz o vi/T e stira n je R e g u la rn ih lz ra z a .ja va // Slu i za isprobavanje reg ularnih iz raz a. // {Args: abcabcabcdefabc "abc+" "(ab c)+ " " ( a b c ) { 2 , } " } import ja v a .u t il,r e g e x .* ; import s t a t ic n e t.m in d v ie w .u til. P r i n t . * ; p ub lic c lass T e stira n je R e g u la rn ih lz ra z a { p ub lic s t a t ic void m a in (S trin g [] args) { if(a r g s .le n g th < 2) { p rin t("U p o tre b a :\ n ja v a T e s tira n je R e g u la rn ih lz ra z a " + "nizZnakova re g u la ra n lz ra z + "); S y s te m .e x it(0 );

}
p r in t("U la z : \ " " + args[0] + " \ " " ) ; f o r (S tr in g arg : args) { p rin t("R e g u la ra n iz ra z : \ " " + arg + " \ " " ) ;

Poglavlje 13: Znakovni nizovi

415

Pattern p = P a tte rn .c o m p ile (a rg ); Matcher m = p .m a tc h e r(a rg s [0 ]); w h ile (m .fin d ()) { p rin t("Po d u d ara se \ " " + m.groupO + "\ " na mestima " + m .s ta rtO + + (m.end() - 1 ));

} } }
} /* Is p is : U laz: "abcabcabcdefabc" Regularan iz ra z : "abcabcabcdefabc" Podudara se "abcabcabcdefabc" na mestima 0-14 Regularan iz ra z : "abc+" Podudara se "abc" na mestima 0-2 Podudara se "abc" na mestima 3-5 Podudara se "abc" na mestima 6-8 Podudara se "abc" na mestima 12-14 Regularan iz ra z : "(a b c )+ Podudara se "abcabcabc" na mestima 0-8 Podudara se "ab c na mestima 12-14 Regularan iz ra z : " ( a b c ) { 2 , } " Podudara se "abcabcabc" na mestima 0-8

* ///:O b je k at tip a P attern pred stav lja p re v e d e n u v erziju reg u larn o g izraza. Kao to v id ite u p re th o d n o m p rim e ru , za pravljenje o b jek ta tip a M atcher o d p rev ed en o g o b jek ta tip a Pattern m o ete u p o tre b iti m e to d u m a tc h e r( ) i ulazni znak o v n i niz. I klasa Pattern im a sta ti n u m eto d u :
s t a t i c boolean m atches(Strin g re g iz , CharSequence ulaz)

koja isp itu je da li regiz o d g o v ara celom u la z n o m nizu znakova (o b jek tu tip a CharSequence), i m e to d u s p lit( ) koja p ro izv o d i niz o b jek ata tip a String koji se p o d u d a ra ju s reg u la rn im izrazom regiz. O b je k a t tipa Matcher g en eriete p o ziv o m m eto d e Pattern.m atcher( ) uz ulazni znakovni niz kao arg u m e n t. Z a tim se o b je k a t tip a Matcher u p o treb ljav a za p ristu p a n je rez u ltatim a, je r se za u tv r iv an je u sp en o sti ili n eu p e n o sti razliitih vrsta p o d u d a ra n ja u p o treb ljav aju njegove m eto d e:
boolean boolean boolean boolean matches() loo k in g A t() fin d () f in d ( in t s t a r t )

M eto da m atches( ) je u spen a ukoliko se u z o ra k p o d u d a ra s c e lo k u p n im u lazn im znak o v n im n izo in , d o k je m e to d a lookingA t( ) u sp en a ako se u lazni znak o v n i niz p o d u d a ra sa u z o rk o m poev od njegovog poetka.

416

Misliti na Javi

Veba 10: (2) Za reenicu Java now has regular expressions odredite da li e sledei izrazi pronai neko podudaranje:
''Ja v a \B reg .* n.w \s+ h (a|i)s s?
s*

s+ S {4} S {1 ). s{0 ,3 )

Veba 11: (2) Prim enite regularan izraz


( ? i ) ( ( A[a e io u ]) | (\s+ [aeiou ]))\w + ?[aeiou ]\b

na
"A r lin e ate eig ht apples and one orange w hile A nita hadn't any"

Metoda find()
M etoda M atcher.find( ) moe se upotrebiti za pronalaenje p o dudaranja vie uzoraka u znakovnom nizu na koji je prim enjena. Na prim er:
// : znak ovn in izovi/Pron alazen je.java import ja v a .u t il,r e g e x .* ; import s t a t ic n e t.m in d v ie w .u til. P r in t . * ; p u b lic c la s s Pronalazenje { p u b lic s t a t ic void m a in (S trin g [] args) { Matcher m = Pattern .com pile("\\w + ") .m atcher("Evening is f u l l of the lin n e t 's wings" ) ; whi 1e(m .fi nd ( ) ) printnb(m .group() + 1 1 "); p r in t(); in t i = 0; whi 1e (m .f in d (i) ) { printnb(m .group() + 1 1 ");

i++;

} }
} /* Is p is : Evening is f u l l of the lin n e t s wings Evening vening ening ning ing ng g is is s f u l l f u ll u ll 1 1 1 of o f f the the he e lin n e t lin n e t innet nnet net et t s s wings wings ings ngs gs s

* ///.-

Poglavlje 13: Znakovni nizovi

417

Uzorak \\w+ cepa ulaz na rei. M etoda fin d () radi kao iterator, poto se kroz ulazni znakovni niz kree unapred. M eutim , drugoj verziji m etode fin d ( ) moete dati celobrojni argum ent koji joj kazuje redni broj znaka za poetak pretraivanja - ta verzija zadaje vrednost argum enta kao mesto pretraivanja, kao to se vidi iz rezultata.

Grupe
G rupe su regularni izrazi navedeni u zagradam a koji se naknadno m ogu pozivati preko broja grupe. G rupa 0 je ceo izraz, grupa 1 je prva grupa u zagradi itd. Stoga u
A (B (C ))D

postoje tri grupe: grupa 0 je ABCD, grupa 1 je BC, a grupa 2 je C. O bjekat tipa Matcher im a m etode koje daju podatke o grupam a:

public int g ro u p C o u n t( ) vraa broj grupa u uzorku objekta. G rupa 0 se ovde ne broji. public String g ro u p ( ) vraa grupu 0 (ceo p o d u darni deo) iz preth o d n e operacije pronalaenja podudarnosti (m etode fin d ( ), na prim er).
p u b lic String group(int i) vraa grupu datog rednog broja iz preth o d n e operacije pronalaenja podudarnosti. Ako je pronalaenje p odudarnosti uspeno obavljeno, ali se data grupa nije podudarila ni s jednim delom ulaznog znakovnog niza, vraa se null.

public int start(in t group) vraa indeks poetka grupe pronaene u prethodnoj operaciji
pronalaenja podudarnosti. p ublic in t e n d (in t group) vraa indeks poslednjeg znaka, plus jedan, grupe pronaene u prethodnoj operaciji pronalaenja podudarnosti. Evo prim era:
//: znakovninizovi/Grupe.java import j a v a . u t i l .re g e x .*; import s t a t ic n et.m in d vie w .u ti1. P r in t .* ; p u b lic c la s s Grupe ( s t a t i c p ub lic fin a l S trin g PESMA = "Twas b r i l l i g , and the s lit h y toves\n" + "Did gyre and gimble in the wabe.\n" + "A ll mimsy were the borogoves,\n" + "And the m om e raths outg rabe.\n\n" + "Beware the Jabberwock, my son,\n" + "The jaws th at b it e , the claws th at catch .\ n " + "Beware the Jubjub b ird , and shun\n" + "The frumious Bandersnatch."; p u b lic s t a t ic void m a in (S trin g [] args) { Matcher m = Pattern.com pi1e("(?m )(\\S+ )\\s+ ((\\S+ )\\s+ (\\S+ ))$ ") .matcher(PESMA);

418

Misliti na Javi

w h ile (m .fin d ()) { f o r ( i n t j = 0; j < = m .groupCount(); j++) p r in t n b (" [" + m .group(j) + " ] " ) ; p r in t();

} }
} /* Is p is : [th e s l it h y t o v e s ] [t h e ] [ s lit h y t o v e s ] [s lit h y ][ t o v e s ] [in the w a b e .][in ][th e w a b e.][th e][w ab e.] [were the b o ro g o ves,][w ere ][th e boro g o ves,][th e][b o ro g o ves,] [mome raths outgrabe.][m om e][raths o u tg ra b e .][ra th s ][o u tg ra b e .] [Jabberwock, my so n,][Jab berw ock,][m y s o n ,][m y ][so n ,] [claw s th a t c a tc h .][c la w s ][th a t c a t c h .] [t h a t ] [c a t c h . ] [b ird , and shun] [b ird ,][a n d shun][and] [shun] [The frumious Bandersnatch.] [The][frum ious B an dersnatch .][frum iou s][Ban dersnatch.]

* ///;Ovo je poetak pesme ,,Jabberwocky Luisa Kerola, iz knjige Alisa iza ogledala. Uzorak u regularnom izrazu im a vie grupa u zagradam a koje se sastoje od proizvoljnog broja znakova koji nisu beline (\S+) praenog proizvoljnim brojem znakova koji jesu beline (\s+). Cilj je uhvatiti tri poslednje rei u svakom redu; kraj reda oznaava $. M eutim, uobiajeno ponaanje je da se $ poredi s krajem celog ulaznog niza, pa regularnom izrazu m orate eksplicitno rei da obrati panju na znakove za prelazak u novi red u ulaznom nizu. To se postie indikatorom uzorka (?m) na poetku niza (ubrzo emo razm otriti indikatore uzoraka).

Veba 12: (5) Prepravite program Grupe.java tako da broji sve razliite rei koje ne
poinju velikim slovom.

Metode start() i end()


Nakon uspeno obavljene operacije traenja podudaranja, m etoda s ta rt( ) vraa indeks poetka prethodno podudarnog dela, a e n d ( ) vraa indeks poslednjeg podudarnog znaka plus jedan. Pozivanjem m etoda s ta r t( ) ili e n d ( ) nakon neuspeno obavljene operacije traenja podudaranja (ili pre nego to je takva operacija zapoeta) prouzrokuje se izuzetak IlIegalStateException. Naredni program prikazuje i rad m etoda m atches( ) i IookingAt( ):
/ / : zn ak o vn in iz o vi/StartE n d .java import j a v a . u t i 1. regex.*; import s t a t ic n e t.m in d view .u ti1 .P r in t . *; p ub lic c la s s StartEnd { p u b lic s t a t i c S trin g ulaz = "As long as there is in ju s t ic e , whenever a\n" + "Targathian baby c rie s out, wherever a d istre ss\ n " + "sig n al sounds among the s ta rs . . . W e 'll be th e re.\n " + "This fin e ship, and th is fin e crew . . . \ n " + "Never g ive up! Never su rre n d e r!";

C ita t iz g o v o ra k o m a n d a n ta T a g a rta u seriji Galaksija.

Poglavlje 13: Znakovni nizovi

419

p riv a te s t a t i c c la s s Is p is i { p riv a te boolean regizOdstampan = f a ls e ; p riv a te S trin g re g iz ; Is p is i( S t r in g reg iz ) { t h is .r e g iz = re g iz ; } void Is p is i( S t r in g poruka) { if(!regizO dstam pan) { p r in t(r e g iz ); regizOdstampan = tru e ;

}
p rin t(p o ru k a );

} }
s t a t ic void is p it a j ( S t r i n g s, S trin g re g iz ) Is p is i d = new I s p i s i ( r e g i z ) ; Pa ttern p = P a tte rn .c o m p ile (re g iz ); Matcher m = p .m a tc h e r(s ); whi 1e (m .fi n d ()) {

d .is p is i ("fin d ()

+ m.groupO +

poetak = "+ m .s ta rt() + " k raj = " + m .e n d ()); if(m .lo o k in g A t()) // r e s e t() n ije potrebna d .is p is i(" lo o k in g A t ( ) poetak = "

+ m .startO + " kraj = " + m .end()); if(m .m atches()) // re se t() n ije potrebna d .is p is i ("matchesO poetak = 1 1 + m .sta rt() + " kraj = " + m .end());

}
p u b lic s t a t ic void m a in (S trin g [] args) { f o r (S t r in g in : u l a z . s p l i t ( " \ n " ) ) { p r in t (" u la z : " + in ) ; f o r (S t r in g regiz : new S t r i n g [ ] { "\\w *ere\\w*", "\\w *e v e r", "T\\w+", " N e v e r .* ? !" } ) is p it a j ( in , re g iz );

} }
} /* Is p is : ulaz : As long as th ere is in ju s t ic e , whenever a \w*ere\w* f in d () 'th e r e ' poetak = 11 kraj = 16 \w*ever fin d () 'whenever' poetak = 31 k raj = 39 ulaz : Targathian baby c rie s out, wherever a d is tre s s \w*ere\w* f in d () wherever' poetak = 27 kraj = 35 \w*ever f in d () w herever poetak = 27 k raj = 35 T\w+ f in d () 'T a rg a th ia n ' poetak = 0 kraj = 10 lo o k in g A t() poetak = 0 kraj = 10 ulaz : signal sounds among the s ta rs . . . W e 'll be th ere. \w*ere\w*

420

Misliti na Javi

find() 'there' poetak = 43 kraj = 48 ulaz : This fine ship, and this fine crew ... T\w+ find() 'This' poetak = 0 kraj = 4 lookingAt() poetak = 0 kraj = 4 ulaz : Never give up! Never surrender! \w*ever find() 'Never' poetak = 0 kraj = 5 find() 'Never' poetak = 15 kraj = 20 lookingAtO poetak = 0 kraj = 5 Never.*?! find() 'Never give up !' poetak = 0 kraj = 14 find() 'Never surrender!' poetak = 15 kraj = 31 lookingAt() poetak = 0 kraj = 14 m a t che s O poetak = 0 kraj = 31

* ///:Vodite rauna o tom e da e m etoda fin d ( ) pronai regularan izraz bilo ge u ulazu, ali lookingA t( ) i m atch es( ) uspevaju sam o ako se regularan izraz p o d udara od samog poetka ulaza. D ok m etoda m atch es( ) uspeva sam o ukoliko se ceo ulaz p o d u d ara s regularnim izrazom , lookingAt( )4 uspeva ako se pod u dara sam o prvi deo ulaza.

Veba 13: (2) Prepravite program StartEnd.java tako da upotrebljava Grupe.PESMA kao ulaz, ali da ipak proizvodi pozitivne rezultate za m etode fin d ( ), lookingA t( ) i m atches( ).

Indikatori klase Pattern


A lternativna m etoda com pile( ) prihvata indikatore koji utiu na ponaanje prilikom traenja podudarnosti:
Pattern Pattern .com pi1e ( S t r i ng re g iz , in t in d ik a to r)

gde je indikator jedna od sledeih konstanti klase Pattern:


Indikator prevoenja Pattern.CAIMON _EQ Uticaj Smatra se da se dva znaka podudaraju ako i samo ako se podudaraju njihove pune kanonske dekompozicije. Na primer, kada je ovaj indikator zadat, izraz\u003F se podudara sa znakovnim nizom Kanonska ekvivalencija se podrazumevano ne uzima u obzir. Podrazumevano ponaanje poredenja bez uzimanja u obzir veliine slova |engl. case-insensitive matching) jeste da se porede samo znakovi iz skupa US-ASCII. Ovaj indikator slui da se ispita podudaranje uzorka bez uzimanja u obzir da li su slova velika ili mala. Ako uz ovaj indikator zadate i indikator U N IC O D E_C A S E, moete porediti i znakove iz skupa Unicode.

Pattern.CASE INSENSITIVE (?')

N em am p o jm a odakle im ovakvo im e m etod e, n iti na ta bi o n o trebalo da upuuje, ali u m iru ju injenicu da o soba koja izm ilja nein tu itiv n a im ena m eto d a jo uvek radi za Sun, i da je njihova o igledna politik a nerevidiranja projekata koda jo uvek na snazi. Izvinite zbog sarkazm a, ali tako neto poinje da zam ara nakon nekoliko godina.

Poglavl'c ! 3: Zi lakovni nizovi

421

Indikator prevoenja P attern.CO M M EN TS (?x) P attern.DO TALL l?sj Pattern.M ULTILINE |?m)

Uticaj U ovom reimu zanemaruju se bo'ine. f .'o kraja reda zanemaruju se ugradeni komentari koji po . ju y kom #. Ugradivanjem indikatora (?x) u izraz moe se ukjjuru /im Unix lines. U reimu dotall, izraz . se podudctr~ - bilo kojim znakom, ak i s graninikom reda. Izraz . se poctr r /ano ne podudara s graninicima redova. U reimu multiline, izrazi A i $ , .. .uju se s poetkom odnos.no krajem reda. A se podudar is p 'f 'kom ulaznog znakovnog niza, a $ s krajem ulaznog znak< 'n : nza. Ovi izrazi se podrazumevano podudaraju samo s poi i krajem celog ulaznog znakovnog niza. Poreenje bez uzimanja u obzir vtw slova, kada je ukljueno indikatorom CASE_INSENSITIVE, snrovodi se u skladu sa standardom Unicode. Podrazumevano i .l aanje poreenja bez uzimanja u obzir veliine slova jeste ... ~>orede samo znakovi iz skupa US-ASCII. , . U ovom reimu se u ponaanju i?\:,: priznaje samo \n A i $ kao graninik reda

P attern.U N IC O D E CASE (?u)

Pattern.UNIX_LINES (?d)

O d ovih indikatora naroito su korisni Pattern.CASE_IK TILINE i Pattern.COMMENTS (koji poveava jasnou i/ili Im ajte u vidu da se ponaanje veine indikatora m oe postu gradam a, napisanih ispod indikatora u tabeli, u regularan hoete da aktivirate taj reim. Uticaj navedenih i drugih indikatora m oete da kom bu,
//: znakovninizovi/O ln d ik ato rim a.java import j a v a . u t i l .re g e x .* ; p u b lic c la s s O ln d ikatorima { p u b lic s t a t i c void m a in (S trin g [] args) { Pa tte rn p = P a tte rn . compi 1e ( " / \ ja v a ", P a tte r n . CASE _ INSENSIT IVE | Pa tte rn .M U L T IL IN E ). Matcher m = p.m atcher( "ja v a ima re g iz\n ja va ima reg iz\n" + "JAVA ima p r ili n o dobre regularne izraze\n" "U je z ik u Ja va postoje reg u larn i i z r a z i " ) ; whi1e (m .fi n d ( ) ) System .out. p ri n t l n(m.group( ) ) ;

IIV E, Pattem.M UL
sava dokumentaciju). L-tanjem znakova u za^pred mesta na kojem peracijom OR (I):

}
) /* Is p is : ja va Java JAVA

* ///:-

422

Misliti na Javi

Napravljen je uzorak koji se podudara s redovim a koji poinju reima java, Java, JAVA itd. Podudaranje se ispituje za svaki red vierednog skupa (podudaranje delova koji poinju na poetku niza znakova i slede iza graninika svakog reda u nizu znakova). O bratite panju na to da m etoda g ro u p ( ) daje sam o deo koji se podudario.

Metoda splitf)
M etoda s p lit( ) cepa ulazni znakovni niz na niz String objekata, razgranienih datim regularnim izrazom.
S t r in g [] split(CharSequence ulaz) S t r in g [] split(CharSequence u la z, in t n ajviePod nizova)

Ovo je podesno za cepanje ulaznog teksta n a delove koji im aju zajedniki graninik:
/ / : z n ak o vn in iz o vi/Prim erZ aS p lit.java import ja v a .u t il.r e g e x .* ; import j a v a . u t i l .* ; import s t a t ic n e t.m in d v ie w .u til. P r i n t . * ; p ub lic c la s s PrimerZaSpl i t { p u b lic s t a t ic void m a in (S trin g [] args) { S trin g ulaz = "T h is! lunusual use! !o f exclam ation! Ip o in ts '1; pri n t(A rra y s . to S tr i ng( P a tte rn .c o m p ile ("! ! " ) . s p l i t ( u l a z ) ) ) ; // Uradi samo prva t r i : p rin t(A r ra y s .to S trin g ( Pattern.com pi1e ( " ! ! " ) . s p l i t ( u l a z , 3 ) ) ) ;

}
} /* Is p is : [T h is, unusual use, of exclam ation, points] [T h is, unusual use, of e x clam atio n !Ip o in ts]

* ///:D rugi oblik m etode s p lit( ) ograniava broj podnizova u rezultatu.

Veba 14: (1) Ponovno napiite program Prim erZaSplit uz korienje m etode S tring.split().

Operacije zamene
Regularni izrazi su posebno podesni za zam enu teksta. Na raspolaganju su ove metode:

replaceFirst(String zamena) zam enjuje prvi p odudarni deo ulaznog znakovnog niza zamenom. replaceAll(String zamena) zam enjuje svaki p odudarni deo ulaznog znakovnog niza zamenom.

Poglavlje 13: Znakovni nizovi

423

appendReplacement(StringBuffer sbaf, String zamena) u baferu sbaf obavlja zamene korak po korak, umesto da zameni samo prvi podudarni deo ili sve njih, kao to rade

replaceFirst( ) odnosno replaceAll( ). Ova m etoda je veoma vana, poto omoguava pozivanje m etoda i obavljanje druge obrade potrebne za proizvodnju znakovnog niza zamena (replaceFirst( ) i replaceAll( ) m ogu da um etnu samo nepromenljive znakovne nizove).
Pomou ove m etode moete program ski da ralanite grupe i napravite m one zamene.

appendTail(StringBuffer sbaf, String zamena) poziva se nakon jednog ili vie poziva m etode appendR ep!acem ent( ) da bi se kopirao ostatak ulaznog znakovnog niza.
Evo prim era u kojem je pokazana upotreba svih operacija zamene. Blok teksta u kom entaru na poetku program a biva izvaen i obraen regularnim izrazim a da bi se upotrebio kao ulaz u ostatku prim era:
//: znakovninizovi/Zam ene.java import ja v a .u t il,r e g e x .* ; import n e t.m in d v ie w .u til.* ; import s t a t ic n e t.m in d v ie w .u til. P r i n t . * ; / * ! H ere's a block of te x t to use as input to the re g u la r expression matcher. Note th a t w e 'll f i r s t e x tra c t the block o f tex t by looking fo r the sp ecial d e lim ite rs , then process the extracted block. !*/ p u b lic c la s s Zamene { p ublic s t a t ic void m a in (S trin g [] args) throws Exception { S trin g s = T e x tF ile .re a d (''Z a m e n e .ja v a "); // Pronai g o rn ji blok te k sta n aro ito pretvoren u komentar: Matcher ulaztlM = Pattern.com pi1e ( " / \ \ * ! ( . * ) !\ \ * / ", Pattern.DOTALL) .m a tc h e r(s ); if(u la z U M .fin d ()) s = u lazU M .g ro u p (l); // Ono to hvataju zagrade // Dva i v i e razmaka zameni jednim: s = s .re p la c e A l1 (" { 2 , } " , " " ) ; // Jedan i l i v i e razmaka na poetku svakog reda // zameni s nula razmaka. Mora se u k lj u i t i reim MULTILINE: s = s .re p la c e A ll ("(? m )/ ' +", " " ) ; p r in t(s ); s = s .r e p la c e F ir s t ( " [a e io u ] " , " ( V0WEL1)" ) ; S trin g B u ffe r sbaf = new S t r in g B u f f e r ( ) ; Pattern p = P a tte r n .c o m p ile ("[a e io u ]" ) ; Matcher m = p .m a tc h e r(s ); // Obradi podatke metode f in d () dok // o b avlja zamene: w h ile (m .fin d ()) m.appendReplacement (s b a f, m.groupO . toUpperC ase()) ; // Umetni ostatak te k s ta : m.appendTai1( s b a f ) ; p r in t ( s b a f ) ;

424

Misliti na Javi

} /* Is p is : H ere's a block of te x t to use as input to the reg u lar expression matcher. Note th a t w e 'll f i r s t ex tract the block of te x t by looking fo r the sp ecial d e lim ite rs , then process the ex tracted block. H (VO W ELl)rE's A blOck Of tE x t tO UsE As InpUt tO thE rEgUlAr ExprEssIOn mAtchEr. NOtE thAt w E 'll f l r s t ExtrA ct thE blOck Of tE x t by lOOklng fOr thE spEcIAl d E lIm ltE rs , thEn prOcEss thE ExtrActEd blOck.

* ///:D atoteka se otvara i uitava pom ou klase TextFile iz biblioteke net.mindview.util (njen kod e biti prikazan u poglavlju Javin ulazno-izlazni sistem). Statina m etoda re a d ( ) ita celu datoteku i vraa je kao String. ulazUM se pravi tako da se podudara s celim tekstom (obratite panju n a zagrade za grupisanje) izm eu 1*1 i !*/. Zatim se dva i vie razm aka svode na jedan razm ak i uklanjaju se svi razm aci s poetka svakog reda (m ora se ukljuiti reim m ultiline da bi se to uradilo u svim redovim a, a ne sam o na poetku ulaza). Te dve zam ene se obavljaju ekvivalentom (u ovom sluaju, podesnijim ) m etode replaceA ll( ) koji je deo klase String. Poto se svaka zam ena u p rogram u upotrebljava sam o jedanput, to ne kota nita vie nego da se obavljalo p reth o d n o prevoenje u objekat tipa Pattern. replaceFirst( ) obavlja samo prvu zam enu koju pronae. Sem toga, zamenski znakovni nizovi u m etodam a replaceFirst( ) i replaceAU() mogu biti sam o literali, pa ako svaku zam enu hoete jo da obradite, od tih m etoda neete imati koristi. U tom sluaju upotrebite m etodu appendR eplacem ent( ) koja omoguava da tokom obavljanja postupka zamene upiete proizvoljnu koliinu koda. U prethodnom prim eru, bira se i obrauje jedna grupa g ro u p ( ) - u ovom sluaju, regularni izraz pronalazi samoglasnik (engl. vowel) koji biva napisan velikim slovom - dok se pravi rezultujui sbaf. O bino se korak po korak obave sve zam ene i zatim pozove appendTail( ), ali ako hoete da sim ulirate replaceFirst( ) (ili replace n), zam enu obavite sam o jednom i zatim pozovete appendTail( ) da ostatak sprem i u sbaf. M etoda appendR eplacem ent( ) om oguava i da uhvaenu grupu naznaim o direktno u zam enskom znakovnom nizu izrazom ,,$g, gde g oznaava broj grupe. M eutim , to je podesno za jednostavniju ob radu i u p rethodnom program u ne bi dalo eljeni rezultat.

Metoda reset()
Postojei objekat tipa Matcher m etodam a re se t( ) moete prim eniti na nov niz znakova:
//: znak ovn in izovi/R esetovan je.java import ja v a .u t il,r e g e x .* ; p u b lic c la s s Resetovanje { p u b lic s t a t ic void m a in (S trin g [] args) throws Exception { Matcher m = Pattern .com pi1e ( " [fr b ] [a iu ] [g x ] ) ,m a tc h e r("fix the rug w ith b ag s");

Poglav[je 13: Znakovni nizovi

425

w h ile (m .fin d ()) System .o u t.p rint(m .g rou p () + " " ) ; S y s te m .o u t .p r in tln (); m .r e s e t("fix the rig w ith rags1 ') ; w h ile (m .fin d ()) System .o u t.p rin t(m .g rou p () + " " ) ;

}
} /* Is p is : f ix rug bag f ix rig rag

* ///:-

re se t( ) bez argum enata prim enjuje objekat tipa M atcher na poetak tekueg niza.

Regularni izrazi i Javin ulazno-izlazni sistem


U veini dosadanjih prim era regularne izraze sm o prim enjivali na statine znakovne nizove. U narednom prim eru prikazan je jedan nain prim ene regularnih izraza za pronalaenje delova datoteke koji se s njim podudaraju. Napravljen na osnovu Unbcove alatke grep, JGrep.java prim a dva argum enta: ime datoteke i regularan izraz koji treba pronai. Iz rezultata se vidi broj reda gde je pronaena podudarnost i poloaj(i) podudarajuih delova reda.
//: zn ak o vn in izovi/JG rep .java // Veoma jednostavna v e r z ija programa "g rep ". // {Args: JG re p .ja v a " \\b[Ssct]\\w + "} import ja v a .u t il,r e g e x .* ; import n e t.m in d v ie w .u til.* ; p u b lic c la s s JGrep { p u b lic s t a t ic void m a in (S trin g [] args) throws Exception { i f (a rg s . 1ength < 2) { S y ste m .o u t.p rin tln ("U p o treb a : ja va JGrep regiz d a to te k e "); S y s te m .e x it(0 );

}
Pa ttern p = P a tte r n .c o m p ile (a r g s [l]); // It e r a c ij a kroz redove ulazne datoteke: in t indeks = 0; Matcher m = p .m a tc h e r (""); fo r (S t r in g red : new T e x tF ile ( a r g s [ 0 ] ) ) { m .re s e t(re d ); whi1e (m .fi nd( ) ) System .out.println(indeks+ + + " : " + m.groupO + " : " + m .s t a r t ( ) ) ;

} }
} /* Is p is : (prim er) 0: s trin g s : 4 1: sim ple: 10 2: the: 28

426

Misliti na Javi

3: 4: 5: 6: 7: 8: 9: 10: jll: 12: 13: 14: 15: 16:

S s ct: 26 c la s s : 7 s ta tic : 9 S trin g : 26 throws: 41 System: 6 System: 6 compile: 24 through: 15 the: 23 the: 36 S trin g : 8 System: 8 s t a r t : 31

* ///:D atoteka se otvara kao objekat tipa net.muidview.util.TextFile (to e biti objanjeno u poglavlju Javin ulazno-izlazni sistem), koji redove datoteke uitava u ArrayList. To znai da iteraciju kroz redove TextFile objekta m oem o obaviti foreach sintaksom. Iako sm o mogli da pravim o nov objekat tipa M atcher u n u tar petlje for, neto je bolje napraviti prazan objekat tipa M atcher izvan petlje i dodeljivati m u red po red ulaza metodom re se t( ). Rezultat toga se pretrauje m etodom fin d ( ). A rgum enti ispitivanja (trei red program a) znae da se datoteka JGrep.java otvara za uitavanje kao ulaz i da se u njoj trae rei koje poinju sa [Ssct], O regularnim izrazima m oete nauiti m nogo vie iz knjige Mastering Regular Expressions, 2nd Edition, koju je napisao Jeffrey E. F. Friedl ( 0 ReilIy, 2002). I na Internetu ima m nogo uvoda u regularne izraze, a i u dokum entaciji jezika Perl i Python esto se mogu nai korisni podaci.

Veba 15: (5) Prepravite JGrep.java tako da prim a indikatore kao argum ente (npr. Pattern.CASEJNSENSITIVE, Pattern.MULTILINE). Veba 16: (5) Prepravite JGrep.java tako da kao argum ent prim a ime datoteke ili direktorijum a (ako se zada direktorijum , pretraivanje treba da obuhvati sve datoteke u njem u). Uputstvo: generiite listu im ena datoteka naredbom :
F i l e [ ] datoteke = new F i l e ( " . ") .1 i s t F i l e s ( ) ;

Veba 17: (8) Napiite program koji uitava datoteku Javinog izvornog koda (ije ime
zadajete na kom andnoj liniji) i prikazuje sve njene kom entare.

Veba 18: (8) Napiite program koji uitava datoteku Javinog izvornog koda (ije ime
zadajete na kom andnoj liniji) i prikazuje sve doslovno (Iiteralno) navedene znakovne nizove u kodu.

Veba 19: (8) Na osnovu preth od ne dve vebe, napiite program koji pretrauje Javin izvorni kod i ispisuje im ena svih klasa koje se spom inju u tom program u.

Poglavlje 13: Znakovni nizovi

427

Leksiko analiziranje ulaza


D osad je uitavanje podataka iz datoteke itljive Ijudima ili sa standardnog ulaza bilo relativno teko. Uobiajeno reenje bilo je da se uita red teksta, ralani na lekseme (engl. tokens) i potom jeziki analizira (engl .parse) raznim m etodam a za tipove Integer, D o u b le itd.:
//: znakovninizovi/ProstoCitanje.java import ja v a .io .* ; public class ProstoCitanje { public s ta tic BufferedReader ulaz = new BufferedReader( new StringReader("Hajduk Veljko\n22 1.61803")); public s ta tic void m ain(String[] args) ( try { System.out.println("Kako se zove?"); String ime = u la z .re a d Lin e (); System .out.println(im e); System .out.println("Koliko godina ima? Koji je tvoj omiljeni broj tip a double?"); System .out.println("(ulaz: <starost> <double>)"); String brojevi = ulaz. read Lin e(); System .o u t.p rin tln (b ro jevi);
String[] numNiz = brojevi.split (" "); int starost = Integer.parselnt(numNiz[0]); double omiljeni = Oouble.parseDouble(numNiz[l]); System.out.format("Zdravo %s.\n", ime);

System.out.format("Za 5 godina imae %d.\n", starost + 5); System.out.format("Moj omiljeni broj tip a double je % f . , omi1jeni / 2); } catch (IOExcepti on e) { System .err.println("Izuzetak ulazno-izlaznog sistem a");

} /* Is p is : Kako se zove? Hajduk Veljko Koliko godina ima? Koji je tvoj omiljeni broj tipa double? (ulaz: <starost> <double>) 22 1.61803 Zdravo Hajduk Veljko. Za 5 godina imae 27. Moj omiljeni broj tipa double je 0.809015.

* ///.U polju ulaz koriste se klase iz paketa java.io koji nee biti zvanino predstavljen do poglavlja Javin ulazno-izlazni sistem. Objekat tipa StringR eader pretvara znakovni niz u tok podataka (engl. stream) koji se moe itati, i taj objekat se upotrebljava za pravljenje

428

Misliti na Javi

objekta tipa BufferedReader, poto BufferedReader im a m etodu readL ine( ). Zato se objekat ulaz moe itati red po red, kao da je standardni ulaz s konzole. readL ine( ) pribavlja String za svaki red ulaza. Jednostavno je ako svaki red podataka hoete da pretvorite u jedan ulaz, ali ukoliko su dve ulazne vrednosti u istom redu, stvari se kom plikuju da bism o svaki ulaz mogli da analiziram o zasebno, red se m o ra podeliti na dva dela. Ovde se podela obavlja kada se pravi num N iz, ali poto je m etoda sp lit( ) uvedena tek u J2SE1.4, pre se to m oralo raditi drugaije. Klasa Scanner se koristi od Jave SE5. O na obavlja glavni deo posla oko leksikog analiziranja ulaza:
//: znakovninizovi/BoljeCitanje.java import java.util public class BoljeCitanje { public static void main(String[] args) { Scanner stdulaz = new Scanner(SimpleRead.ulaz); System.out.println("Kako se zove?1 1 ); String ime = stdulaz.nextLine(); System.out.println(ime);
S y s te m .o u t.p rin tln ("K o lik o godina ima? K oji je tv o j o m iljen i broj tip a d o u b le ?"); S y s te m .o u t.p rin tln ("(u la z : <starost> <double>)"); in t s ta ro s t = s t d u la z .n e x t ln t (); double om iljen i = std u la z .n e x tD o u b le (); S y s te m .o u t.p rin tln (s ta ro s t); Sy ste m .o u t.p rin tln (o m il j e n i ) ; System .out.form at("Zdravo % s .\ n ", im e); System .out.form at("Za 5 godina imae % d.\n ", sta ro s t + 5 ); System .out.form at("M oj o m iljen i broj tip a double je % f . " , omi 1je n i / 2 );

}
} /* Is p is : Kako se zove? Hajduk V eljko Koliko godina ima? Koji je tv o j o m iljen i broj tip a double? (u la z : <starost> <double>)

22
1.61803 Zdravo Hajduk V eljko. Za 5 godina imae 27. Moj o m iljen i broj tip a double j e 0.809015.

* ///:K onstruktor klase Scanner prim a gotovo sve vrste ulaznih objekata - m edu njim a su objekat tipa File (koji e takoe biti objanjen u poglavlju Javin ulazno-izlazni sistem ), objekat tipa InputStream , objekat tipa String ili u ovom sluaju objekat tipa Readable,

Poglavlje 13: Znakovni nizovi

429

to je interfejs koji je Java SE5 uvela za opisivanje neega to im a m etodu re a d ( ). U tu kategoriju spada BufferedReader iz preth od no g prim era. U klasi Scanner se m etode za uitavanje ulaza, podelu na lekseme i jeziku analizu kriju u raznim vrstam a m etoda ,,next. O bina m etoda n e x t( ) vraa sledeu leksem u znakovnog niza, a postoje i ,,next m etode za sve proste tipove (sem char), kao i za BigDecim al i Biglnteger. Sve ,,next m etode blokiraju, to znai da vraaju rezultat tek kada celokupna leksema postane dostupna na ulazu. Postoje i odgovarajue m etode ,,hasNext, koje vraaju true ukoliko je sledea ulazna leksema ispravnog tipa. Zanimljiva razlika izm edu dva preth od na prim era: u p rogram u BoljeCitanje.java ne postoji blok try za izuzetke tipa IOException. Jedna od pretpostavki klase Scanner jeste da IOException signalizira kraj ulaza, pa te izuzetke Scanner guta. M edutim , najnoviji izuzetak je dostupan preko m etode ioE xception( ), pa m oete da ga ispitate ako treba.

Veba 18: (2) N apravite klasu koja sadri polja tipova int, long, float, double i String. N apravite konstruktor za tu klasu; on treba da p rim a jedan argum ent tipa String, da analizira taj znakovni niz i da ga ralani na spom enuta polja. D odajte m etodu to S trin g ( ) i
pokaite da klasa ispravno radi.

Graninici klase Scanner


Klasa Scanner podrazum evano cepa ulazne lekseme na m estim a razmaka, ali m oete da zadate i sopstveni uzorak graninika u obliku regularnog izraza:
//: znakovninizovi/Granicni kKlaseScan n er.java import j a v a . u t i 1 p ub lic c la s s GranicnikKlaseScanner { p u b lic s t a t ic void m a in (S trin g [] args) { Scanner skener = new Scanner("12, 42, 78, 99, 4 2 "); s k e n e r.u s e D e lim ite r ("\ \ s * ,\ \ s * "); w h ile (s k e n e r.h a s N e x tIn t()) S y s te m .o u t .p r in tln (s k e n e r .n e x t In t());

}
} /* Is p is :

12
42 78 99 42

* ///:U ovom prim eru, kao graninici prilikom itanja datog znakovnog niza upotrebljeni (oko kojih moe biti proizvoljna koliina belina). Ista tehnika se m oe upotrebiti za uitavanje datoteka u kojim a su podaci razgranieni zarezima. Pored m etode u se D e lim ite r() za zadavanje uzorka graninika, postoji i d e lim ite r() - on vraa tekui objekat tipa P attern koji se upotrebljava kao graninik.
sli zarezi

430

Misliti na Javi

Leksika analiza pomou regularnih izraza


Pored traenja unapred definisanih prostih tipova, m oete traiti i sopstvene (korisniki definisane) uzorke, to je podesno za analizu sloenijih podataka. U narednom prim eru, traim o pretee podatke u zapisniku kakav vodi, recim o, zatitna barijera (engl. firewall):
/ / : z n a k o v n in iz o v i/ A n a liz a to rP re tn ji.ja v a import ja v a .u t il.r e g e x .* ; import j a v a . u t i l .* ; p ub lic c la s s A n a liz a to rP r e tn ji { s t a t ic S trin g p reteciPodaci =

"58.27.82.161@02/10/2005\n" + "204.45.234.40@02/ll/2005\n" + "58.27.82.161@02/ll/2005\n" + "58.27.82.161@02/12/2005\n" + "58.27.82.161@02/12/2005\n" +


"[S le d e i od eljak zapisnika s drugaijim formatom p o dataka]"; public s t a t ic void m a in (S trin g [] args) { Scanner skener = new S c a n n e r(p re te ciP o d a ci); S trin g uzorak = "(\\d + [.]\\d + [.]\\d + [.]\\d + )@ " +

" (\\d{2 }/\\d{2 }/\\d{4 })";


w hile(skener.hasN ex t(u zorak)) { sk e n e r.n ex t(u zo rak ); MatchResult pronasao = sk en er.m a tch (); S trin g ip = p ro n a sao .g ro u p (l); S trin g datum = pronasao.group(2); Sy stem .o u t.fo rm a t("Pretn ja dana %s od %s\n", datum .ip);

1
)
} /* Is p is : Pre tn ja dana Pre tn ja dana Pre tn ja dana Pre tn ja dana Pre tn ja dana 02/10/2005 02/11/2005 02/11/2005 02/12/2005 02/12/2005 od od od od od 58.27.82.161 204.45.234.40 58.27.82.161 58.27.82.161 58.27.82.161

* ///:Kada m etodu n e x t( ) upotrebljavate s konkretnim uzorkom , ona ga trai u sledeoj ulaznoj leksemi. Rezultat pravi m etoda m a tc h ( ), i kao to vidite u gornjem prim eru, ona radi ba kao regularni izrazi koje ste ve upoznali. Kada leksiku analizu obavljate pom ou regularnih izraza, vodite rauna o tom e da se uzorak trai samo u sledeoj ulaznoj leksemi, pa ako uzorak sadri i graninik, nee nikada biti pronaen.

Poglavlje 13: Znakovni nizovi

431

Klasa StringTokenizer
Pre regularnih izraza (koji su uvedeni u J2SE1.4) ili klase Scanner (koja se koristi od Jave SE5), znakovni niz se cepao na lekseme p o m o u klase StringTokenizer. Ali sada se to m nogo lake i saetije obavlja pom ou regularnog izraza ili klase Scanner. Pogledajte jednostavno poreenje ldase StringTokenizer i druge dve tehnike:
//: znakovninizovi/Um estoStringTokenizera.java import j a v a . u t i l .* ; p u b lic c la s s UmestoStringTokenizera { p ub lic s t a t ic void m a in (S trin g [] args) { S trin g input = "But I'm not dead y e t ! I fe e l happy!"; String Tokenizer stoke = new S trin g T o k e n iz e r(u la z ); w hile(stoke.hasM oreElem ents()) System .o u t.p rin t(sto k e.n ex tT o k en () + " " ) ; S y s te m .o u t .p r in tln (); S y s te m .o u t .p r in tln (A r r a y s .to S tr in g (u la z .s p lit (" " ) ) ) ; Scanner skener = new Sc a n n e r(u la z ); w h ile (s k e n e r.h asN e x t()) S y ste m .o u t.p rin t(s k e n e r.n e x t() + " '') ;

}
} /* Is p is : But I'm not dead y e t ! I fe e l happy! [B u t, I'm , not, dead, y e t ! , I , f e e l , happy!] But I'm not dead y e t ! I fe e l happy!

* ///:Pom ou regularnih izraza ili objekata tipa Scanner, znakovni niz m oete podeliti na delove i korienjem sloenijih uzoraka - to bi bilo teko pom ou klase StringTokenizer. Izgleda da m oem o bezbedno rei kako je StringTokenizer zastarela.

Saetak
Ranije je Javina podrka za obradu znakovnih nizova bila rudim entarna, ali u novijim izdanjim a jezika postoji m nogo sofisticiranija podrka usvojena iz drugih jezika. Sada je podrka za znakovne nizove prilino potpuna, iako ponekad m orate paziti na efikasnost, recimo prilikom upotrebe klase StringBuilder.
R eenja o d a b r a n ih vebi d a ta su u e le k tro n sk o m d o k u m e n tu Thinking in Java Annotated Solution

Guide , koji se m o e k u p iti na lo k a ji www.BruceEckel.com.

Podaci o tipu
Prepoznavanje tipa u vreme izvravanja (engl. ru n -tim e type identification, RTTI ) omoguuje ustanovljavanje i upotrebu tipa objekta dok se progratn izvrava. RTTI VAS OSLOBAA O G RA N IEN JA D A T IPO V E M OR A TE ZN A T I VE U VREM E PREV O EN JA , te stoga om oguuje pisanje veom a m onih program a. Potreba za prepoznavanjem tipa u vrem e izvravanja pokree niz zanim ljivih, a esto i sloenih problem a u objektno orijentisanom projektovanju, a otvara i osnovno pitanje: kako bi trebalo da izgleda struktura program a. Ovo poglavlje obrauje m etode za otkrivanje inform acija o objektim a i klasama u vrem e izvravanja. To se obavlja na dva naina: tradicionalnim otkrivanjem tipa tokom izvravanja, kada se pretpostavlja da su svi tipovi dostupni ve tokom prevoenja koda, i refleksivnim m ehanizm om , koji om oguuje otkrivanje inform acija o klasama iskljuivo tokom izvravanja.

Potreba za prepoznavanjem tipa tokom izvravanja


Posm atrajm o sada ve poznat prim er hijerarhije klasa u kojoj se koristi polimorfizam. Generiki tip je osnovna klasa Oblik, a specifini izvedeni tipovi su Krug, Kvadrat i Trougao:

Ovo je tipian dijagram hijerarhije klasa u kom e se osnovna klasa nalazi na vrhu, a izvedene klase se ravaju nie. Uobiajeni cilj u objektno orijentisanom program iranju jeste da kod radi s referencama na osnovni tip (u ovom sluaju Oblik), jer ako kasnije odluite da proirite program dodavanjem nove klase (kao to je Romboid, izveden iz klase Oblik), to nee uticati na najvei deo koda. U ovom prim eru, m etoda crtaj() u interfejsu Oblik dinamiki je povezana, pa program er klijent treba da poziva m etodu crtaj() preko generike reference na Oblik. Metoda crtaj() se redefinie u svim izvedenim klasama, a poto je re o dinam iki povezanoj m etodi, ona e se ponaati na odgovarajui nain ak i ako se poziva preko generike reference na objekat klase Oblik. To je polim orfizam. Stoga obino pravite odreeni objekat (Krug, Kvadrat ili Trougao), pretvarate ga u optiji tip O blik (zanem arujete specifini tip objekta) i koristite tu referencu tipa Oblik u ostatku program a.

Poglavlje 14: Podaci o tipu

433

H ijerarhija Oblika se m oe program irati ovako:


//: p o d a c io tip u / O b lic i. ja va import j a v a . u t i l .* ; a b s tra c t c la s s O blik { void c r t a j ( ) { S y s te m .o u t.p rin tln (th is + . c r t a j O " ) ; a b s tra c t p ub lic S trin g t o S tr in g O ;

}
c la s s Krug extends O blik { p u b lic S trin g to S trin g O { return "Krug'1; }

}
c la s s Kvadrat extends O blik { p u b lic S trin g t o S t r in g () { return "K v a d ra t"; }

}
c la s s Trougao extends O blik { p u b lic S trin g to S trin g O { return "Trougao"; }

}
public c la s s Obli c i { p u b lic s t a t ic void m a in (S trin g [] args) { List<Oblik> lis t a O b lik a = A rr a y s .a s L is t ( new K ru g (), new K v a d ra t(), new Trougao()

);
f o r (0 b lik o b lik : lis t a O b lik a ) obl i k . c r t a j ( ) ;

}
} /* Is p is : K r u g .c r t a j() K v a d r a t.c r ta j () T ro u g ao .crtaj ()

* ///:O snovna klasa sadri m etodu crtaj() koja indirektno koristi m etodu toStringO za tam panje identifikatora klase pom ou prosleivanja reference this m etodi System .out.println() (obratite panju na to da je m etoda toStringO deklarisana kao apstraktna, da bi se njeni naslednici prim orali da je redefiniu i da b ise spreilo pravljenje objekta tipa Oblik). Ukoliko se u izrazu za nadovezivanje znakovnih nizova (u kom e postoje znak + i objekti tipa String) pojavi neki objekat, autom atski se poziva m etoda toStringO prosleenog objekta da bi predstavila njegov sadraj u objektu tipa String. Svaka izvedena klasa redefinie m etodu toStringO klase Object, pa crtaj() na kraju u svakom pojedinanom sluaju (polim orfno) tam pa neto razliito. U ovom p rim eru, do svodenja navie dolazi kada se oblik smesti u List<Oblik>. Prilikom svoenja navie gubi se podatak o tom e da su objekti specifini tipovi klase Oblik. to se tie niza, svi njegovi elem enti su tipa Oblik.

434

Misliti na Javi

U trenutku kada se elem ent ita iz niza, kontejner - za koji su svi njegovi elementi tipa

Object - autom atski pretvara svoj rezultat ponovo u Oblik. To je najosnovniji oblik prepoznavanja tipa u vreme izvravanja, poto se u Javi u vrem e izvravanja proverava pravilnost svih konverzija tipova. Upravo to i jeste smisao prepoznavanja tipa u vreme izvravanja: tokom izvravanja, prepoznaje se tip objekta. U ovom sluaju, konverzija je sam o delimina: Object se konvertuje u tip Oblik, a ne do kraja u tipove Krug, Kvadrat ili Trougao. U tom trenutku znate samo da je List<ObIik> p u n objekata tipa Oblik. Tokom prevoenja to osigurava kontejner i Javin sistem generikih tipova, ali tokom izvravanja obezbeuje ih konverzija tipa. Sada na scenu stupa polim orfizam , pa se kod koji se izvrava za Oblik bira prem a tom e da li se radi o referenci na Krug, Kvadrat ili Trougao. Tako bi uglavnom trebalo da bude, jer elite da najvei deo program a zna to m anje o specifinim tipovim a objekata, odnosno da radi sa optom predstavom o porodici objekata (u ovom sluaju, Oblik). Takav kod e se lake pisati, itati i odravati, a projekti e se lake prim enjivati, razum eti i menjati. Zbog toga je polim orfizam jedan od optih principa u objektno orijentisanom program iranju. M eutim , ta e se desiti ako p ri p rogram iranju im ate poseban problem koji se najlake reava ako znate taan tip generike reference? Na prim er, pretpostavim o da elite om oguiti korisnicima da posebnom bojom istaknu sve figure odreenog oblika? Tako bi mogli da pron au sve trouglove na ekranu, jer su posebno obojeni. Ili, recimo, m etoda je dobila zadatak da rotira sve oblike navedene u nekoj listi, ali krugove nem a smisla rotirati, pa biste njih hteli da preskoite. To om oguuje prepoznavanje tipa u vreme izvravanja: moete da zatraite od reference na Oblik da vrati taan tip na koji ukazuje i tako izabere i izdvoji specijalne sluajeve.

Objekat tipa Class


Da biste razum eli kako se u Javi odvija prepoznavanje tipa tokom izvravanja, prvo m orate da shvatite kako se u vrem e izvravanja predstavljaju inform acije o tipu. To se postie pom ou specijalne vrste objekta koji pripada klasi Class, a sadri inform acije o klasi. Zapravo, za pravljenje svih objekata ,,obinih klasa koristi se upravo objekat klase Class. Prepoznavanje tipa u vreme izvravanja, Java obavlja pom ou Class objekta, ak i kada radite neto p oput svoenja navie. Klasa Class obezbeuje jo naina za upotrebljavanje prepoznavanja tipa u vreme izvravanja. Za svaku klasu koja je deo program a postoji po jedan objekat tipa Class. Kad god napiete i prevedete kod za neku novu klasu, pravi se i jedan objekat tipa Class (i uva u datoteci .class istovetnog imena). Za pravljenje objekta te klase, Javina virtuelna maina (JVM) koja izvrava program upotrebljava podsistem nazvan uitava klasa (engl. class loader). Taj podsistem moe da obuhvati ceo lanac uitavaa klasa, ali postoji sam o jedan primordijalni uitava klasa koji je deo realizacije JVM-a. Prim ordijalni uitava klasa, obino s lokalnog diska, uitava takozvane klase od poverenja, m eu kojim a su i klase Java API-ja. U lancu najee nije po trebn o im ati dodatne uitavae klasa, ali ako imate posebne potrebe (kao to su uitavanje klasa na poseban nain za podrku aplikacija Web servera ili uitavanje klasa preko mree), onda postoji nain da te klase angaujete.

Poglav < 14: Podaci o tipu

435

U JVM-u se sve klase uitavaju dinamid, prilikom prve upotre v . To se eava kada program napravi prvu referencu nekog statinog lana te klase. Ispostavlja se da ie i konstruktor statina metoda klase, iako se za njega ne pie rezervisana re static. Zato se i pravljr > y; novog objekta te klase operatorom nevv smatra za referenciranje statinog ana te klase. Dakle, program napisan na Javi ne uitava se po tp u n o pro ncgo to pone izvravanje, nego se delovi program a uitavaju po potrebi. Po tom e se java razlikuje od m nogih tradicionalnih jezika. D inam iko uitavanje om oguuje ponaanje koje je teko ili nem ogue postii u jezicim a sa statikim uitavanjem , kao to je C-H-, Uitava klasa prvo proverava da li je uitan objekat tipa Class za taj tip. Ako nije, podrazum evani uitava ldasa uitava ga pronalaenjem datoteke .c lass s tim im enom . (Ui uitava klasa, instaliran kao softverski dodatak, trai odgovarajue bajtkodove u bazi podataka, na prim er.) Tokom uitavanja bajtkodova klase, JVv.' noverava da li su bajtovi oteeni i da li sainjavaju lo kod. (To je jedan od naina nak< ' : r;> <i postiebezbedan rad.) Kada se objekat Class za taj tip uita u m em oriju, k o r i s t i ; iravljenje svih objekata tog tipa. Evo jednog program a koji e vas u to ubediti:
//: p o d a cio tip u /P ro d a v n ic a S la tk isa .ja va // Is p it iv a n je naina rada u ita va a k lase. c la s s Bombona { s t a t ic { p rin t("U c ita v a in Bombonu");

}
class Zvaka { static { printC'Ucitavam Zvaku'1); }

}
class Kolacic { static { print("Ucitavam Kolacic"); }

}
public class ProdavnicaSlatkisa { public static void ma in(String[ ] args) print("u metodi main"); new Bombona(); print("Posle pravljenja Bombone"); try { Cl as s. fo rN am e( "Z va ka" ); } catcb(ClassNotFoundException e) { pr int("Nisam pronaao klasu Zvaka"); {

}
print("Nakon C1as s. fo rN am e( \" Zv aka \" )"); new K o l a c i c ( ) ; print("Posle pravljenja Kolacica");

}
} /* Ispis: u metodi main Ucitavam Bombonu Posle pravljenja Bombone

436

Misliti na Javi

Ucitavam Zvaku Nakon Class.forN am e("Zvaka") Ucitavam K o lacic Posle p ra v lje n ja Kolacica

* ///:Klase Bombona, Zvaka i Kolacic sadre blok static koji se izvrava p ri njihovom prvom uitavanju. Informacije se ispisuju da biste znali kada se uitava odredena klasa. O bjekti se u m etodi main() prave izm edu naredaba za tam panje, da bi se lake odredio tren u tak uitavanja. Vidite da se svaki objekat klase Class uitava tek kada zatreba, a inicijalizacija bloka static obavlja se po uitavanju klase. Posebno je zanimljiv red:
C la ss.fo rN am e ("Z vak a");

Svi objekti tipa Class pripadaju klasi Class. O bjekat tipa Class je isti kao i svi drugi objekti, pa m oete da dobijete referencu na njega i da radite s njom (to radi uitava klasa). Jedan od naina za dobijanje reference na objekat tipa Class jeste statina m etoda forName() iji argum ent tipa String sadri tekstualno im e (pazite na pravopis, m ala i velika slova!) odreene klase iju referencu hoete. Ova m etoda vraa referencu na objekat tipa Class, koja je ovde bila zanem arena; m etodu forName() pozivam o radi njenog sporednog dejstva, a to je uitavanje klase Zvaka ako nije ve uitana. U postu p k u uitavanja izvrava se statini blok klase Zvaka. U p rethodnom prim eru, ako m etoda Class.forN am e( ) zakae zato to ne moe da pronae klasu koju treba da uita, generisae izuzetak ClassNotFoundException. Ovde sam o prijavljujem o problem i idem o dalje, ali u sofisticiranijim program im a mogli biste pokuati da reite problem u bloku za o bradu izuzetaka. Kad god budete hteli da upotrebite podatke o tipu prepoznate u vreme izvravanja, najpre ete m orati da pribavite referencu odgovarajueg objekta Class. Jedan od podesnih naina za to prua m etoda Class.forN am e( ), zato to vam nije potreban objekat tog tipa da biste dobili referencu klase Class. M eutim , ukoliko ve im ate objekat tipa koji vas zanim a, referencu klase Class m oete pribaviti pozivanjem m etode getC lass( ) koja je deo korenske klase Object. Ona vraa referencu klase Class koja predstavlja stvarni tip objekta. Class ima m nogo zanimljivih m etoda; evo nekih od njih:
//: p o d a c io tip u / ig ra c k e / Is p itiv a n je lg ra c a k a .ja v a // T e s tira n je klase C lass. package p o d acio tip u .ig rack e; import s t a t ic n e t.m in d view .u ti1 .P r i n t .* ; in te rfa c e Im aB aterije {} in te rfa c e OtpornaNaVodu {} in te rfa c e Puca {} c la s s Igracka { // Sled ei podrazumevani konstruktor p re tv o r ite u komentar // da b is te v id e li izuzetak NoSuchMethodError od ( * !* )

Poglavlje 14: Podaci o tipu

437

Ig ra c k a () {} Ig ra c k a (in t i ) {}

}
c la s s Lepalgracka extends Igracka implements Im a B a te rije , OtpornaNaVodu, Puca { LepalgrackaO { s u p e r (l); }

}
pu b lic c lass Is p itiv a n je lg ra c a k a { s t a t ic void p rin tIn fo (C la s s cc) { p rin t("Im e klase: 1 1 + cc.getName() + " j e in t e r f e js ? [" + c c . is ln t e r f a c e ( ) + " ] " ) ; p r in t("P r o s to ime: " + cc.getSim pleN am eO ); print("Kanonsko ime : " + cc.getC anonicalN am eO );

}
pu b lic s t a t ic void m a in (S trin g [] args) C1ass c = n u l1; try { {

c = C la s s .fo rN a m e ("ty p e in fo .ig ra c k e .L e p a Ig ra c k a "); } catch(ClassNotFoundException e) { p rin t("N e mogu da pronaem klasu L e p a lg ra c k a "); S y s t e m .e x it (l);

}
p r in tln fo (c ); fo r(C la s s fe js : c .g e t In te r fa c e s ( ) ) p r in tln fo (fe js ); Class nad = c .g e tS u p e rc la s s ( ) ; Object obj = n u l1; tr y { // Zahteva podrazumevani konstruktor: obj = nad .new ln stance(); } c a tc h (In sta n tia tio n E x c e p tio n e) { p rin t("N e mogu da napravim p rim erak "); S y s t e m .e x it (l); } c a tc h (Ille g a lA c c e ss E x c e p tio n e) { p rin t("N e mogu da p ris tu p im "); S y s t e m .e x it (l);

}
p r in t In f o (o b j.g e t C la s s ()) ;

}
} /* Is p is : Ime k lase: typ e in fo .ig ra c k e .L e p a Ig ra c k a j e in t e r f e j s ? [ f a ls e ] Prosto ime: Lepalgracka Kanonsko ime : typ ein fo .ig ra c k e .L e p a lg ra c k a Ime k lase: ty p e in fo .ig ra c k e .Im a B a te rije je in t e r f e j s ? [tru e ] Prosto ime: Im aB aterije Kanonsko ime : ty p e in fo .ig ra c k e .Im a B a te rije Ime k lase: typeinfo.igracke.OtpornaNaVodu je in t e r f e j s ? [tru e ] Prosto ime: OtpornaNaVodu

4 38

Misliti na Javi

Kanonsko ime : typeinfo.igracke.OtpornaNaVodu Ime k lase: ty p e in fo .ig ra c k e .P u c a j e in t e r f e js ? [tru e ] Prosto ime: Puca Kanonsko ime : typ e in fo .ig ra c k e .P u c a Ime k lase: ty p e in fo .ig ra c k e .T o y j e in t e r f e js ? [fa ls e ] Prosto ime: Toy Kanonsko ime : ty p e in fo .ig ra c k e .T o y

* ///:-

Lepalgracka nasleuje klasu Igracka i realizuje interfejse ImaBaterije, OtpornaN aVodu i Puca. U m etodi m a in ( ), pravi se referenca klase Class i inicijalizuje na objekat Lepalgracka tipa Class pom ou m etode forN am e( ) u odgovarajuem bloku try. Vodite
rauna o tom e da m orate navesti p un o im e (ukljuujujui i im e paketa) u znakovnom nizu koji prosleujete m etodi forN am e( ). M etoda p rin tIn fo ( ) upotrebljava getN am e( ) za dobijanje punog im ena klase, a getSim pleN am e( ) i getC anonicalN am e( ) - uvedene u Javi SE5 - za dobijanje im ena klase bez im ena paketa, odnosno punog im ena. Kao to joj im e govori, m etoda islnterface( ) kazuje da li dati objekat tipa Class predstavlja interfejs. Dalde, p om ou objekta tipa Class m oete saznati zaista sve o odreenom tipu. U m etodi m a in ( ) poziva se m etoda C lass.getlnterfaces( ); ona vraa niz Class objekata koji predstavlja interfejse obuhvaene u Class objektu o kojem je re. Ukoliko im ate objekat tipa Class, m etodom getSuperclass( ) m oete ga pitati koja je njegova neposredna natklasa. M etoda vraa referencu objekta tipa Class koju moete ispitivati dalje. Dalde, u vrem e izvravanja moete saznati celokupnu hijerarhiju klasa svakog objekta. M etoda new ln stan ce( ) klase Class jeste jedan od naina da se realizujevirtuelni konstru k to r, koji om oguava da kaete: ,,Ne znam tano tvoj tip, ali svejedno je - napravi se kako treba. U p rethodn om prim eru, nad je sam o Class referenca bez ikakvih daljih podataka o tipu koji bi bili poznati u vrem e prevoenja. I kada napravite nov prim erak neke klase, vraa vam se referenca tipa Object. Ali ta referenca pokazuje na objekat tipa Igracka. Naravno, da biste mogli slati i druge poruke sem onih koje prihvata klasa Object, m orate da saznate pravi tip i izvrite konverziju tipa. Sem toga, klasa koja se pravi m etodom new ln stance( ) m ora im ati podrazum evani konstruktor. U nastavku poglavlja videete kako se objekti klasa prave dinam iki, pom ou bilo kog konstruktora, u Javinom refleksivnom interfejsu za program iranje.

Veba 1: (1) U program u Ispitivanjelgracaka.java, pretvorite podrazum evani konstrukto r klase Igracka u kom entar i objasnite ta se zbog toga deava. Veba 2: (2) U program Ispitivanjelgracaka.java dodajte novu vrstu interfejsa. Dokaite
da se on ispravno tretira i prikazuje.

Veba 3: (2) D odajte Romboid u program Oblici.java. Napravite jedan Romboid, svedite ga navie na Oblik, zatim ponovo nanie na Romboid. Pokuajte da svedete nanie na Krug i vidite ta se deava. Veba4: (2) Prepravite prethodnu vebu tako da se naredbom instanceof tip utvrdi pre
svoenja nanie.

Poglavlje 14: Podaci o tipu

439

Veba 5: (3) U program u Oblici.java realizujte m etodu rotiraj(O blik) tako da proverava da li joj je zadato da rotira Krug (i neka ga u tom sluaju ne rotira). Veba 6: (4) Prepravite program Oblici.java tako da istie sve oblike odreenog tipa, odnosno postavlja indikator u njim a. M etoda to S trin g ( ) svake izvedene potklase klase ObIik treba da pokae da li je taj O blik ,,istaknut. Veba 7: (3) Prepravite ProdavnicaSlatkisa.java tako da svaku vrstu pravljenja objekta
odreuje argum ent koji se zadaje na kom andnoj liniji. Dakle, ako na kom andnoj liniji pie java ProdavnicaSlatkisa Bombon, o nda se pravi sam o objekat tipa Bombon. O bratite panju na to kako argum entom s kom andne linije odreujete Class objekte koji e biti uitani.

Veba 8: (5) Napiite m etodu koja prim a objekat i rekurzivno ispisuje sve klase u njegovoj
hijerarhiji.

Veba 9: (5) Prepravite prethodnu vebu tako da se pom ou m etode Class.getDeclaredF ields( ) prikazuju i podaci o poljim a klase. Veba 10: (3) Napiite program koji utvruje da li je niz elem enata tipa char p ro st tip ili
pravi objekat.

Literali klase
Java nudi jo jedan nain za dobijanje reference na objekat klase Class, a to je korienje literala klase (engl. class literal). U prethodno navedenom p rogram u to bi izgledalo ovako:
L e p alg rack a.class;

to ne sam o da je jednostavnije, ve je i bezbednije jer se proverava ve tokom prevoenja (te stoga ne m ora biti smeteno u blok try). Poto se ne poziva m etoda forName(), istovrem eno je i efikasnije. Literali klase rade sa obinim klasama, ali i sa interfejsima, nizovim a i prostim tipovima. Pored toga, sve om otake klase prostih tipova sadre standardno polje TYPE. Ovo polje daje referencu na objekat tipa Class za sve odgovarajue proste tipove, kao to su:
...ekvivalentno je ... boolean.class char.class byte.class short.class int.class long.class float.class double.class void.class Boolean TYPE Character.TYPE Byte.TYPE Short.TYPE lnteger.TYPE Long.TYPE Float.TYPE Double.TYPE Void.TYPE

440

Misliti na Javi

Preporuujem da koristite verzije class ako je to mogue, poto se tako pristupa i obinim klasama. Zanimljivo je prim etiti da se pravljenjem reference na objekat tipa Class literalom class, taj objekat ne inicijalizuje autom atski. Priprem a klase za upotrebu zapravo obuhvata tri koraka: 1. Uitavanje, koje obavlja uitava klasa. O na pronalazi bajtkod (obino na disku navedenom u sistemskoj prom enljivoj classpath, ali to ne m o ra biti sluaj) i od njih pravi objekat tipa Class. 2. Povezivanje. U fazi povezivanja proverava se bajtkod klase, dodeljuje m em orija statinim poljim a, i ako treba, razreavaju sve reference na druge klase koje ova klasa pravi.
3 . Inicijalizaciju. Ako postoji natklasa, ona se inicijalizuje. Izvravaju se statini inici-

jalizatori i statini blokovi inicijalizatora. Inicijalizacija se odgaa do prve reference na neku statinu m etodu (konstruktor je im plicitno statian) ili na neko nekonstantno statino polje:
//: p o d a c io tip u / In ic ija liz a c ija O b je k ta T ip a C la s s .ja v a import j a v a . u t i l c la s s In ita b e la { s t a t ic f in a l in t s t a t ic F in a l = 47; s t a t ic fin a l in t s t a t ic F in a l2 = C la s s ln it ia liz a t io n .s lu c a ja n . n e x t ln t (1000); s t a t ic { S y s t e m .o u t .p r in t ln ( " In ic ij a liz a c ija In i t a b e l e " ) ;

1
}
c lass In ita b e la 2 { s t a t ic in t sta tic n a N e Fin a ln a = 147; s t a t ic { S y s t e m .o u t .p r in t ln ( " In ic ij a liz a c ija

In it a b e le 2 " ) ;

} }
c la s s In ita b e la 3 { s t a t ic in t staticn a N eFin aln a = 74; s t a t ic { S y s t e m .o u t .p r in t ln ( " In ic ij a liz a c ija In it a b e le 3 " ) ;

} }
p ub lic c la s s In ic ija liz a c ija O b je k ta T ip a C la s s { p u b lic s t a t i c Random slu ca ja n = new Random(47); p u b lic s t a t ic void m a in (S trin g [] args) throws Exception {

Poglavlje 14: Podaci o tipu

441

C lass In ita b e la = In it a b e la .c la s s ; Sy stem .o u t.p rin tln ("N ak o n p ra v lje n ja re f In it a b e le " ) ; // Ne pokree i n i c i j a l i z a c i j u : S y s t e m .o u t .p r in t ln ( In it a b e la .s t a t ic F in a l) ; // Pokree i n i c i j a l i z a c i j u : S y s te m .o u t .p r in t ln ( In it a b e la .s t a t ic F in a l2 ) ; // Pokree i n i c i j a l i z a c i j u : S y s te m .o u t.p rin tln (In ita b e la 2 .s ta tic n a N e F in a ln a ); Class In ita b e la 3 = C la s s .fo rN a m e ("In ita b e la 3 "); Sy stem .o u t.p rin tln ("N ak o n p ra v lje n ja r e f In it a b e le 3 " ) ; S y s te m .o u t.p rin tln (In ita b e la 3 .s ta tic n a N e F in a ln a );

}
} /* Is p is : Nakon p ra v lje n ja r e f In ita b e le 47 I n i c i j a l i z a c i j a In ita b e le 258 I n i c i j a l i z a c i j a In ita b e le 2 147 I n i c i j a l i z a c i j a In ita b e le 3 Nakon p ra v lje n ja r e f In ita b e le 3 74

* ///:Zapravo, inicijalizacija je lenja koliko god je to m ogue. Nakon pravljenja reference objekta Initabela, vidite da upotreba sintakse .class za dobijanje reference na klasu ne prouzrokuje inicijalizaciju. M edutim , Class.forN am e( ) odm ah inicijalizuje klasu da bi proizvela Class referencu, kao to vidite iz pravljenja objekta Initabela3. Ukoliko je neka statina finalna vrednost konstanta u vreme prevoenja kao to je Initabela.staticFinal, ta vrednost se moe proitati a da klasa Initabela ne bude inicijalizovana. M eutim , ako je neko polje statino i finalno, takvo ponaanje nije zajemeno: pristupanje polju Initabela.staticFinal2 nam ee inicijalizaciju klase, poto ono ne moe biti konstanta u vreme prevoenja. Ako neko statino polje nije finalno, pristupanje njem u uvek zahteva povezivanje (da bi se polju dodelila m em orija) i inicijalizaciju (da bi se inicijalizovala ta m em orija) pre nego to se polje pone itati, kao to vidite iz pristupanja polju Initabela2.staticnaNe-

Finalna.

Generike reference klasa


Class referenca upuuje na neki Class objekat koji proizvodi instance klasa i sadri kod svih m etoda za te instance. Sadri i statine delove te klase. Stoga Class referenca zapravo pokazuje taan tip onoga na ta upuuje: odreenog objekta klase Class.
M edutim , projektanti Jave SE5 ugrabili su priliku da to naprave malo specifinijim, tako to su om oguili da generikom sintaksom ograniite tip Class objekta na koji upuuje Class referenca. U nared no m prim eru, obe sintakse su ispravne:

442

Misliti na Javi

//: podaciotipu/G enerickeC lassReference.java p u b lic c lass GenerickeClassReference { p ub lic s t a t ic void m a in {S trin g [ ] args) { Class in tC las s = in t . c la s s ; Class<Integer> g e n e ric In tC la ss = in t . c la s s ; g e n e ricIn tC lass = In te g e r .c la s s ; // Is t a s tv a r in tC la s s = d o u b le.class; // g en e ricIn tC lass = d o u b le .c la s s; // N ije dozvoljeno

} } /// = O bina referenca klase ne prouzrokuje upozorenje. M eutim , vidite da se obina referenca klase m oe dodeliti bilo kojem drugom objektu tipa Class, d o k se generika referenca klase m oe dodeliti sam o svom deklarisanom tipu. Korienjem generike sintakse om oguavate prevodiocu da sprovede jo jed n u proveru tipova. Kako biste to ogranienje m alo ublaili? N a prvi pogled, izgleda da biste mogli uraditi neto poput:
Class<Number> genericNumberClass = in t . c la s s ;

Ovo naizgled ima smisla jer se Integer nasleuje od klase Number. Ali to nije ispravno, zato to Integer Class objekat nije potklasa N um ber Class objekta. (Razlika vam je moda previe tanana; razm otriem o je poblie u poglavlju Generiki tipovi). Da bih ublaio ogranienja prilikom korienja generikih Class referenci, upotrebljavam dokera koji je deo Javinih generikih tipova. Dokerski znak je ? i on naznauje bilo ta. Stoga obinoj Class referenci u gornjem p rim eru m oem o dodati dokere i dobiemo iste rezultate:
//: podaciotipu/D zokerskeClassReference.java p ub lic c lass DzokerskeClassReference { pu b lic s t a t ic void m a in (S trin g [ ] args) { Class<?> in tC las s = in t .c la s s ; in tC las s = d o u b le.class;

} } ///= U Javi SE5, Class<?> treba da im a prednost nad golim Class iako su ekvivalentni, a golo Class, kao to ste videli, ne prouzrokuje upozorenje prevodioca. Prednost reference Class<?> jeste to to pokazuje da nespecifinu referencu klase ne upotrebljavate sluajno ili zbog neznanja, nego namerno. Za pravljenje Class reference ograniene na odreeni tip ili bilo koji njegov podtip , kom binujte dokera i rezervisanu re extends; tim e pravite ogranienje. Dakle, um esto da kaete sam o Class<Number>, recite:

Poglavlje 14: Podaci o tipu

443

/ / : pod aciotipu/O graniceneClassReference.java pu b lic c lass OgraniceneClassReference { p ub lic s t a t i c void m a in (S trin g [ ] args) { Class<? extends Number> ogranicen = in t .c la s s ; ogranicen = d o u b le .c la s s; ogranicen = Number.class; // I l i b ilo ta drugo izvedeno iz klase Number.

} } ///= G enerika sintaksa se dodaje Class referencam a sam o da bi se proverili tipovi u vreme prevoenja, pa ako negde pogreite, saznaete to neto ranije. Ni sa obinim Class referencam a ne m oete zastraniti, ali ako negde pogreite, saznaete to tek nakon izvravanja program a, to um e da bude nezgodno. Evo prim era upotrebe generike sintakse. U p rogram u se skladiti referenca klase i kasnije pravi Lista popunjena objektim a generisanim m etodom new lnstance( ):
//: podacio tipu /PopunjenaLista.java import j a v a . u t i l c la s s PrebrojaniC eoBroj { p riv a te s t a t i c long b ro jac; p riv a te f in a l long id = brojac++; p u b lic S trin g to S trin g O { return L o n g .to S tr in g (id ); }

}
p ub lic c lass PopunjenaLista<T> { p riv a te Class<T> t ip ; p u b lic PopunjenaLista(C1ass<T> t ip ) { t h i s . t i p = t ip ; p ub lic List<T> c r e a t e (in t nElemenata) { List<T> re z u lta t = new A rra y List< T > (); try { f o r ( i n t i = 0; i < nElemenata; i++) r e z u lt a t . add( tip .n e w ln s ta n c e ()) ; } catch (Ex cep tion e) { throw new Runtim eException(e);

}
return r e z u lta t;

}
p ub lic s t a t ic void m a in (S trin g [ ] args) { PopunjenaLista<PrebrojaniCeoBroj> pl = new Po p u n je n aLista< Preb ro jan iC eo Bro j> (Preb ro jan iC eo B ro j.class); System .out. pri n t l n ( p l . c re a te (1 5 )) ;

}
} /* Is p is : [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]

* ///:-

444

Misliti na Javi

Vodite rauna o tom e da ova klasa m ora pretpostaviti kako svaki tip s kojim radi im a podrazum evani konstruktor (onaj bez argum enata), inae ete dobiti izuzetak. Za ovaj program prevodilac ne generie nikakvo upozorenje. Zanimljiva se stvar deava kada za objekte tipa Class upotrebite generiku sintaksu: m etoda new lnstance( ) vratie taan tip objekta, a ne sam o tip Object kako ste videli u program u Ispitivanjelgracaka.java. To je donekle ogranieno:
//: p o d a cio tip u /ig ra c k e /G e n e ric k o Isp itiva n je lg ra c a k a .ja va // Is p it iv a n je klase C lass. package p o d a cio tip u .ig rack e ; p u b lic c la s s G e n e ric k o lsp itiva n je lg ra c a k a { p u b lic s t a t i c void m a in (S trin g [ ] args) throws Exception { Class<LepaIgracka> liC la s s = Lep a lg ra c k a .cla ss ; // Daje taan t ip : Lepalgracka Lepalgracka = liC la s s .n e w In s ta n c e (); Class<? super Lepalgracka> nad = liC la s s .g e tS u p e r c la s s (); // Ovo se ne bi prevelo : // Class<Igracka> nad2 = T iC la s s .g e tS u p e rc la s s (); // Daje obian t ip O bject: Object obj = n ad .new ln sta n ce();

} } ///= Ako dobijete natklasu, prevodilac e dozvoliti samo da kaete kako je referenca natklase neka klasa koja je natklasa klase Lepalgracka, kao to se vidi iz izraza Class<? super Lepalgracka>. Nee prihvatiti deklaraciju CIass<Igracka>. To izgleda pom alo udno, zato to getSuperclass( ) vraa osnovnu klasu (ne interfejs), a prevodilac u vrem e prevoenja zna koja je to klasa - u ovom sluaju, Igracka.class, a ne sam o neka natklasa od Lepalgracka. U svakom sluaju, zbog te neodredenosti, povratna vrednost m etode nad.new lnstance( ) nije taan tip, nego sam o Object.

Nova sintaksa za konverziju tipova


U Javu SE5 dodata je i posebna sintaksa za konverziju tipova koja se upotrebljava za Class reference; to je m etoda c a st( ):
/ / : p odaciotip u/K o nverzijaC lassT ip o va.java c la s s Zgrada { } c la s s Kuca extends Zgrada { } p u b lic c la s s KonverzijaClassTipova { p u b lic s t a t i c void m a in (S trin g [] args) { Zgrada z = new K u c a (); Class<Kuca> tipKuce = K u c a.class; Kuca k = tip K u c e .c a s t(z ); k = (K u ca)z ; // . . . i l i u ra d ite samo ovo.

} } ///:-

Poglavlje 14: Podaci o tipu

445

M etoda c a st( ) prim a objekat kao argum ent i pretvara ga u tip Class reference. Naravno, nakon to pogledate gornji kod, to izgleda kao m nogo vie posla nego u poslednjem redu m etode m a in ( ) koji radi istu stvar. N ova sintaksa za konverziju tipova je podesna za situacije u kojim a ne moete da upotrebite obinu konverziju tipa. To se najee dogaa kada piete generiki kod (to ete nauiti u poglavlju Generiki tipovi) i uskladitili ste Class referencu koju kasnije nam eravate da upotrebite za konverziju. Ispostavlja se da je to retko p o treb n o u celoj biblioteci Jave SE5 naao sam sam o jedan sluaj gde je m etoda c a st( ) bila koriena (u

com .sun.m irror.util.D eclarationFilter).


Jedna nova m ogunost uopte nije nala p rim enu u biblioteci Jave SE5: Class.asSub-

class( ). O na slui za konverziju objekta tipa Class u specifiniji tip.

Provera pre konverzije tipa


D osad ste videli sledee naine za prepoznavanje tipa tokom izvravanja:

1 . Klasina konverzija tipa, npr. (Oblik), koja tokom izvravanja prepoznaje tip da bi proverila ispravnost konverzije tipa ili generisala izuzetak tipa CIassCastException
ako je konverzija loa.

2. O bjekat tipa Class koji predstavlja tip objekta. O bjekat tipa Class se m oe ispitivati
tokom izvravanja da bi se saznale korisne informacije. U jeziku C ++ , ldasina konverzija tipa (Oblik) ne prepoznaje stvarni tip tokom izvravanja, ve sam o saoptava prevodiocu da objekat posm atra kao novi tip. U Javi, koja proverava tip, ovakva konverzija tipa esto se naziva svoenje nanie koje uva tip (engl. type-safe owncast). Ime svoenje nanie (engl. downcast) nastalo je zbog uobiajenog izgleda dijagram a hijerarhije klasa. Ako je konverzija iz klase Krug u Oblik svoenje navie (engl. upcast), onda je svoenje iz O blik u Krug svoenje nanie. M eutim , poto znam o da je Krug istovrem eno i Oblik, prevodilac dozvoljava odelu uz svoenje navie bez ikakvih ogranienja. Prevodilac ne moe znati, za dati Oblik, ta je taj O blik zapravo m oda je ba Oblik, ili podtip klase Oblik, kao to su Krug, Kvadrat, Trougao ili drugi oblik. U vrem e prevoenja, prevodilac vidi sam o Oblik. Stoga prevodilac ne dozvoljava dodelu uz svoenje nanie bez eksplicitnog korienja operatora konverzije tipa, da bi m u saoptio kako vi im ate dodatne podatke na osnovu kojih znate taan tip (prevodilac e proveriti da li je to svoenje nanie razborito i nee dozvoliti svodenje nanie na tip koji nije potklasa). Postoji i trei oblik prepoznavanja tipa tokom izvravanja u Javi. To je rezervisana re in stan c eo f koja proverava da li je objekat instanca odreenog tipa. O na vraa rezultat tipa boolean, pa je na sledei nain moete koristiti u logikim izrazima:
i f ( x in stan ceo f Pas) ( ( P a s )x ) .1a j ( ) ;

G ornja naredba if proverava da li objekat x pripada klasi Pas pre nego to izvri konverziju u tip Pas. Kada nem a drugih inform acija o tipu objekta, vano je da se in stan ceo f upotrebi pre svoenja nanie; u suprotnom , nastae izuzetak tipa ClassC astException.

446

Misliti na Javi

O bino ete biti usredsreeni na jedan tip (npr. da pretvorite sve trouglove u ruiaste), ali pom ou rezervisane rei instanceof m oete lako proveriti sve objekte. Pretpostavimo da im am o porodicu klasa za opisivanje kunih Ljubimaca (i njihovih vlasnika, to e nam zatrebati kasnije). Svaka Jedinka u hijerarhiji im a id i opciono ime. Iako sledee klase nasleuju klasu Jedinka, ona je donekle sloena, pa emo njen kod prikazati i objasniti u poglavlju Detaljno razmatranje kontejnera. Kao to zapaate, u ovom trenutku i nije neophodno da vidite kod klase Jedinka - treba sam o da znate sledee: moete je napraviti sa im enom ili bez im ena i svaka Jedinka im a m etodu id ( ) koja vraa jedinstven identifikator (napravljen brojanjem svakog objekta). Tu je i m etoda toS tring( ); ako napravite prim erak klase Jedinka, a ne date joj ime, m etoda to S trin g ( ) vratie samo prosto ime tipa. Ova hijerarhija klasa nasleuje klasu Jedinka:
//: podaciotipu/1jubim ci/O soba.java package p o d a cio tip u .1ju b im c i; p u b lic c la s s Osoba extends Jedinka { p u b lic O soba(String ime) { su per(im e); }

) III-/ / : podaciotipu/1jubim ci/Ljubim ac.java package p o d a c io tip u .lju b im c i; p ub lic c la s s Ljubimac extends Jedin ka { p u b lic Lju b im ac(Strin g ime) { super(im e); } p u b lic Ljubim ac() { su p e r(); }

} ///= //: p od aciotipu/1jubim ci/Pas.java package p o d a c io tip u .lju b im c i; p u b lic c la s s Pas extends Ljubimac { p u b lic P a s (S trin g ime) { su per(im e); } p u b lic Pa s() { s u p e r (); }
1

///:-

/ / : podaciotipu/1jubim ci/Mesanac.ja va package p o d a c io tip u .lju b im c i; p ub lic c la s s Mesanac extends Pas { p u b lic M esanac(String ime) { super(im e); } p u b lic MesanacO { s u p e r(); }

} ///://: podaciotipu/1jubim ci/M ops.java package p o d a c io tip u .lju b im c i; pu b lic c la s s Mops extends Pas { p u b lic M ops(String ime) { super(im e); }

p u b lic Mops() { s u p e r(); }

} III-//: podaciotipu/1jubim ci/M acka.java package p o d a c io tip u .lju b im c i; p u b lic c la s s Macka extends Ljubimac { p u b lic M acka(String ime) { super(im e); } p u b lic Macka() { s u p e r(); }

} III-/ / : p od aciotipu /1ju bim ci/Egip atska.java package p o d a cio tip u .1ju b im c i; p u b lic c la s s Egipatska extends Macka { p u b lic E g ip a ts k a (S trin g ime) { super(ime) p u b lic EgipatskaO { s u p e r(); }

} III-/ / : podaciotipu/1jubim ci/Manska.java package p o d a c io tip u .lju b im c i; p u b lic c la s s Manska extends Macka { p u b lic M anska(String ime) { super(im e); } p u b lic Manska() { s u p e r (); }

} lll-~
I I : podaciotipu/1jubim ci/Velska.java
package p o d a c io tip u .lju b im c i; p u b lic c la s s Velska extends Manska { p u b lic V e ls k a (S trin g ime) { super(im e); } p u b lic V e lsk a () { s u p e r(); }

III---

I I : podacioti pu/1jubim ci/Glodar.java


package p o d a cio tip u .lju b im c i; p u b lic c la s s Glodar extends Ljubimac { p u b lic G lo d a r(S trin g ime) { super(im e); } p u b lic G lo d ar() { s u p e r (); }

} III--/ / : podaciotipu/1jubim ci/Pacov.java package p o d a cio tip u .lju b im c i; p u b lic c la s s Pacov extends Glodar { p u b lic Pa co v (S trin g ime) { super(im e); } p u b lic Pacov() { s u p e r(); }

} ///:-

448

Misliti na Javi

//: podaciotipu/1jubim ci/M is.ja va package p o d a cio tip u .lju b im c i; public c la s s Mis extends Glodar { public M is (S trin g ime) { sup er(im e); } public M is() { s u p e r (); }

} ///://: podaciotipu/1jubim ci/H rcak.java package p o d a cio tip u .lju b im c i; public c la s s Hrcak extends Glodar { public H rcak (Strin g ime) { su per(im e); } p ublic H rcak() { s u p e r (); }

} ///:Sada nam treba nain za sluajno generisanje razliitih vrsta ljubim aca, i da bi nam bilo lake, za pravljenje nizova i Lista Ijubimaca. Da bi ova alatka mogla da se razvija kroz nekoliko razliitih realizacija, definisaemo je kao apstraktnu klasu:
//: p od aciotip u /1 jub im ci/Pravljen jeLju b im aca.ja v a // Pravi slu a jn e sekvence Ljubimaca. package p o d a cio tip u .lju b im c i; import j a v a . u t i l .* ; public a b s tra ct class Pravljen jeLju b im a ca { p riv a te Random slu cajan = new Random(47); // L is ta r a z l i i t i h vrs ta Ljubimaca koje treba n a p r a v iti: public ab stra ct List<Class<? extends L ju b im a c t i p o v i ( ) ; p ub lic Ljubimac nekiLjubim ac() { // Napravi jednog nasuminog Ljubimca in t n = s lu c a ja n .n e x t In t (t ip o v i( ) . s i z e ( ) ) ; try { return t i p o v i( ) .g e t(n ).n e w ln s ta n c e (); } c a tc h (In s ta n tia tio n Ex c e p tio n e) { throw new Runtim eException(e); } c a tc h (Ille g a lA c c e ss E x c e p tio n e) { throw new Runtim eException(e);

} }
p ublic Ljubim ac[ ] c re a te A rra y ( in t v e lic in a ) { Ljubimac[ ] re s u lt = new Ljubim ac[vel i c in a ] ; f o r ( i n t i = 0; i < v e lic in a ; i++) r e z u l t a t [ i ] = n e k iL ju b im a c (); return r e z u lta t;

}
public ArrayList<Ljubim ac> a r r a y L is t ( in t v e lic in a ) { ArrayList<Ljubimac> re z u lta t = new A rrayList< Lju b im ac> (); C o lle c tio n s .a d d A l1(r e z u lt a t , c r e a t e A r r a y (v e lic in a )) ; return r e z u lta t;

} } ///:-

PoglavJj'e 14: Podaci o tipu

449

Da bi dobila Listu objekata tipa Class, apstraktna m etoda tip o v i( ) obraa se nekoj izvedenoj klasi - ovo je varijanta projektnog obrasca Template Method (ablonska metoda). Poto je zadato da tip klase bude neto izvedeno iz klase Ljubixnac, m etoda newInstance( ) pravi objekat tipa Ljubimac bez konverzije tipa. nekiLjubim ac( ) nasumino indeksira Listu i izabrane objekte tipa Class upotrebljava za generisanje nove instance te klase pom ou m etode Class.new lnstance( ). M etoda createA rray( ) upotrebljava m etodu nekiLjubim ac( ) za popunjavanje niza, a arrayL ist( ) upotrebljava createArray( ). Kada pozovete m etodu new ln stance( ), m oete da prouzrokujete dve vrste izuzetaka koje su obraene u odredbam a catch iza bloka try. Podsetim o se, im ena izuzetaka su relativno jasna objanjenja onoga to je zakazalo (IllegalAccessException oznaava krenje pravila Javinog m ehanizm a bezbednosti, u ovom sluaju podrazum evani konstruktor je privatan). Kada izvodite potklasu klase PravljenjeLjubimaca, jedino m orate da navedete Listu tipova ljubim aca koju hoete da napravite m eto dom nekiLjubim ac( ) i drugim m etodama. M etoda getTypes( ) obino vraa sam o referencu neke statine Liste. Evo jedne realizacije m etodom forN am e( ):
//: podaciotip u/1ju b im ci/ForN am ePravljen je.java package p o d a cio tip u .lju b im c i; import j a v a . u t i l .* ; p ublic c la s s ForNam ePravljenje extends Pravljen jeLju b im a ca { p riv a te s t a t ic List<Class<? extends L ju b im a c tip o v i = new ArrayList< Class< ? extends L ju b im a c (); // Tipovi koje hoete da n ap ravite nasumino: p riv a te s t a t ic S t r in g [ ] imenaTipova = { "podaci o ti pu.1jub im ci.M esanac", "p o d a c io tip u .lju b im c i.Mops", "p o d a c io tip u .1ju b im c i. E g ip a ts k a ", "p o d a c io tip u .lju b im c i.Manska", "po d acio ti pu. 1ju b im c i. Vel s k a ", "p o d acio ti pu. 1ju b im c i. Pa co v ", "podaci o ti pu. 1ju b im c i.Mi s " , "p o d a c io tip u .1ju b im c i.Hrcak"

};
@SuppressWarni ngs("unchecked") p riv a te s t a t ic void lo a d e r() { try { f o r (S t r in g ime : imenaTipova) tip o v i,a d d ( (Class<? Extends Ljubimac>)C1a s s . forName(ime)) ; } catch (C1assNotFoundException e) { throw new Runtim eException(e);

} }
s t a t ic { lo a d e r (); } p ub lic List<Class<? extends L ju b im a c t i p o v i ( ) {re tu rn t i p o v i ; }

} ///:-

450

Misliti na Javi

M etoda lo ad e r( ) pravi Listu objekata tipa Class pom ou m etode Class.forN am e( ). To moe da prouzrokuje izuzetak ClassNotFoundException, to im a smisla jer joj prosleujete znakovni niz ija se validnost ne m oe proveriti u vrem e prevoenja. Poto su objekti tipa Ljubimac u paketu podaciotipu, prilikom navoenja im ena klase m ora se navesti i ime paketa. Da bi se napravila Lista objekata tipa Class za razliite tipove, potreb n a je konverzija tipova koja prouzrokuje upozorenje u vrem e prevoenja. M etodu lo a d e r( ) definiemo zasebno i zatim sm etam o u n u tar statinog bloka za inicijalizaciju, zato to se anotacija @SuppressWarnings ne m oe napisati direktno u n u ta r statinog bloka za inicijalizaciju. Za brojanje prim eraka klase Ljubimci treba nam alatka koja prati broj razliitih tipova prim eraka klase Ljubimci. Za to je Mapa savrena; kljuevi su im ena tipova Ljubimaca, a vrednosti Integeri koji odraavaju broj Ljubimaca. Na taj nain m oete rei: Koliko im a objekata tipa Hrcak?" Za brojanje prim eraka klase Ljubimci m oem o upotrebiti rezervisanu re instanceof:
//: podaciotipu/PrebrojavanjeLjubim aca.java // Upotreba rezervisane re i in stan ceo f. import p o d a cio tip u .lju b im c i.* ; import j a v a . u t i l .* ; import s t a t ic n e t.m in d v ie w .u til. P r i n t . * ; p ublic c lass PrebrojavanjeLjubim aca { s t a t ic c lass BrojacLjubim aca extends HashMap<String,Integer> { public void p re b ro j(S trin g t i p ) { In teger k o lic in a = g e t ( t ip ) ; if ( k o l ic in a = = n u ll) p u t(tip , 1); el se p u t(tip , k o lic in a + 1 );

} 1
public s t a t ic void preb rojLju b im ce(PravljenjeLju bim aca p ra v lje n je ) { BrojacLjubim aca brojac= new B ro ja c L ju b im a c a (); for(Ljubim ac Ijubim ac : p ra v lje n je .c re a te A r ra y (2 0 )) { // Is p i i svakog pojedinog ljubim ca: p rin tn b (lju b im ac.g etC lass().g e tSim p leN am e() + " " ) ; if(lju b im a c in stan ceo f Ljubimac) b ro ja c .p re b ro j("L ju b im a c "); if(lju b im a c in stan ceo f Pas) b r o ja c .p r e b r o j(" P a s "); if(lju b im a c in stan ceof Mesanac) b ro ja c .p re b ro j("M e s a n a c "); if(lju b im a c in stan ceo f Mops) b ro ja c .p re b ro j("M o p s "); if(lju b im a c in stan ceo f Macka) b ro ja c .p re b ro j("M a c k a ");

Poglavlje 14: Podaci o tipu

451

i f (1jubimac in stanceo f Manska) b r o ja c .p r e b r o j(" E g ip a ts k a " ); if(lju b im a c in stan ce o f Manska) b ro ja c .p re b ro j("M a n s k a "); if(lju b im a c in stan ceo f Manska) b r o ja c .p r e b r o j(" V e ls k a "); if(lju b im a c in stan ceo f Glodar) b r o ja c .p r e b r o j("G lo d a r "); if(lju b im a c in stan ceo f Pacov) b r o ja c .p r e b ro j("P a c o v "); if(lju b im a c in stan ceo f Mis) b r o ja c .p r e b r o j(" M is "); if(lju b im a c in stan ce o f Hrcak) b r o ja c .p r e b ro j("H r c a k ");

}
// Is p i i broj ljubim aca: p r in t(); p r in t ( b r o ja c ) ;

}
pu b lic s t a t ic void m a in (S trin g [] args) { prebrojLjubimce(new ForNamePravl j e n j e ( ) ) ;

}
} /* Is p is : Pacov Manska Velska Mesanac Mops Velska Mops Manska Velska Pacov Egipatska Hrcak Egipatska Mesanac Mesana Velska Mis Mops Mis Velska {Mops=3, Macka=9, Hrcak=l, Velska=7, Mis=2, Mesanac=3, Glodar=5, Ljubimac=20, Manska=7, Egipatska=7, Pas=6, Pacov=2}

* ///:U metodi prebrojL jubinice(), PravljenjeLjubimaca nasum ino popunjava niz prim ercim a klase Ljubinici. Zatim se svaki Ljubimac u nizu ispituje i broji pom ou rezervisane rei instanceof. Rezervisana re instanceof im a prilino blago ogranienje: om oguuje poreenja sam o sa im enovanim tipom , a ne sa objektom tipa Class. Gledajui gornji prim er, moda ste pomislili kako je dosadno pisati sve te izraze sa instanceof, i u pravu ste. M eutim , nem a naina za pam etno autom atizovanje poreenja pom ou instanceof tako to e se napraviti niz objekata tipa Class i porediti s njim a (ne oajavajte, ipak postoji alternativno reenje). To nije veliko ogranienje kao to m oda mislite, poto vam projekat i nije ba najbolji ako piete m nogo izraza instanceof.

Korienje literala klase


Ako prepravim o prim er PravljenjeLjubimaca.java tako da koristi literale klase, dobiem o m nogo itljiviji kod:
//: podaciotipu/1jubim ci/RucnoPravljenjeLjubim aca.java // Korienje l i t e r a l a klase. package p o d a c io tip u .lju b im c i; import j a v a . u t i l .* ;

452

Misliti na Javi

p ub lic c la s s RucnoPravljenjeLjubim aca extends Pravljen jeLju b im aca { // Blok t r y n ije potreban. @SuppressWarnings("unchecked") p ub lic s t a t ic fin a l List<Class<? extends L ju b im a c sv iT ip o v i = C ol1ections.unmodi f i abl eLi s t (A r r a y s . asLi s t ( Lju b im a c.class, M acka.class, G lo d a r.c la s s , M esanac.class, M ops.class, E g ip a ts k a .c la s s , M anska.class, V e ls k a .c la s s , P a c o v .c la s s , M is .c la s s .H r c a k .c la s s )); // Tipovi za nasumino p ra v lje n je : p riv a te s t a t ic fin a l List<Class<? extends L ju b im a c tip o v i = s v iT ip o v i.s u b L is t(s v iT ip o v i.in d e x O f(M e s a n a c .c la s s ), s v iT ip o v i, s iz e ( ) ) ; p u b lic List<Class<? extends L ju b im a c t i p o v i ( ) { return tip o v i;

}
pu b lic s t a t ic void m a in (S trin g [] args) S y s te m .o u t .p r in tln (tip o v i); {

}
} /* Is p is : [c la s s pod acio tip u.ljubim ci.M esanac, c la s s p o dacio tipu .lju bim ci.M o ps, c la s s p o d a c io tip u .lju b im c i.E g ip a ts k a , c la s s p odaciotipu.ljubim ci.M anska, c la s s p o d a c io tip u .lju b im c i.V e ls k a , c la s s p o d a cio tip u .1ju b im c i. Pacov, c la s s p o d a cio tip u .lju b im c i.M is, c la s s p o d a cio tip u .lju b im ci.H rca k ]

* ///:U prim eru PrebrojavanjeLjubimaca3.java koji em o prikazati u narednom odeljku, m oram o unapred da popunim o Mapu svim tipovim a klase Ljubimac (ne sam o onim a koji e biti nasum ino generisani), pa nam je neophodna Lista sviTipovi. Lista tipovi je deo liste sviTipovi napravljene m etodom List.subList( ); ona obuhvata tane tipove Ijubimaca, pa se upotrebljava za nasum ino pravljenje Ljubimaca. Ovoga puta lista tipovi ne m ora da se pravi u bloku try, poto se proverava tokom prevoenja i stoga nee generisati nikakve izuzetke, za razliku o m etode Class.forN am e( ). U biblioteci podaciotipu.ljubim ci sada im am o dve realizacije klase PravljenjeLjubimaca. Da bism o drugu od njih proglasili podrazum evanom , m oem o napraviti projektni obrazac Fafade (Fasada) koji upotrebljava program RucnoPravljenjeLjubimaca:
//: p odaciotipu /1jubim ci/Ljubim ci. ja va // Fasada za p ra v lje n je podrazumevane r e a liz a c ij e klase // Pravljen je Lju b im a ca . package p o d a c io tip u .lju b im c i; import j a v a . u t i 1 .*; p u b lic c la s s Ljubimci { p u b lic s t a t i c fin a l Pravljen jeL ju b im a ca p ra v lje n je = new R u cn o Pravljen je L ju b im a ca (); p u b lic s t a t ic Ljubimac nekiLjubim acO { return p r a v lje n je .n e k iL ju b im a c ();

Poglavlje 14: Podaci o tipu

453

p u b lic s t a t ic Ljubim ac[ ] c re a te A rra y (in t v e lic in a ) return p r a v lje n je .c r e a t e A r r a y (v e lic in a );

}
p u b lic s t a t ic ArrayList<Ljubimac> a r r a y L is t ( in t v e lic in a ) return p r a v l je n j e . a r r a y L is t ( v e l ic in a ) ; {

} } ///:Tim e je napravljena i indirekcija za m etode nekiLjubim ac(), createA rray() i

arrayL ist().
Poto m etoda PrebrojavanjeLjubim aca.prebrojLjubim ce() prim a argum ent PravljenjeLjubimaca, lako nam je da ispitam o program RucnoPravljenjeLjubimaca (preko p retho dne Fasade):
// : podaciotipu/PrebrojavanjeLjubim aca2.java import p o d a c io tip u .lju b im c i.* ; p u b lic c la s s PrebrojavanjeLjubim aca2 { p u b lic s t a t i c void m a in (S trin g [ ] args) { Prebrojavan jeLju b im aca.p rebrojLjubim ce(Ljub im ci. pravl j e n j e ) ;

}
} /* (Po k re n ite da b is te v id e li re z u lta te ) * / / / :

Rezultati su isti kao oni program a PrebrojavanjeLjubimaca.java.

Dinamiki instanceof
M etoda Class.islnstance nudi nain za dinam iko ispitivanje tipa objekta. Tako se sve dosadne naredbe instanceof m ogu ukloniti iz prim era PrebrojavanjeLjubimaca:
//: podaciotipu/PrebrojavanjeLjubim aca3.java // K o rien je metode is ln s t a n c e () . import p o d a cio tip u .1ju b im c i.* ; import j a v a . u t i l .* ; import n e t.m in d v ie w .u til.* ; import s t a t ic n e t.m in d v ie w .u til. P r in t . * ; p ub lic c la s s PrebrojavanjeLjubim aca3 { s t a t ic c lass BrojacLjubim aca extends L i nkedHashMap<Class<? extends Ljubimac>,Integer> { p u b lic BrojacLjubim acaO { super(M apD ata.m ap(RucnoPravljenjeLjubim aca.sviTipovi, 0 ) ) ;

}
p u b lic void preb roj(Ljubim ac Ijubim ac) { // C la s s .is ln s ta n c e f) uklanja naredbe in stan ceo f: for(M ap.Entry<Class<? extends Ljubimac>,Integer> par : e n tr y S e t()) if ( p a r .g e t K e y ( ) . i sln s ta n c e (lju b im a c )) p u t(p a r .g e tK e y (), p a r.g e tV a lu e () + 1 );

454

Misliti na Javi

p u b lic S trin g t o S t r in g () { S trin g B u ild e r re z u lta t = new S t r in g B u ild e r C 'C ') ; for(M ap.Entry<Class<? extends Ljubimac>,Integer> par : e n t r y S e t ()) { rez u lta t.a p p e n d (p a r.g e tK e y ().g e tSim p le N a m e ()); rez u ltat.ap p en d ("= ) ; rez u lta t.a p p en d (p a r.g etV al u e ( ) ) ; re z u lta t.a p p e n d (", " ) ;

}
r e z u lt a t .d e le t e (r e z u lt a t .le n g t h ()- 2 , r e z u lt a t .le n g t h O ); re z u lta t.a p p e n d C '}"); return r e z u l t a t . t o S t r in g ( ) ;

} }
p u b lic s t a t ic void m a in (S trin g [] args) { BrojacLjubim aca brojLjubim aca = new B ro ja cL ju b im a c a (); for(Ljub im ac ljubim ac : L ju b im c i.c re a te A rra y (2 0 )) { p rin tn b (l jub im ac.g etC lassO .getSimpleName() + " " ) ; b ro jLju b im a ca .p reb ro j(1 ju b im a c );

}
p r in t(); p rin t(b ro jL ju b im a c a );

}
} /* Is p is : Pacov Manska Velska Mesanac Mops Velska Mops Manska Velska Pacov Egipatska Hrcak Egipatska Mesanac Mesanac Velska Mis Mops Mis Velska {Ljubimac=20, Pas=6, Macka=9, Glodar=5, Mesanac=3, Mops=3, Egipatska=2, Manska=7, Velska=5, Pacov=2, Mis=2, Hrcak=l}

* ///:Da bi se prebrojali svi razliiti tipovi prim eraka klase Ljubimac, Mapa BrojacLjubimaca se unapred popunjava tipovim a iz liste RucnoPravIjenjeLjubimaca.sviTipovi. Ta lista upotrebljava klasu net.mindview.util.M apData koja prim a objekat tipa Iterable (listu sviTipovi) i konstantu (u ovom sluaju, nulu), i popunjava Mapu kljuevima uzetim iz liste sviTipovi i vrednou nula. Da Mapu nism o popunili unapred, prebrojali bismo sam o nasum ino generisane tipove, ali ne i osnovne tipove kao to su Ljubimac i Macka. Vidite da zbog korienja m etode islnstance() nisu potrebni izrazi instanceof. Osim toga, to znai i da nove tipove kunih ljubim aca m oete da dodajete jednostavnim menjanjem niza tipovi; ostatak program a ne m ora da se m enja (to nije bio sluaj pri korienju izraza instanceof). M etodu to S trin g () preklopili sm o da bi davala itljiviji izlaz koji i dalje odgovara tipinom izlazu koji se dobija ispisivanjem M ape.

Rekurzivno brojanje
Mapa u PrebrojavanjeLjubimaca3.BrojacLjubimaca bila je unapred popunjena svim razliitim klasama Ljubimac. Umesto da m apu popunjavam o unapred, m oem o upotrebiti m etodu Class.isAssignableFrom ( ) i napraviti alatku opte nam ene koja um e da broji sve, a ne samo prim erke klase Ljubimci:

Poglavlje 14: Podaci o tipu

455

//: n e t/m in d view /u til/Bro jacT ip o va.java // B r o ji in stan ce date porodice tip o va . package n e t.m in d v ie w .u til; import j a v a . u t i l .* ; p u b lic c la s s BrojacTipova extends HashMap<Class<?>,Integer>{ p riv a te Class<?> osnovniTip; p u b lic BrojacTipova(Class< ?> osnovniTip) { th is.o s n o vn iT ip = osnovniTip;

}
p u b lic void p re b ro j(O b ject obj) { Class<?> t ip = o b j. g e tC lass( ) ; if(!o s n o v n iT ip .is A s s ig n a b le F ro m (tip )) throw new RuntimeException(obj + " netaan t i p : 1 1 + t ip + " , tre b a lo bi da bude t ip i l i podtip od 1 1 + osnovniTi p ) ; c o u n t C la s s (tip );

}
p riv a te void countClass(Class<?> t i p ) { In te g e r k o lic in a = g e t ( t ip ) ; p u t (t ip , k o lic in a = = n ull ? 1 : k o lic in a + 1); Class<?> natKlasa = tip .g e tS u p e r c la s s (); if(n a d K la s a != n u ll && osnovn iT ip.isA ssignableFrom (nadKlasa)) c o u n tC la s s(n a tK la s a );

}
p u b lic S trin g to S trin g O { S trin g B u ild e r re z u lta t = new S t r i n g B u i ld e r ( " { " ) ; for(M ap.Entry<Class<?>,Integer> par : e n tr y S e t()) re z u lta t.a p p en d (p ar.g etK ey().g etSim p leN a m e ()); rezul ta t.ap pen d('' =" ) ; re z u lta t.a p p e n d (p a r.g e tV a lu e ( ) ) ; re z u lta t.a p p e n d (", " ) ;

}
r e z u lta t.d e le te (r e z u lta t .le n g th ()- 2 , r e z u lt a t .le n g t h ( ) ) ; r e z u lt a t . append( } " ) ; retu rn r e z u lt a t .t o S t r in g O ;

} > ///:M etoda p re b ro j( ) od svog argum enta dobija objekat tipa Class, i upotrebljava m etodu isA ssignableFrom ( ) za proveru tokom izvravanja da li objekat koji ste prosledili zaista pripada hijerarhiji o kojoj je re. M etoda countC lass( ) najpre broji taan tip te klase. Zatim , ako je osnovniTip dodeljiv (engl. assignable) iz natklase, countC Iass( ) se poziva rekurzivno na natklasu.
// : podaciotipu/PrebrojavanjeLjubim aca4.ja va import p o d a c io tip u .lju b im c i.* ; import n e t.m in d v ie w .u til.* ; import s t a t ic n e t.m in d v ie w .u til. P r i n t . * ;

456

Misliti na Javi

p u b lic c la s s PrebrojavanjeLjubim aca4 { p u b lic s t a t ic void m a in (S trin g [] args) { BrojacTipova brojac = new B ro ja c T ip o v a (L ju b im a c .c la s s ); for(Ljub im ac ljubimac : L ju b im c i.c re a te A rra y (2 0 )) { printnb (1jub im ac.g etC lass().getSim pleN am e() + " ) ; b ro ja c .p re b ro j(1ju b im ac);

)
p r in t(); p r in t ( b r o ja c ) ;

}
} /* Is p is : (Sample) Pacov Manska Velska Mesanac Mops Velska Mops Manska Velska Pacov Egipatska Hrcak Egipatska Mesanac Mesanac Velska Mis Mops Mis Velska {Mis=2, Pas=6, Manska=7, Egipatska=2, Glodar=5, Mops=3, Mesanac=3, Velska=5, Macka=9, Hrcak=l, Ljubimac=20, Pacov=2}

* ///:Kao to vidite iz rezultata, broje se i osnovni tipovi i tani tipovi.

Veba 11: (2) Biblioteci podaciotipu.Ijubim ci dodajte klasu M orskoPrase i prepravite sve prim ere u ovom poglavlju tako da se prilagode novoj klasi. Veba 12: (3) U potrebite BrojacTipova s klasom GeneratorKafe.java iz poglavlja Generiki tipovi.

Veba 13: (3) U potrebite BrojacTipova iz prim era RegisteredFactories.java u ovom


poglavlju.

Registrovane proizvodne metode


Ako generiete objekte hijerarhije Ljubimci, m orate se setiti da sve budue tipove te hijerarhije dodate i u klasu rucnoPravljenjeLjubimaca.java. Ukoliko esto pravite nove klase, to m oe predstavljati problem . M oda ste pomislili da potklasam a treba dodati statini inicijalizator koji upisuje klasu u neku listu. Naalost, statini inicijalizatori se pozivaju prilikom uitavanja klase, pa se javlja problem kokoke i jajeta: kada klasa nije u listi poznatih tipova, rucnoPravljenjeLjubimaca ne moe da pravi objekte te klase, a zbog toga klasa nee ni biti uitana ni stavljena na Iistu. U sutini, takvu listu m orate sami da napravite, i to runo (sem ukoliko napiete alatku koja analizira izvorni kod). Stoga je korisno da lista bude na centralnom , vidljivom m estu. Najbolje je da tu listu stavite u osnovnu klasu hijerarhije. N apraviem o jo jednu izm enu - prebaciem o pravljenje objekta u klase, za ta emo u potrebiti projektni obrazac Factory Method (Proizvodna m etoda). Proizvodna m etoda pravi objekat odgovarajueg tipa, a moe se pozivati polim orfno. Evo jednostavnog prim era u kom e se koristi proizvodna m etoda c reate( ) interfejsa Proizvodjac:
//: p o d acio tip u /p ro izvo d jac/Pro izvo d jac.ja va package po d acio tip u .p ro izvo d jac; p ub lic in te rfa c e Proizvodjac<T> { T c r e a t e () ;

} / / / :

Poglavlje 14: Podaci o tipu

457

Generiki p aram etar T znai da m etoda create( ) m oe da vraa razliit tip rezultata za razne realizacije interfejsa Proizvodjac. U ovom prim eru, osnovna klasa Deo sadri listu objekata tipa Proizvodjac. D odavanjem u listu proizvodjaciDelova, u osnovnoj klasi se ,,registruju proizvodni interfejsi za tipove koje e praviti m etoda createR andom ( ):
//: p o d a cio tip u /R e g is tro v a n iP ro iz vo d ja c i. ja v a // R egistro vanje proizvodnih klasa u osnovnoj k la s i. import p o d a cio tip u .p ro iz vo d ja c .*; import j a v a . u t i l .* ; c la s s Oeo ( pu b lic S trin g to S tr in g () { return g etC lass().g etSim p le N am e();

}
s t a t ic List< Proizvodjac< ? extends D e o pro izvo djaciD elo va = new A rrayList< Proizvodjac< ? extends D e o ( ) ; s t a t ic { // C o lle c tio n s .a d d A ll() daje upozorenje "unchecked generic // a rra y cre atio n . . . fo r varargs parameter" /'/ ("neprovereno p ra v lje n je generikog niza . . . // za parametar prom enljive d u in e "). proizvodjaciD elova.add(new P r e c is t a c G o r iv a .P r o iz v o d ja c O ); proizvodjaciD elova.add(new P re c is ta c V a z d u h a .P ro iz v o d ja c O ); proizvodjaciD elova.add(new PrecistacVazduhaZaKabinu. P ro iz v o d ja c ( ) ) ; proizvodjaciD elova.add(new P r e c is t a c U lja .P r o iz v o d ja c ()) ; proizvodjaciD elova.add(new K a is V e n tila to r a .P r o iz v o d ja c O ); proizvodjaciD elova.add(new K a is S e rv o V o la n a .P ro iz v o d ja c O ); proizvodjaciD elova.add(new K a is G e n e ra to ra .P ro iz v o d ja c ()) ;

}
p riv a te s t a t ic Random slu ca ja n = new Random(47); p ub lic s t a t ic Deo createRandom() { in t n = s lu c a ja n . n e x tIn t(p r o iz v o d ja c iD e lo v a .s iz e ()) ; return p ro iz v o d ja c iD e lo v a .g e t (n ).c r e a t e ();

} }
c la s s F i l t a r extends Deo {} class P re cista c G o riva extends F i l t a r { // P r a v lje n je proizvodne klase za svaki konkretni t ip : p ublic s t a t ic c lass Proizvodjac implements p o d acio tip u .p ro izvo d jac.Pro izvo d jac< PrecistacG o riva> { p u b lic Pre cista c G o riva c r e a t e () { retu rn new P r e c is t a c G o r iv a (); }

} }
c lass PrecistacVazduha extends F i l t a r { public s t a t ic c lass Proizvodjac

458

Misliti na Javi

implements p od aciotipu.proizvodjac.Proizvodjac< PrecistacVazduha> { p ub lic PrecistacVazduha c re a te {) { return new P re cista c V a z d u h a {); }

} }
c lass PrecistacVazduhaZaKabinu extends F i l t a r { p ub lic s t a t ic c la s s Proizvodjac implements podaciotipu.proizvodjac.Proizvodjac< PrecistacVazduhaZa Kabinu> { p ub lic PrecistacVazduhaZaKabinu c re a te () { retu rn new PrecistacVazduhaZaKabinu{);

i } }
c la s s P r e c is ta c U lja extends F i l t a r { p ub lic s t a t i c c la s s Proizvodjac implements p o d a cio tip u .p ro izvo d jac.P ro izvo d jac< P recistacU lja> { p u b lic P r e c is ta c U lja c re a te () { return new P r e c i s t a c U lj a ( ) ; }

} }
c lass Kais extends Deo {} c la s s K a is V e n tila to ra extends Kais { pu b lic s t a t i c c la s s Proizvodjac implements po daciotipu . p ro izvo d ja c.Pro izvo d jac< K aisV en ti1atora> { public K a is V e n tila to ra c re a te () { return new K a is V e n ti1a t o r a ( ) ; }

} }
c lass KaisGeneratora extends Kais { pu b lic s t a t i c c la s s Proizvodjac implements pod aciotipu.proizvodjac.Proizvodjac< KaisG eneratora> { public KaisGeneratora c re a te () { return new K a is G e n e ra to ra ();

} } }
c lass KaisServoVolana extends Kais { p ub lic s t a t i c c la s s Proizvodjac implements p od acio tip u . proizvodjac.Proizvodjac< KaisServoVolana> { p u b lic KaisServoVolana c re a te f) { return new K a is Se rv o V o la n a ();

} } }
public c la s s R e g is tro v a n iP ro iz v o d ja c i{ p ub lic s t a t i c void m a in (S trin g [] args) {

Poglavlje 1 4: Podaci o tipu

459

f o r ( i n t i = 0; i < 10; i++) Syste m .o u t.p rin tln (D e o .createR an d o m ());

}
} /* Is p is : KaisGeneratora PrecistacVazduhaZaKabinu Kai sGeneratora PrecistacVazduha KaisServoVolana Preci stacVazduhaZaKabi nu Pre cista c G o riva KaisServoVolana KaisServoVolana Preci stacG oriva

* ///:Neke klase iz hijerarhije nisu u listi: Filtar i Kais su sam o klasifikatori, pa nikad ne pravim o objekte tih klasa, nego sam o njihovih potklasa. Klase koje m etoda createRandom( ) treba da uzm e u obzir sadre un utranju klasu Proizvodjac. Kao to vidite, osnovni interfejs Proizvodjac koristi se tako to se navede p o tp u n o ime podaciotipu.proizvodjac.Proizvodjac i tako se izbegava dvosmislenost. Prilikom pravljenja liste objekata nism o koristili m etodu CoIIections.addAll( ), zato to bi to izazvalo greku generic array creation (pravljenje generikog niza, to e biti objanjeno u poglavlju Generiki tipovi). Umesto nje, koristio sam m etodu a d d ( ). Metoda createR andom ( ) nasum ino bira objekat iz liste proizvodjaciDelova i poziva njegovu m etodu create( ) da napravi nov Deo.

Veba 14: (4) I konstruktor je svojevrsna proizvodna m etoda. Prepravite program RegistrovaniProizvodjaci.java tako da se um esto eksplicitnog navoenja im ena proizvoaa, objekat klase smeta u listu, i svaki se objekat pravi m etodom new lnstance( ). Veba 15: (4) Realizujte novu kiasu PravIjenjeLjubimaca koristei Registrovane Proizvodiace i prepravite Fasadu Ljubimci tako da upotrebljava nju, a ne druge dve. Postarajte se da ostali prim eri u kojima se upotrebljava program Ljubimci.java i dalje rade ispravno.
Veba 16: (4) Izmenite hijerarhiju Kafa u poglavlju Generiki tipovi tako da se koriste Registrovani Proizvodjaci.

Poreenje instanceof sa ekvivalencijama klase


Kada traite inform acije o tipu, postoji vana razlika izm eu oba oblika naredbe instanceof (tj. instanceof ili islnstance(), koje daju istovetne rezultate) i direktnog poredenja objekata tipa Class. Evo prim era koji ilustruje razliku:
//: podaciotipu/SrodnostProtivTacnogTipa.java // R azlik a izmeu in stan ceof i e k v iv a le n c ije package podaciotipu; import s t a t ic n e t.m in d v ie w .u til. P r in t . * ;

460

Misllti na Javi

c lass Osnovna { } c la s s Izveena extends Osnovna {} p ub lic c la s s SrodnostProtivTacnogTipa { s t a t ic void te s t(O b je c t x) { p r in t (" T e s t ir a n je x tip a " + x .g e t C la s s ( )) ; p r in t (" x in stan ceo f Osnovna " + (x in stan ceo f Osnovna)); p r in t (" x in stan ceo f Izvedena " + (x in stan ceo f Iz ved en a)); p rin t("O s n o v n a .is In s ta n c e (x ) " + O s n o v n a .c la s s .is ln s ta n c e (x )); p r in t("lz v e d e n a .is ln s t a n c e (x ) " + Iz v e d e n a .c la s s .is ln s ta n c e (x )); p r in t (" x .g e t C la s s () = = O snovna.class " + (x .g e tC la s s () = = O s n o v n a .c la s s )); p r in t (" x .g e t C la s s () = = Iz ved en a.class " + (x .g e tC la s s () = = Iz v e d e n a .c la s s )); p r in t("x .g e tC la s s (),e q u a ls (O s n o v n a .c la s s )) " + (x .g e tC la s s ().e q u a ls (O s n o v n a .c la s s ))); p r in t("x .g e t C la s s ().e q u a ls (Iz v e d e n a .c la s s )) " + (x .g e t C la s s ().e q u a ls (Iz v e d e n a .c la s s )));

}
p ub lic s t a t i c void m a in (S trin g [ ] args) test(new O sno vn aO ); test(new Iz v e d e n a ()); {

}
} /* Is p is : T e s tira n je x tip a c la s s Osnovna x in stan ce o f Osnovna tru e x in stan ce o f Izvedena fa ls e O sn o vn a.isln stan ce(x ) tru e Iz v e d e n a .isln sta n ce (x ) fa ls e x .g e tC la s s () = = Osnovna.class tru e x .g e tC la s s () = = Iz ved en a.class fa ls e x .g e tC la s s (),e q u a ls (O s n o v n a .c la s s )) true x .g e tC la s s (),e q u a ls (Iz v e d e n a .c la s s )) fa ls e T e s tira n je x tip a c la s s Izvedena x in stan ce o f Osnovna tru e x in stan ce o f Izvedena tru e O sn o vn a.isIn stan ce(x ) tru e Iz v e d e n a .is ln s ta n c e (x ) tru e x .g e tC la s s () = = Osnovna.class fa ls e x .g e tC la s s () = = Iz ve d e n a.class true x .g e tC la s s ().e q u a ls (O s n o v n a .c la s s )) fa ls e x .g e tC la s s (),e q u a ls (Iz v e d e n a .c la s s ) true

* ///:M etoda test() proverava tip, pri em u njen argum ent koristi oba oblika instanceof. Potom ita referencu na Class i koristi = = i equals() za ispitivanje jednakosti objekata tipa Class. Kao to je i oekivano, instanceof i islnstance() daju istovetne rezultate, isto kao i equals() i ==. M edutim , sami testovi dovoe do razliitih zakljuaka. Saglasno s pojm om tipa, instanceof pita: ,,Da li je ovo tvoja klasa, ili klasa izvedena iz nje? S druge strane, ako stvarne objekte tipa Class uporedite pom ou operatora = =, tu nasteivanje ne igra nikakvu ulogu: objekti su ili istovetnog tipa ili nisu.

Poglavlje 14: Podaci o tipu

461

Refleksija: informacije o klasi u vreme izvravanja


Ako ne znate kog je tipa objekat, saznaete iz prepoznavanja tipa tokom izvravanja. Meu tim , postoji jedno ogranienje: da biste tip mogli da otkrijete tokom izvravanja i da biste uradili neto korisno s tom inform acijom , on m ora biti poznat u vreme prevoenja. D rugim reima, prevodilac m ora da zna za sve klase s kojima radite kako bi prepoznao tip tokom izvravanja. Na prvi pogled to ne deluje kao veiiko ogranienje, ali pretpostavim o da ste dobili referencu na objekat koji ne pripada prostoru vaeg program a. Zapravo, klasa objekta ak nije ni do stu pn a program u tokom prevoenja. Na prim er, zam islim o da ste dobili gom ilu bajtova iz datoteke s diska ili ste ih preuzeli iz m ree, a reeno vam je da oni predstavljaju klasu. Poto prevodilac ne m oe da zna za tu klasu dok prevodi kod, kako uopte moete da je koristite? U tradicionalnom program erskom okruenju ovo izgleda kao malo verovatno. M eutim , kako irim o vidike program iranja, sreemo vane sluajeve u kojima se to deava. Prvi sluaj je program iranje kom ponenata, gde se projekti prave u okruenju za brzo razvijanje aplikacija (engl. Rapid Application Development, RAD), pom ou alatke za pravljenje aplikacija u integrisanom razvojnom okruenju (engl. Integrated Development Environment, IDE). To je vizuelni pristup pravljenju program a (koji se na ekranu prikazuje kao obrazac), a sastoji se od prem etanja po obrascu ikonica koje prcilstavljaju kom ponente. Te k om ponente se zatim konfiguriu podeavanjem nekih param ctara u vreme program iranja. Pri konfigurisanju tokom projektovanja, m ora biti omogiu no da se svim kom ponentam a dodeljuju instance, da svaka m oe prikazivati svoje deiove i da dozvoljava itanje i postavljanje svojih vrednosti. Osim toga, kom ponente koje obrauju grafike dogaaje m oraju da prikazuju inform acije o odgovarajuim m etodam a : ;ko bi razvojno okruenje pom oglo program eru u redefinisanju m etoda za obradu dogudaja. Refleksija obezbeuje m ehanizam za otkrivanje postojeih m etoda u klasi i daje imena tih m etoda. Java obezbeuje stru k tu ru za program iranje u kom ponentam a pom ou tehnologije zrna Jave (engl. Java Beans) koja je opisana u poglavlju Grafika korisnika okruenja. Jo jedan znaajan m otiv za otkrivanje inform acija o klasi u vreme izvravanja jeste om oguavanje pravljenja i pokretanja objekata na udaljenim )_latform am a u mrei. To se zove daljinsko pozivanje metoda (engl. Remote M ethod Invocation, RMI) a om oguuje Javinom p ro gram u da koristi objekte koji su ratrkani na raznim raunarim a. Ratrkanost objekata je opravdana: na prim er, m oda izvravate zadatak s m nogo izraunavanja, pa elite da ga podelite i poaljete delove besposlenim raunarim a kako biste ubrzali izvravanje. Ponekad ete hteti da odreenom raunaru poaljete kod koji obrauje odreene vrste zadataka (npr. poslovna pravila" u vieslojnoj kljent/sei ver arhitekturi), pa taj raunar postaje zajednika ostava koja opisuje akcije i moe se lako prilagoditi tako da utie na sve u sistem u. (To je zanimljiv napredak, poto je svrha raunara iskljuivo da olakava prom ene softvera!) Uzgred, distribuirana obrada podrava i specijalizovan hardver koji m oda odgovara odreenom zadatku, npr. inverziji matrice, ali nije pogodan ili je preskup za program iranje opte nam ene.

462

Misliti na Javi

Klasa Class, opisana ranije u poglavlju, podrava pojam refleksije, a postoji i dodatna biblioteka java.lang.reflect s klasam a Field, M ethod i C onstructor (pri em u svaka realizuje interfejs Member). O bjekte tih tipova pravi Javina virtuelna m aina tokom izvravanja, da bi predstavila odgovarajueg lana nepoznate klase. Zatim m oete koristiti konstruktore da biste pravili nove objekte, m etode get() i set() za itanje i m enjanje polja povezanih sa objektim a tipa Field i m etodu invoke() za pozivanje m etode povezane s objektom tipa M ethod. Pored toga, m oete da pozovete m etode getFields(), getMethods(), getConstructors() itd. da biste dobili nizove objekata koji predstavljaju polja, m etode i konstruktore. (Vie o ovom e nai ete u dokum entaciji o klasi Class u Javinom razvojnom okruenju, JDK.) Znai, inform acije o klasi an onim nih objekata m ogu se u p otpunosti otkriti tokom izvravanja, a nita ne m ora da se zna tokom prevoenja. Vano je shvatiti da refleksija nije nita posebno. Kada koristite refleksiju za rad sa objektim a nepoznatog tipa, Javina virtuelna m aina e proveriti da li neki objekat pripada odreenoj klasi (kao p ri obinom p ostu p ku prepoznavanja tipa u vreme izvravanja), ali zatim , pre nego to uradi bilo ta drugo, m ora da uita objekat tipa Class. Stoga datoteka .class za taj tip i dalje m ora da bude d o stu pna Javinoj virtuelnoj m aini, bilo na lokalnom sistem u ili u mrei. Prem a tom e, prava razlika izm eu prepoznavanja tipa u vreme izvravanja i refleksije lei u tom e to u prvom nainu prevodilac otvara i ispituje datoteku .class tokom prevoenja. D rugim reima, m oete da pozovete sve m etode objekta na uobiajen nain. Pri refleksiji, datoteka .class nije dostupna tokom prevoenja; nju otvara i ispituje izvrno okruenje.

itanje metoda klase


Retko e biti potrebno da direktno koristite refleksivne alatke; one postoje u jeziku da bi podravale pravljenje dinam inijeg koda. Refleksija je stavljena u jezik da bi porala druge Javine m ogunosti kao to su serijalizacija objekata i zrna Jave (obraena u nastavku knjige). M eutim , postoje prilike kada je m ogunost dinam ikog izdvajanja inform acija o klasi korisna. Jedna izuzetno korisna alatka je ita m etoda klase. Izvorni kod s definicijom klase ili dokumentacija na Webu prikazuju samo m etode koje su definisane ili redefinisane unutar definicije te klase. Ko zna koliko jo dostupnih m etoda postoji 11 osnovnim klasama. Njihovo pronalaenje je zam orno i oduzim a m nogo vrem ena.1Sreom, retleksija obezbeuje nain za pravljenje jednostavne alatke koja autom atski prikazuje ceo interfejs. Evo kako ona radi:
//: podaciotipu/Pri kaziM etode.java // Primena r e f le k s ije za p rik a z iv a n je svih metoda k lase, // ak i kada su d efin isan e u osnovnoj k la s i. // {Args: PrikaziM etode} import j a v a .la n g .r e f le c t .* ; import ja v a .u t il.r e g e x .* ; import s t a t ic n e t.m in d v ie w .u til. P r i n t . * ;

N a ro ito ra n ije . M e u tim , k o m p a n ija S u n je p rili n o p o b o lj a la H T M L c io k u m e n ta ju za Javu, pa se m e to d e o s n o v n e klase la k e p ro n a la z e .

Poglavlje 14: Podaci o tipu

463

p u b lic c la s s PrikaziM etodef p riv a te s t a t i c S trin g upotreba = "upotreba: \n" + "PrikaziM etode puno.im e.klase\n" + Da bi se p rik a z a le sve metode klase i l i : \n" + "PrikaziM etode puno.im e.klase re\n" + "Da bi se pronasle metode na osnovu ' r e c i ' " ; p riv a te s t a t ic Pa tte rn uzorak = Pa t.te rn .co m p ile("\\w + \\."); p u b lic s t a t ic void m a in (S trin g [ ] args) { if (a r g s .le n g th < 1) { p rin t(u p o tre b a ); S y s te m .e x it(0 );

}
in t redovi = 0; try { Class<?> c = C la ss.fo rN am e(a rg s[0 ]) ; Method[ ] metode = c.g etM eth o d s(); C on stru cto rf ] c to rs = c .g e tC o n s tru c to rs (); if (a r g s .le n g t h = = 1) { fo r (Method metoda : metode) p r in t ( p.matcher(metoda. t o S t r i n g O ) . repl aceAl 1 ( " " ) ) ; fo r(C o n stru c to r c to r : c to rs ) p r in t(p .m a tc h e r (c to r .t o S t r in g ()).r e p la c e A ll ( " " ) ) ; redovi = metode.length + c to rs .le n g th ; } e ls e { for(Method metoda : methoda) if (m e t o d a .t o S t r in g (). in dex O f(args[1 ]) != -1) { p r in t ( p.m atcher(m eto d a.to Strin g O ) .rep laceA l 1 ( " " ) ) ; redovi ++;

}
fo r(C o n stru c to r c to r : c to rs ) i f ( c t o r . to S t r in g ( ) . indexOf(a rg s [1 ]) != -1) { p rin t(p .m a tc h e r( c to r. t o S t r i n g ( ) ) . repl aceAl 1 ( ' " ' ) ) ; redovi++;

} }
} c a tc h (C1assNotFoundHxception e) { p rin t("N e p o sto ji takva k la sa: " + e );

} }
} /* Is p is : p u b lic s t a t ic void m a in (S t r in g []) p u b lic n a tiv e in t hashCodeO p u b lic fin a l n a tiv e Class g e tC la s s() pu b lic f in a l void w a it (1o n g ,in t) throws InterruptedException p u b lic f in a l void w a it () throws InterruptedException p u b lic fin a l n a tiv e void w a it(lo n g ) throws InterruptedException

464

Misliti na Javi

pu b lic boolean equals(O b ject) p u b lic S trin g to S trin g O p ub lic fin a l n ative void n o t if y ( ) p u b lic f in a l n ative void n o t if y A U () p ub lic PrikaziM etodeO

* ///:M etode getMethods() i getC onstructors() klase Class vraaju niz objekata tipa Method odnosno Constructor. Svaka od ovih klasa im a dalje m etode za izdvajanje im ena, argum enata i povratnih vrednosti m etoda koje predstavljaju. M eutim , za dobijanje znakovnog niza s potpunim potpisom m etode moete da koristite sam o m etodu toStringO, kao to je ovde uinjeno. O statak koda slui samo za izdvajanje inform acija s kom andne linije, za utvrivanje da li odreeni potpis odgovara ciljnom znakovnom nizu (korienjem funkcije indexOf()) i za odbacivanje kvalifikatora im ena p om ou regularnih izraza (koji su predstavljeni u poglavlju Znakovni nizovi). Poto rezultat koji daje Class.forName() ne m oe da bude poznat u vrem e prevoenja, sve inform acije o potpisu m etode izdvajaju se tokom izvravanja. Ako potraite objanjenje refleksije u dokum entaciji na Webu, videete da im a dovoljno podrke za stvarno podeavanje i pozivanje m etode za objekat koji je p o tp u n o nepoznat tokom prevoenja (prim era za ovo bie u nastavku knjige). lako ovo m oda nikada neete m orati da radite sami, vrednost pune refleksije um e da iznenadi. G ornje rezultate je proizvela kom andna linija:
ja v a PrikaziM etode PrikaziM etode

Ovo daje listing koji sadri podrazum evani javni konstruktor, m ada se u kodu vidi da nije definisan nikakav konstruktor. K onstruktor koji vidite autom atski pravi prevodilac. Ako potom klasu PrikaziMetode pretvorite u nejavnu (dakle, s paketnim p ristupom ), napravljen podrazum evani konstruktor se vie nee pojavljivati u rezultatu program a. N apravljenom podrazum evanom konstruktoru se autom atski dodeljuje isti nivo pristupa kao klasi. Bilo bi zanimljivo pozvati i java P rikaziM etodc java.lan g .S trin g uz dodatni argum ent tipa char, in t, S trin g itd. Ova alatka zaista moe da utedi vrem e prilikom program iranja, kada ne m oete da se setite da li klasa ima neku m etodu, a ne elite da pregledate hijerarhiju klasa u dokum entaciji na Webu, ili ne znate da li ta klasa, prim era radi, moe da radi neto sa objektiina tipa Color. Poglavlje Grafika koristiika okruenja sadri grafiku verziju ovog program a (prilagoenu za izdvajanje inform acija za Swing kom ponente), pa m oete da je izvravate dok piete kod da biste brzo pronali ono to vam treba.

Veba 17: (2) Izm enite regularan izraz u program u PokaziMetode.java tako da se uklanjaju i rezervisane rei native i final. Uputstvo: upotrebite o perator I (OR). Veba 18: (1) Izm enite klasu PokaziMetode tako da ne bude javna i dokaite da se napravljen podrazum evani konstruktor vie ne pojavljuje u rezultatu program a.

Poglavlje 14: Podaci o tipu

465

Veba 19: (4) U program u Ispitivanjelgracaka.java, u potrebite refleksiju za pravljenje objekta tipa Igracka pom ou nepodrazum evanog konstruktora. Veba 20: (5) U dokum entaciji JDK na adresi http://java.sun.com potraite interfejs za java.lang.Class. Napiite program koji im e klase p rim a kao argum ent kom andne linije, zatim Class m etodam a ispisuje sve inform acije dostupne za tu klasu. Ispitajte program
pom ou neke klase iz standardne biblioteke i neke klase koju ste sam i napravili.

Dinamiki posrednici
Projektni obrazac Proxy (Posrednik) jedan je od osnovnih projektnih obrazaca. To je objekat koji umeete um esto ,,pravog objekta da biste obavili neke dopunske ili razliite operacije m eu njim a je obino i kom unikacija sa odreenim pravim " objektom . S truk turu projektnog obrasca Proxy pokazaem o n a jed n o m trivijalnom prim eru:
//: podaciotipu/ProstPrim erZaPosrednika.java import s t a t ic n e t.m in d v ie w .u til. P r i n t . * ; in te rfa c e In t e r fe js { void u ra d iN e s to (); void nestoDrugo(String a r g );

1
c la s s PraviO bjekat implements In t e r f e js { p u b lic void u rad iN esto() { p rin t("u ra d iN e s to ') ; } p u b lic void nestoDrugo(String arg) { print("nestoD rugo " + a rg );

} }
c lass JednostavanPosrednik implements In t e r f e js { p riv a te In t e r fe js imaposrednika; p ub lic JednostavanPosrednik (In t e r f e js im aposrednika) { this.im aposrednika = imaposrednika;

}
p ub lic void u rad iN esto() { p rin t("Jed no stavan Po sred n ik u ra d iN e s to "); im aposrednika.uradiNestoO ;

}
p ub lic void nestoDrugo(String arg) { p r i n t ( " JednostavanPosrednik nestoDrugo " + a r g ) ; im aposrednika.nestoDrugo(arg);

} }
c la s s ProstPrim erZaPosrednika { p u b lic s t a t ic void p o tr o s a c (In te r fe js i f e j s ) if e js .u r a d iN e s t o ( ) ; i f e j s . nestoDrugo("bonobo");

466

Misliti na Javi

p u b lic s t a t i c void m a in (S trin g [] args) ( potrosac(new P r a v iO b je k a tO ); potrosac(new JednostavanPosrednik(new P r a v iO b je k a t ()));

}
} /* Is p is : uradiNesto nestoDrugo bonobo JednostavanPosrednik uradiNesto uradiNesto JednostavanPosrednik nestoDrugo bonobo nestoDrugo bonobo

* ///:Poto p o tro sac( ) prim a jedan Interfejs, on ne m oe znati da li je dobio PraviObjekat ili objekat tipa JednostavanPosrednik, jer oba realizuju Interfejs. Ali JednostavanPosrednik, koji je um etnut izm eu klijenta i objekta PraviObjekat obavlja operacije i zatim poziva identinu m etodu za PraviObjekat. Projektni obrazac Proxy je podesan kad god neke dodatne operacije treba izdvojiti od pravog objekta" i naroito kada hoete da olakate prelaz od nekorienja tih dodatnih operacija na korienje i obrnuto (svrha projektnog obrasca jeste kapsuliranje prom ena - da biste opravdali upotrebu obrasca, neto m orate da menjate). Na prim er, kako biste pratili pozive m etoda u obiektu PraviObjekat ili merili reijske trokove tih poziva? Takvom kodu nije m esto u gotovoj aplikaciji, pa Proxy om oguuje da ga lako dodate i uklonite. Javin dinamiki posrednik (engl. dynamicproxy) podie ideju upotrebe posrednika na vii nivo, poto objekat posrednika pravi dinam iki i isto tako obrauje pozive posredovanih m etoda. Svipozivi dinam ikogposrednika bivaju preusm ereni na jedini blokza pozive (engl. invocation handler) koji prepoznaje poziv i odluuje ta s njim da radi. Preradio sam ProstPrim erZaPosrednika.java tako da posrenik bude dinamiki:
//: podacio ti pu/ProstDi nami cki Posredni k.ja va import j a v a . 1a n g .r e f le c t .* ; c la s s BlokDinamickogPosrednika implements InvocationH andler { p riv a te O bject imaposrednika; pu b lic B1okDinamickogPosrednika(0bject imaposrednika) { this.im aposrednika = imaposrednika;

}
public Object invoke(O bject posrednik, Method metoda, O b je c t[] argumenti) throws Throwable { S y s te m .o u t.p rin tln ("* * * * posrednik: " + posrednik .g e tC la s s () + " , metoda: " + metoda + " , argumenti: " + argum enti); if(argu m en ti ! = n u ll) fo r(O b je ct arg : argumenti) S y s te m .o u t.p rin tln (" " + a r g ); return m etoda.invoke(im aposrednika, argum enti);

} }

Poglavlje 14: Podaci o tipu

467

c la s s ProstDinam ickiPosrednik { p ub lic s t a t ic void p o tr o s a c (In te r fe js i f e j s ) if e js .u r a d iN e s t o ( ) ; ifejs.n e sto D ru g o ("b o n o b o "); {

}
p ub lic s t a t ic void m a in (S trin g [] args) { PraviO bjekat pravi = new P r a v iO b je k a t(); p o tro s a c (p ra v i) ; // Umetni posrednika i pozovi ponovo: In t e r f e js posrednik = (In te rfe js )P ro x y.n e w P ro x yIn sta n c e ( In t e r fe js .c la s s .g e t C la s s L o a d e r (), new C la s s [ ]{ In t e r f e js .c la s s } , new B1okDi nami ckogPosredni k a (p ra v i) ) ; p o tro sac(p o sre d n ik );

}
} /* Is p is : (95% podudaranja) uradiNesto nestoDrugo bonobo ** ** posrednik: c la s s $ProxyO, metoda: pu b lic a b s tra ct void In t e r f e js .u r a d iN e s t o (), argumenti: null uradiNesto **** posrednik: c la s s $ProxyO, metoda: pu b lic a b s tra ct void In te rfe js .n e s to D ru g o (ja v a .la n g .S trin g ), argumenti: [ Ljava.lang.0bject;@ 42e816 bonobo nestoDrugo bonobo

* ///:Dinam ikog posrednika pravite pozivom statine m etode Proxy.newProxy-Instance( ) koja zahteva uitava klase (po pravilu, moete joj dati uitava klase nekog ve uitanog objekta), listu interfejsa (ne klasa niti apstraktnih klasa) koje posrednik treba da realizuje i realizaciju interfejsa InvocationHandler. Dinam iki posrednik preusm erava sve pozive bloku za pozive, pa konstruktor bloka za pozive obino dobija referencu na ,,pravi objekat, da bi mogao da prosleuje zahteve nakon to obavi svoj posredniki posao. M etoda in v o k e () dobija objekat posrednika, za sluaj da hoete da saznate odakle je poziv doao - to je retko potrebno. M eutim , budite oprezni kada iz m etode invoke( ) pozivate m etode posrednika, poto se pozivi kroz interfejs preusm eravaju kroz posrednik. Po pravilu, obaviete posredovanu operaciju i zatim upotrebiti M ethod.invoke( ) za prosleivanje zahteva objektu koji ima posrednika, prosleujui m u potreb n e argumente. M oda e vam to isprva izgledati ograniavajue inie se da m oete obavljati sam o generike operacije. M eutim, pozive odreenih m etoda moete izdvojiti filtrom, dok ete druge m etode sam o proslediti:
//: podaciotipu/IzborM etoda.java // Traenje odreenih metoda u dinamikom posredniku. import j a v a .la n g .r e f le c t .* ; import s t a t i c n et.m in d view .u til . P r in t . * ;

468

Misliti na Javi

c la s s SelektorMetoda implements InvocationH andler { p riv a te Object imaposrednika; p u b lic SelektorM etoda(O bject imaposrednika) { this.im aposrednika = imaposrednika;

)
p u b lic Object invoke(O bject posrednik, Method metoda, O b je c t[] argumenti) throws Throwable { i f (metoda.getName() .equals(''zanim l j i v o " ) ) p rin t("P o s re d n ik j e o tk rio za n im ljivu metodu"); return m etoda.invoke(im aposrednika, argum enti);

} }
in te rfa c e NekeMetode { void d o sad n alO ; void dosadna2(); void z a n im ljiv a (S tr in g a r g ); void z a n im ljiv a 3 ( ) ;

}
c la s s R e a liz a c ija implements NekeMetode { p ub lic void dosadnal() { p r in t("d o s a d n a l"); } p u b lic void dosadna2() { p rin t("d o s a d n a 2 "); } p u b lic void z a n im ljiv a (S tr in g arg) { p rin t("z a n im ljiv a " + a r g );

}
p u b lic void dosadna3() { p rin t("d o s a d n a 3 "); }

}
c lass IzborMetoda { p ub lic s t a t ic void m a in (S trin g [] args) { NekeMetode posrednik= (NekeMetode)Proxy.newProxyInstance( NekeMetode.class. g e tC la s s L o a d e r(), n e w C la s s []{ NekeMetode.class } , new IzborMetoda(new R e a li z a c i j a ( ) ) ) ; posrednik.dosadnal( ) ; posredni k.dosadna2 (); posrednik.zanim lj i va ("b o n o b o "); posredni k.dosadna3 ();

}
} /* Is p is : dosadnal dosadna2 Posrednik je o tk rio za n im ljivu metodu za n im ljiva bonobo dosadna3

* ///:-

Poglavlje 14: Podaci o tipu

469

Ovde su nas zanim ala sam o im ena m etoda, ali vi moete traiti i druge delove potpisa m etoda, pa ak i vrednosti odreenih argum enata. D inam iki posrednik nije alatka koju ete upotrebljavati svakog dana, ali neke vrste problem a njom e m oete veoma lepo da reite. O projektnom obrascu Proxy i drugim projektnim obrascim a vie ete saznati u knjigam a Thitiking in Patterns (posetite www.MindView.net) i Design Patterns, autor je Erich G am m a i dr. (Addison-Wesley, 1995).

Veba 21: (3) Prepravite program ProstPrim erZaPosrednik.java tako da m eri vrem ena poziva m etoda. Veba 22: (3) Prepravite program ProstDinam ickiPosrednik.java tako da m eri vrem ena
poziva m etoda.

Veba23: (3) U nutar m etode invoke( ) u program u ProstDinamickiPosrednik.java, pokuajte da ispiete argum ent posrednik i objasnite ta se deava. Projekat:2 Napiite sistem u kojem dinam iki posrednici realizuju transakcije, gde posrednik obavlja potvrivanje ako je posredovani poziv bio uspean (nije generisao izuzetke), o dno sno ponitavanje ako je poziv zakazao. Potvrivanje i ponitavanje se obavljaju na spoljnoj tekstualnoj datoteci koja je izvan kontrole Javinih izuzetaka. M oraete da pazite na atomizovanost (usitnjenost) operacija.

Null objekti
Kada ugraenu null referencu upotrebljavate da biste naznaili kako odreeni objekat ne postoji, svaki p u t kada dobijete neku referencu, m orate proveriti da li je jednaka null. To um e da postane veom a zam orno i da proizvede zam oran kod. Problem je to to null referenca nem a svoje ponaanje, sem to proizvodi NullPointerException ako ita pokuate s njom da uradite. Katkada je korisno uvesti Null objekat* koji prim a poruke za objekat koji ,,zam enjuje, ali vraa vrednosti koje pokazuju da nem a ,,pravog objekta. O nda biste smeli pretpostaviti da su svi objekti validni pa ne biste gubili program sko vrem e proveravajui reference na n u ll (i na itanje rezultujueg koda). Iako bi bilo zabavno zamiljati program ski jezik koji autom atski pravi Null objekte za vas, u praksi nem a smisla upotrebljavati ih posvuda ponekad ba treba proveriti da li referenca ukazuje na null, katkada se m oe razborito pretpostaviti da null referenca nee naii, dok im a i sluajeva kada je prihvatljivo otkrivanje greaka preko izuzetka NullPointerException. Izgleda da su NuII objekti najkorisniji kada su blie podacim a, sa objektim a koji predstavljaju entitete u prostoru problem a. Jednostavan prim er je to to m nogi sistemi imaju klasu Osoba, a u kodu ima situacija kada stvarna osoba ne postoji (ili postoji, ali vi jo niste dobili sve podatke o njoj), pa bi se tada tradicionalno koristila null referenca i odgovarajua provera. Umesto toga, m oem o napraviti Null objekat. lako
2 P ro je k ti su p re d lo z i z a s e m e s tra ln e ra d o v e (n a p r im e r ) . Solution g u id e n e s a d r i re e n ja p ro je k a ta . Iz u m ili su ga B o b b y W o o lf i B ru c e A n d e rs o n . To se m o e s m a tr a ti s p e c ija ln im s lu a je m p ro je k tn o g o b ra s c a Strategy (S tra te g ija ). V a rija n ta N u ll objekta je o b ra z a c N u ll iterator k o ji ite rir a n je v o ro v a u k o m p o z itn o j h ije ra rh iji in i n e v id ljiv im za k lije n ta (k lije n t is tu lo g ik u m o e d a u p o tr e b i za ite rira n je k o m p o z itn ih i z a v rn ih v o ro v a ).

470

Misliti na Javi

Null objekat odgovara na sve poruke na koje bi odgovorio i pravi objekat, i alje je potreban nain ispitivanja jednakosti s null. Najjednostavniji nain da se to uradi je istoim eni interfejs:
//: net/m in d view /u til/N u ll. ja va package n e t.m in d vie w .u til; p ub lic in te rfa c e Null { } / / / :

Time bi se om oguilo da in stan c eo f otkrije Null objekat, i to je jo vanije, ne biste m orali da dodajete m etodu is N u ll() svim svojim klasam a (to bi, na kraju krajeva, samo predstavljalo drugaiji nain prepoznavanja inform acija tokom izvravanja - pa zato onda ne bism o koristili ugraeni RTTI?).
//: podaciotipu/Osoba.java // Klasa koja ima Null objekat. import n e t.m in d v ie w .u til.* ; c la s s Osoba { p u b lic fin a l S trin g ime; p ublic fin a l S trin g prezime; p ublic fin a l S trin g adresa; // e tc . p ublic O soba(String ime, S trin g prezime, S trin g ad re sa ){ th is.im e = ime; this.prezim e = prezime; th is.a d re sa = adresa;

}
p ub lic S trin g to S trin g O { return "Osoba: " + ime + " " + prezime + " " + adresa;

}
p ublic s t a t ic c lass NullOsoba extends Osoba implements Null { p riv a te NullOsoba() { super("Nem a", "Nema", "Nema"); } p ublic S trin g to S trin g O { retu rn "N ullOsoba"; }

}
p ublic s t a t ic fin a l Osoba NULL = new N u llO so ba();

} ///:Po pravilu, Null objekat je projektni obrazac Singleton (Singlton), pa je ovde napravljen kao statina finalna instanca. To funkcionie zato to je Osoba nepromenljiva m oete postavljati samo vrednosti u kon stru kto ru , i zatini ih itati, ali ih ne moete m enjati (poto su objekti tipa String po svojoj prirodi neprom enljivi). Ukoliko hoete a prom enite objekat tipa NuIlOsoba, jedino moete da ga zam enite novim objektom tipa Osoba. Imajte u vidu da pom ou rezervisane rei instanceof i dalje moete otkrivati generiki objekat Null ili specifiniji objekat tipa NulIOsoba, ali singltonskim pristupom m oete se ograniiti na korienje m etode e q u a ls( ) ili ak operatora = = za poreenje sa

Osoba.NULL.

Poglavlje 14: Podaci o tlpu

471

Pretpostavim o da ivite u vrem e nastanka Interneta i da ste dobili m nogo kapitala za realizaciju svoje Izvanredne ideje. Sprem ni ste da prim ite osoblje, ali dok ekate da se radna m esta popune, m oete upotrebiti Null objekte tipa Osoba da uvaju m esto za svako

RadnoMesto:
//: podaciotipu/RadnoMesto.java c la s s RadnoMesto { p riv a te S trin g zvanje; p riv a te Osoba osoba; p u b lic RadnoMesto(Stririg Fu n k cija , Osoba zaposleni) { zvanje = Fu n k cija; osoba = zap o sleni; if(o so b a = = n u ll) osoba = Osoba.NULL;

}
p u b lic RadnoMesto(String Fu n k cija ) { zvanje = Fu n k cija ; osoba = Osoba.NULL;

}
p u b lic S trin g d ajZ van jeO { return zvanje; } p u b lic void z a d a jZ v a n je (Strin g novoZvanje) { zvanje = novoZvanje

}
p u b lic Osoba dajOsobu() { retu rn osoba; } p ub lic void zadaj0sobu(0soba novaOsoba) { osoba = novaOsoba; if(o s o b a = = n u ll) osoba = Osoba.NULL;

}
p u b lic S trin g to S trin g O { return "RadnoMesto: " + zvanje + " " + osoba;

} } ///= Uz klasu RadnoMesto ne m oram o da pravim o Null objekat, poto postojanje objekta tipa Osoba.NULL im plicira postojanje praznog objekta tipa RadnoMesto. Mogue je da se kasnije m ora eksplicitno dodati Null objekat za RadnoMesto, ali YAGNI4 ( You Arent Going to Need It, Nee biti potrebno) ui da u prvoj verziji program a pokuate s najjednostavnijim to bi moglo da fu nkon ie i ekate dok vas neki aspekt program a ne natera da dodate neto novo; to je bolje nego da o dm ah pretpostavljate da e to biti neophodno. Kada budete popunjavali radna mesta, klasa Osoblje e moi da trai Null objekte:
//: po dacio tipu /O soblje.java import j a v a . u t i l .* ; p u b lic c la s s O soblje extends ArrayList<RadnoMesto> { p ub lic void ad d (String zvan je, Osoba osoba) {
P rin c ip Ekstrenviogprogramiranja (X P ), k a o to je i N a p ra v i n a jje d n o sta v n iju m o g u u stv ar koja rai.

472

Misliti na Javi

add(new RadnoMesto(zvanje, o so b a));

}
p u b lic void a d d (S t r in g ... zvanja) ( fo r (S tr in g zvanje : zvanja) add(new RadnoM esto(zvanje));

}
p u b lic O s o b lje (S t r in g ... zvanja) { add (zvan ja); } p u b lic boolean slobodnoRadnoMesto(String zvanje) { for(RadnoMesto radnomesto : t h is ) if(ra d n o m e sto .d a jZ v a n je ().e q u a ls(z va n je ) && radnomesto.dajOsobuO = = Osoba.NULL) return tru e; return fa ls e ;

}
p u b lic void popuniRadnoMesto(String zvanje, Osoba z a p o s li) { for(RadnoMesto radnomesto : th is ) i f (radnom esto.dajZvanjeO .equal s(z van je) && radnomesto.dajOsobuO = = Osoba.NULL) { radnom esto.zadajOsobu(zaposli); re tu rn ;

}
throw new RuntimeException ( "RadnoMesto " + zvanje + " n ije slobodno");

}
p u b lic s t a t ic void m a in (S trin g [] args) { O soblje osoblje = new O s o b lje ("P re d se d n ik ", "Tehniki d ir e k to r " , "D ire k to r m arketinga", "D ire k to r proizvo dn je", "Voa p ro je k ta ", "S o ftv e ra ", "S o ftv e ra ", "S o ftv e ra ", "S o ftv e ra ", "In e n je r is p i t i v a n j a " , "P is a c tehnikih te k s to v a "); osoblje.popuni RadnoMesto( Predsedni k " , new Osoba( J a " , "Prezim e", " V r h " ) ); osoblje.popuniRadnoMesto("Voda p ro je k ta ", new Osoba("Denet", P la n e r", " S p r a v ic e " ) ) ; i f(o so b lje.slo b o d n o R a d n o M esto ("So ftvera")) osobl je.po p un iR adn oM esto("Softvera", new Osoba("Bob", "Koder", "S v e tlo g r a d ")) ; S y s te m .o u t.p rin tln (o s o b lje );

}
} /* Is p is : [RadnoMesto: Predsednik Osoba: Ja Prezime Vrh, RadnoMesto: Tehniki d ire k to r NullOsoba, RadnoMesto: D ire k to r marketinga NullOsoba, RadnoMesto: D irekto r proizvodnje NullOsoba, RadnoMesto: Voa projekta Osoba: Denet Planer S p ra v ic e , RadnoMesto: So ftvera Osoba: Bob Koder Svetlo g ra d , RadnoMesto: S o ftve ra NullOsoba, RadnoMesto: So ftvera NullOsoba, RadnoMesto: So ftve ra NullOsoba, RadnoMesto: In e n je r is p it iv a n ja NullOsoba, RadnoMesto: Pisac teh n ik ih tekstova NullOsoba]

* ///:-

Poglav[je 14: Podaci o tipu

473

Vodite rauna o tom e da na nekim m estim a ipak m orate proveravati postoje li Null objekti, to se ne razlikuje ba m nogo od provere null vrednosti. Na drugim m estim a - kao to su to S trin g ( ) konverzije, u ovom sluaju - ne m orate da obavljate dodatne provere; sm ete da pretpostavite kako su sve reference objekata validne. Ako um esto s konkretnim klasama radite sa interfejsim a, m oete u potrebiti DynamicProxy za autom atsko pravljenje Null objekata. Pretpostavim o da im am o interfejs Robot koji definie ime, m odel i List<Operacija> koji opisuju ta Robot um e da radi. O peracija sadri opis i kom andu - to je jedna vrsta obrasca Comtnand (Kom anda):
//: p o d acio tip u /O p eracija.java p u b lic in te rfa c e O peracija { S trin g o p i s ( ) ; void komanda();

} III-U slugama Robota pristupate pozivanjem m etode operacije( ):


//: podaciotipu/Robot.java import j a v a . u t i l . * ; import n e t.m in d v ie w .u til.* ; p u b lic in te rfa c e Robot { S trin g im e (); S t r i ng model ( ) ; List<O peracija> o p e r a c ije (); c la s s Test { p u b lic s t a t ic void test(R ob ot r ) { i f ( r instan ceo f N u ll) System .out. p r i n t l n ("[N u l 1 Robot]" ) ; Sy ste m .o u t.p rin tln ("R o b o t ime: " + r . im e ( ) ) ; Sy ste m .o u t.p rin tln ("R o b o t model: " + r.m o d e lO ); fo r(O p e ra c ija o p era cija : r.o p e ra c ij e ( ) ) { Sy ste m .o u t.p rin tln (o p e ra ci j a . o p i s O ) ; operaci j a . komandaO;

} } }

} III'-U program u je i ugneena klasa za testiranje. Sada m oem o da napravim o Robota koji isti sneg:
/ / : podaci o ti pu/RobotKoj i Ci s t i Sneg.j ava import j a v a . u t i l .* ; p u b lic c la s s RobotKojiC istiSneg implements Robot { p riv a te S trin g ime; p ub lic R o b o tK o jiC is tiSn eg (Strin g ime) { th is .im e = im e;}

4 74

Misliti na Javi

p u b lic S trin g ime() { return ime; } p u b lic S trin g model() { return "SnegoBot S e r ija 11"; } p u b lic List<Operacija> o p e ra c ije O { retu rn A rr a y s .a s L is t ( new O p e racija O { p ub lic S trin g o p is () { return ime + " moe da i s t i sneg";

}
p u b lic void komanda() { Sy ste m .o u t.p rin tln (im e + i s t i sn e g ");

} },
new O p e ra c ija O { p u b lic S trin g o p is () { return ime + " moe da i s t i

le d ";

}
p u b lic void komanda() { Sy ste m .o u t.p rin tln (im e + " i s t i le d " ) ;

} },
new O p e rac ija O { p u b lic S trin g o p is () { return ime + " moe da p o is ti k ro v";

}
p u b lic void komanda() { S y stem .o u t.p rin tln (im e + " p o is ti k ro v ");

} } ); }
pu b lic s t a t i c void m a in (S trin g [] args) { R o b o t.T est.test(n ew RobotKojiCistiSnegC'Sam oRadi" ) ) ;

}
} /* Is p is : Robot ime: SamoRadi Robot model: SnegoBot S e r ij a 11 SamoRadi moe da i s t i sneg SamoRadi i s t i sneg SamoRadi moe da i s t i led SamoRadi i s t i led SamoRadi moe da p o is ti krov SamoRadi p o is ti krov

* ///:Pretpostavljamo da e biti m nogo razliitih tipova Robota i hteli bismo da svaki Null objekat radi neto posebno za svaki tip Robota - u ovom sluaju, sadri podatke o tanom tipu Robota koji Null objekat zamenjuje. Te podatke e hvatati dinamiki posrednik:

Poglavjje )

Podaci o tipu

475

//: p o daciotipu /N ullR obot.java // P r a v lje n je Null objekta pomou dinamikog posredrri import ja v a . la n g . r e f le c t . * ; import j a v a . u t i l .* ; import n e t.m in d v ie w .u til.* ;

a.

c la s s BlokZaObraduNullRobota implements InvocationH anfiler { p riv a te S trin g n u lllm e; p riv a te Robot imaposrednika = new NRobot( ) ; BlokZaObraduNullRobota(Class<? extends Robot> t ip ) nulllm e = tip.getSim pleNam e() + " NullRobot1 1 ;

}
p riv a te c la s s NRobot implements N u ll, Robot { p u b lic S trin g im e() { return n ulllm e; } p u b lic S trin g model() { retu rn n u lllm e; } p ub lic L is tO p e ra c ija > o p e ra c ije O { retu rn C o lle c tio n s .e m p ty L is t();

} }
p u b lic Object invoke(O bject posrednik, Method metoda, O b je c t[] ari throws Throwable { return method.invoke(imaposrednika, argum enti); jn t i)

p u b lic c la s s NullRobot { p ub lic s t a t ic Robot novNullRobot(Class<? extends Robot> t ip ) { return (Robot)Proxy.newProxyInstance( Nul1R o b o t.c la s s .g e tC la s s L o a d e r(), new C la s s [ ]{ N u ll.c la s s , Robot.class } , new B lo kZaO bradu N u llR o bo ta(tip));

}
p u b lic s t a t ic void m a in (S trin g [] args) { Robot[] botovi = { new RobotKoji Ci s tiS n e g ("S n e z a n a "), novNul1R o b o t(R o b o tK o jiC istiSn e g .class)

};
for(Robot bot : botovi) R o b o t.T e s t.te s t(b o t);

}
} /* Is p is : Robot ime: Snezana Robot model: SnegoBot S e r ij a 11 Snezana moe da i s t i sneg Snezana i s t i sneg Snezana moe da i s t i led Snezana i s t i led Snezana moe da p o is ti krov Snezana p o is ti krov

4 76

Misliti na Javi

[Null Robot] Robot ime: RobotKojiCistiSneg NullRobot Robot model: RobotKojiC istiSneg NullRobot

* ///:Kad god vam zatreba prazan (null) objekat tipa Robot, sam o pozovete m etodu novN ullR obot( ) prosleujui joj tip Robota za koji hoete posrednika. Posrednik ispunjava zahteve interfejsa Robot i Null i daje specifino im e tipa za koji posreduje.

Lani objekti i vezivne funkcije


Lani objekat (engl. Mock Object) i vezivna funkcija (engl. Stub) logike su varijacije Null objekta. Kao Null objekat, oni su zam ene za pravi objekat koji e biti upotrebljen u zavrenom program u. M eutim , i lani objekat i vezivna funkcija glume da su ivi objekti koji isporuuju prave podatke, um esto to su inteligentne zam ene za null, kao Null objekat. Lani objekat i vezivna funkcija razlikuju se u stepenu. Lani objekti su uglavnom laki, sami sebe testiraju, i obino se pravi m nogo njih za razne situacije tokom testiranja. Vezivne funkcije vraaju samo zaetke podataka, obino su teki objekti i esto se vie puta upotrebljavaju u raznim testiranjima. Vezivne funkcije se m ogu konfigurisati tako da se izmene u zavisnosti od naina na koji su pozvane. Dakle, vezivna funkcija je sofisticirani objekat koji radi svata, dok se za razne poslove pravi m notvo razliitih malih lanih objekata.

Veba 24: (4) D odajte Null objekte u program RegistrovaniProizvodjaci.java.

Interfejsi i podaci o tipu


Vana svrha rezervisane rei interface jeste da program eru om ogui da izoluje kom ponente i tako sm anji njihov m eusobni uticaj. To se postie pisanjem u interfejse, ali to se tie podataka o tipu, interfejse je m ogue zaobii - oni nisu savreno jem stvo razdvajanja realizacije klase od naina pristupa klasi. Za poetak, evo jednog interfejsa:
//: p o d a cio tip u / in te rfe jsa / A .ja v a package podaci o ti pu. i n t e r f e j s a ; p ub lic in te rfa c e A { void f ( ) ;

} ///:Kada se ovaj interfejs realizuje, videete kako se ipak moe saznati stvarni realizovani tip:
//: p o d a c io tip u / K rs e n je ln te rfe js a .ja v a // Z aob ilaenje in t e r fe js a . import p o d a c io tip u .in t e r fe js a .* ; c la s s B implements A { p u b lic void f ( ) {} p ub lic void g () {}

Poglavlje 14: Podaci o tipu

477

public class Krsenjelnterfejsa { public Static void main(String[] A a = new B ( ) ; a.f(); // a.g(); // Ovo bi izazvalo greku u prevoenju System.out ,println(a.getCl ass() . g e t N a m e O ) ; if(a instanceof B) { B b = (B)a; b.g(); args) {

} }
} /* Ispis: B

* ///:P o m o u p rep o zn a v an ja tip a to k o m izvravanja sazn ajem o d a je a realizovano kao B. Sv o enjem tip a n a B, m o e m o p o zv ati m e to d u k o ja nije u A. O vo je sasvim d o zv o ljen o i prihv atljiv o, ali v a m a m o d a n e od g o v ara da p ro g ra m e ri klijenti to rade, p o to im to daje p rilik u d a se tenje poveu s vaim k o d o m nego to biste vi hteli. D akle, vi m o d a m islite d a vas titi rezerv isana re interface, ali to nije istina, a injen ica da u p o tre b lja v ate B da b iste realizovali A, u o vo m slu aju je zapravo p ita n je javne ta jn e.5 M oete rei da su p ro g ra m e ri sam i krivi ako su o d luili da u p o tre b e klasu u m esto in terfejsa. To je v ero v atn o ta n o u veini sluajeva, ali ako vam ,,verovatno nije dovoljno, m o ete u p o tre b iti stroe k o n tro le. N ajlake reenje je u p o tre b iti p a k e tn i p ristu p za realizaciju tak o da je k lijenti izvan paketa ne vide:
//: po da ci ot ip u/ paketnipristup/SkrivenaC.java package podacioti pu.paketni pristup; import po da ci ot ip u.interfejsa.*; import static net.mi nd vi ew .u t i1 .P r i n t .*; class C implements A { public void f() public void g() void u() { print("javna C . f ( ) ); } { print("javna C .g ()"); } { print("zatiena C .v ()) ; }

{ print("paketni C .u ()"); } { print("privatna C . w ( ) ); }

protected void v() private void w()

}
public class SkrivenaC { public static A napraviA() { return new C(); }

} ///:-

N a ju v e n iji p r im e r o v o g a je o p e ra tiv n i siste m W in d o w s; o n je im a o z v a n i n i API koji je tre b a lo da b u d e k o ri e n za sve, i n e z v a n i a n ali v id ljiv s k u p fu n k c ija k oje su k o ris n ic i m o g li o tk riti i p o z iv a ti. Da bi reavali p ro b le m e , p ro g r a m e ri su u p o tre b lja v a li s k riv e n e A PI fu n k c ije i tim e p rim o ra li M icro so ft da ih o d r a v a k a o d a su d e o z v a n i n o g A P I-ja. T o je p o s ta lo iz v o r v e lik o g tro k a i tru d a za tu k o m p a n iju .

478

Misliti na Javi

Jedini javni d eo ovog p aketa, SkrivenaC, p ro izvo di interfejs A kada je pozovete. ak i kada b iste iz m e to d e napraviA ( ) vraali C, zanim ljivo je da izvan pak eta i dalje n e biste m ogli da koristite nita osim A, p o to im e klase C izvan p ak eta n e m oete n i d a u p otreb ite. A ko sada p o k u ate d a svedete n an ie n a C, to n eete m o i d a u rad ite, p o to izvan paketa n e m a d o stu p n o g tip a C:
//: podaciotipu/SkrivenaRealizacija.java // Zaobilaenje paketnog pristupa. import podaciotipu.interfejsa.*; import podaciotipu.paketnipristup.*; import java.lang.reflect.*; public class SkrivenaRealizacija { public static void main(String[] args) throws Exception { A a = S k r i v e na C. na pr av iA (); a.f 0 ; System.out.println(a.getClass() . g e t N a m e O ) ; // Greka tokom prevoenja: cannot find symbol /* if(a instanceof C) { C c = (C)a; c.g(); 'C':

) */
// Pazi sad! Refleksija ipak omoguava da pozovemo g(): pozivSkriveneMetode(a, pozivSkriveneMetode(a, pozivSkriveneMetode(a, pozivSkriveneMetode(a, "g"); "u"); "v"); "w"); // Pa ak i metode koje su jo manje dostupne!

}
static void pozivSkriveneMetode(Object a, String imeMetode) throws Exception { Method g = a.ge tC la ss () .g et De cla re dM et ho d( im eM et od e); g . s e tA cc es si bl e( tr ue); g .i n v o k e ( a ) ;

)
} /* Ispis: javna C.f() podaciotipu.paketnipristup.C javna C.g() paketni C.u() zatiena C.v() privatna C.w()

* ///:Kao to vidite, i dalje je zb o g refleksije m o g u e pozivati sve m eto d e , ak i privatne! Ako z n ate im e m eto d e k oju h o e te d a pozovete, sam o pozovite setAccessible(true) za taj objek at tipa M ethod, kako pie u definiciji m eto d e pozivSkriveneM etode( ).

Poglavlje 14: Podaci o tipu

479

M oda m islite da ete to spreiti tako to ete d is trib u ira ti sam o ve p rev ed en k o d , ali varate se. D ov oljn o je takav k o d p ro p u s titi k ro z javap, p rev o d ilac u n a z a d k o ji se isporu u je uz JDK. Evo kako izgleda o d g o v araju a k o m a n d n a linija:
javap -private C

In d ik ato r -private kazuje d a treb a p rik azati sve lanove, ak i o n e privatne. Evo rezultata:
class podaciotipu.paketnipristup.C extends java.lang.Object implements podaciotipu.interfejsa.A { podaciotipu.paketnipristup.CO; public void f ( ) ; public void g(); void u ( ) ; protected void v(); private void w ( ) ;

} N a taj n a in , svi m o g u sazn ati im en a i p o tp ise svih vaih (i n ajp riv atn ijih ) m e to d a i pozvati ih. ta e biti ako interfejs realizujete kao p riv a tn u u n u tra n ju ldasu? Evo kako to izgleda:
//: podaciotipu/UnutranjaRealizacija.java // Privatne unutranje klase ne mogu se sakriti od refleksije. import podaciotipu.interfejsa.*; import static net.mind vi ew .u ti l.Print.*; class UnutrasnjaA { private static class C implements A { public void f() { print("javna C .f ()"); } public void g() void u() { print("javna C.g()"); } } { print("paketna C .u ()"); } { print("zatiena C.v()"); } { print("privatna C.w()"); { return new C(); }

protected void v() private void w()

I
public static A napraviA()

public class UnutranjaRealizacija { public static void main(String[] A a = Unutra sn ja A. na pr av iA( ); a.f 0 ; System.out.println(a.getClass() . g e t N a m e O ) ; // Refleksija i dalje hvata privatne klase: Sk rivenaRealizacija.pozivSkriveneMetode(a, Sk rivenaRealizacija.pozivSkriveneMetode(a, Sk rivenaRealizacija.pozivSkriveneMetode(a, S k r i ve na Re al iz ac ij a.pozivSkriveneMetode(a, "g"); "u"); "v"); args) throws Exception {

"w ");

48 0

Misliti na Javi

} /* Ispis: javna C.f() UnutrasnjaA$C javna C.g() paketna C.u() zatiena C.v() privatna C.w()

* ///:T im e se n ita nije sakrilo o d refleksije. D a li e to u sp eti a n o n im n o j klasi?


//: podaciotipu/AnonimnaRealizacija.java // Anonimne unutranje klase ne mogu se sakriti od refleksije. import podaciotipu.interfejsa.*; import static ne t. mi nd vi ew .u ti l.Print.*; class AnonimnaA { public static A napraviA() return new A() { { print("javna C.f()"); } } } } { p r in t(javna C.g()''); } { print("zatiena C.v()"); public void f() public void g() void u() {

{ print("paketna C.u()");

protected void v() private void w()

{ print("privatna C.w()");

}; } }
public class AnonimnaRealizacija { public static void main(String[] args) throws Exception { A a = A n on im na A. na pr av iA (); a.f 0 ; System.out.println ( a . ge tC la ss(),getName()); // Refleksija i dalje hvata anonimne klase: SkrivenaRealizacija.pozivSkriveneMetode(a, SkrivenaRealizacija.pozivSkriveneMetode(a, SkrivenaReal izacija.pozi vSkri veneMetode(a, Skri venaReal izacija . p o z i vSkri veneMetode(a, "g"); "u"); "v"); "w");

}
} /* Ispis: javna C.f() AnonimnaA$l javna C.g() paketna C.u() zatiena C.v() privatna C.w()

Poglavlje 14: Podaci o tipu

481

Kao da ne postoji n a in da se sprei d a refleksija p ro n a e i pozove m e to d e koje im aju n ejav n i p ristu p . Isto vai i za p o lja, ak i o n a p riv atn a:
//: podaciotipu/ModifikovanjePrivatnihPolja.java import java.lang.reflect.*; class UzPrivatnoFinalnoPolje { private int i = 1; private final String s = "Potpuno sam bezbedan"; private String s2 = Jesam li bezbedan?"; public String t o S t r i n g O { return "i = " + i + ", " + s + ", " + s2;

} }
public class ModifikovanjePrivatnihPolja { public static void main(String[] args) throvvs Exception { UzPrivatnoFinalnoPol je pf = new UzPrivatnoFinalnoPol j e ( ) ; Sy st em .o ut .p ri nt ln (pf ); Field p = pf.getClass().getDeclaredField("i"); p.setAcc es si bl e( tr ue); System.out.println("p.getInt(pf): p.setlnt(pf, 47); System.out.printl n ( p f ) ; p = p f . g e t C l a s s O .g et De claredField("s"); p.se tA cc es si bl e( tr ue); System.out.p ri nt ln ("p .g et (p f): " + p.get(pf)); p.set(pf, "Ne, nisi!"); S y st em .o ut.pri nt l n ( p f ) ; p = pf.getClass().getDeclaredField("s2"); p.se tA cc es si bl e( tr ue); System.out.p ri nt ln ("p .g et (p f): " + p.get(pf)); p.set(pf, "Ne, nisi!"); Sy st em .o ut .p ri nt ln (pf ); " + p.getlnt(pf));

}
} /* Ispis: i = 1, Potpuno sam bezbedan, Jesam li bezbedan? p. g e t l n t ( p f ) : 1 i = 47, Potpuno sam bezbedan, Jesam li bezbedan? p.get(pf): Potpuno sam bezbedan i = 47, Potpuno sam bezbedan, Jesam li bezbedan? p.get(pf): Jesam li bezbedan? i = 47, Potpuno sam bezbedan, Ne, nisi!

* ///:M e u tim , finalna polja se zapravo ne m o g u m en jati. Izvrni sistem se nee aliti n a p o kuaje takv ih izm ena, ali ih nee ni sprovesti.

482

Misliti na Javi

Po pravilu, sva ta krenja prava p ristu p a n isu n ajgora stvar n a svetu. U koliko neko u p o trebi takvu teh n ik u za pozivanje m e to d a koje ste oznaili kao p riv a te ili kao m eto d e s paketn im p ristu p o m (im e ste svim a jasn o stavili d o zn an ja kako ne bi trebalo da ih pozivaju), o n d a teko m oe da se ali k ad a izm enite neki aspekt tih m etoda. S d ru g e stran e, injenica da uvek im ate m ala v rata u klasu, m oe o m o g u iti da reite o d re en e vrste p ro b lem a koji bi inae bili teki ili nem ogui, a p red n o sti refleksije su, u o p te n o govorei, neosporne.

Veba 25: (2) N apravite klasu koja sadri p riv a tn e m eto d e , zatiene m eto d e i m eto d e
s p a k etn im p ristu p o m . N ap iite k o d za p ristu p a n je tim m e to d a m a spolja, tj. izvan pak eta te klase.

Saetak
P rep o zn av an je tip a to k o m izvravanja o m o g u u je o tk riv a n je in fo rm ac ija o tip u p o m o u a n o n im n e reference n a klasu. P o etn ici ga esto z lo u p o treb ljav aju je r im izgleda p o g o d nije o d poziva p o lim o rfn ih m eto d a. M n o g i lju d i, navikli n a p ro c e d u ra ln o p ro g ram ira n je , teko se o d v ikavaju o d o rg an izo v an ja p ro g ra m a u o b lik u sk u p a n a re d a b a sw itch . Takva s tru k tu ra bi se m o gla p o stii o d re iv a n je m tip a u v re m e izvravanja, ali b i se izgubila vana o so b in a p o lim o rfizm a to k o m razvoja i o d rav an ja koda. S u tin a o'bjektno o rijen tisan o g p ro g ra m ira n ja je d a se u k o d u k o riste pozivi p o lim o rfn ih m eto d a, a da se p rep o zn av an je tip a u v rem e izvravanja k o risti sam o kada je n eo p h o d n o . Da b i se p ozivi p o lim o rfn ih m e to d a ko ristili na p ro p isan i n ain , m o ra se k o n tro lisati definicija o sn o v n e klase, zato to bi se m o g lo desiti d a u o d re e n o m tre n u tk u , kada p ro irujete p ro g ra m , u stan o v ite k ako o sn o v n a klasa ne sadri p o tre b n u m eto d u . Ako osnovna klasa p o tie iz biblioteke ili je k o n tro lie n eko d ru g i, reenje p ro b le m a lei u p rep o zn av an ju tipa to k o m izvravanja, je r tak o m o ete izvesti n o v tip i d o d a ti eljenu m eto d u . N a n ek o m d ru g o m m e stu u k o d u m o ete d a o tk rijete pravi tip i pozovete specijaln u m e to d u . P olim orfizam i m o g u n o st p ro iriv an ja p ro g ra m a nee b iti u p ro p aen i, jer se p ri d o d av a n ju novog tip a ne m o ra trag ati za n a re d b a m a sw itc h u p ro g ra m u . M e u tim , u k o d u za koji je n e o p h o d n a n o v a m eto d a, m o ra te p rim e n iti p rep o zn av an je tip a u v rem e izvravanja da b iste o tk rili o d re e n i tip. Z bog sm etan ja nove funkcije u o sn o v n u klasu m o g lo bi se desiti d a zarad jed n e klase svim ostalim klasam a izvedenim iz iste o sn o v n e klase treb a d o d a ti n ek u b esm islen u m eto d u . T im e se gubi p reg led n o st in terfejsa i n e rv iraju se p ro g ra m eri, koji m o raju da redefiniu a p stra k tn e m e to d e k ad a izvode d ru g e klase iz te o sn o v n e klase. Na p rim er, p o sm a tra jin o h ije ra rh iju klasa koja pred stav lja m u zike in stru m e n te . P retp o sta v im o da elite d a p ro istite piskove svih d u v ak ih in s tru m e n a ta u o rk estru . Jedna m o g u n o st je da stavite m e to d u procistiPisak() u o sn o v n u klasu Instruinent, ali to z b u n ju je zato to p o d razum eva da i u d arak i i iani in s tru m e n ti im aju pisak. P rep o zn av an je tip a u v rem e izvravanja o bezb e u je m n o g o p o g o d n ije reenje u ovom sluaju zato to m e to d u m oete staviti u o re e n u klasu (u o v o m sluaju, u klasu d u v ak ih in stru m e n a ta ) gde joj je i m esto. M e u tim , m n o g o bolje reenje je d a se m e to d a priprem ilnstrum ent() stavi u osnovn u klasu. Ipak, to m o d a n eete p red v id eti kada se p rv i p u t sretn ete s p ro b le m o m , pa ete p ogreno zakljuiti d a m o ra te k o ristiti p re p o z n a v an je tip a u v rem e izvravanja.

Poglavlje 14: Podaci o tipu

483

Na k raju , p rim e n o m p re p o z n a v an ja tip a to k o m izvravanja p o n e k a d se reavaju p ro b le m i sa efikasnou. A ko se u k o d u d o b ro k o risti p o lim o rfizam , ali o b jek at reaguje na ko d o p te n a m e n e iz u ze tn o neefikasno, m o ete d a izolujete njegov tip zahvaljujui p rep o z n a v a n ju u v rem e izvravanja i d a napiete k o d za taj p o se b a n sluaj da b iste poveali efikasnost. Ipak, n e m o jte da srljate u p ro g ra m ira n je rad i poveanja efikasnosti b ez p re th o d n e pro vere, je r je to zam ka. N ajbolje je p rv o nekako n a tera ti p ro g ra m d a radi, p o to m o d lu iti da li rad i d o v o ljn o b rzo , i tek n ak o n toga ra z m o triti efikasnost.(T o se rad i p rofajle ro m - v id eti d o d a ta k n a adresi http://M indV iew .net/B ooks/B etterJava.) V ideli sm o da refleksija o tv a ra n o v svet m o g u n o sti u p ro g ra m ira n ju , tak o to o m o gu u je m n o g o d in am i n iji stil p ro g ra m ira n ja . Im a lju d i koje onesp o k o jav a d in a m i n a p riro d a refleksije. in jen ica d a m o ete ra d iti n eto to se m oe p ro v e riti tek u v rem e izvravanja i p rijav iti p o m o u izuzetaka, izgleda o n im a koji su navikli n a b e z b e d n o st statike p ro v ere tip o v a kao p o g rean sm er. N eki p re te ru ju , p a kau k ako je u v o en je m o g u n o sti pojave izuzetk a u v re m e izvravanja jasan pokazatelj da takav k o d treb a izbegavati. S m a tra m d a je to osean je b ezb ed n o sti ilu z o rn o - u v rem e izvravanja uvek se m o e d esiti n eto to e izazvati izuzetak, ak i u p ro g ra m u koji ne sad ri n i blokove tr y n iti specifikacije izuzetaka. M islim d a d o sled n i m o d el prijavljivanja greaka om oguuje d a p ie m o d in a m i k i k o d i u p o tre b ljav am o refleksiju. N arav n o , vred i p o k u a ti n ap isati k o d koji se m o e statiki p r o v e r iti... k ad a je to m ogue. Ali sm a tra m da je d in a m i k i k o d jed n a o d v an ih m o g u n o sti koje odvajaju Javu o jezika kao to je C + + . V eba 26: (3) R ealizujte m e to d u p ro c istiP is a k () kako je o p isan o u saetku.
R een ja o d a b r a n ih v eb i d a ta su u e le k tro n sk o m d o k u m e n tu The Thinking in Java Annotated Solution Guide, koji se m o e k u p iti n a lo k a ji www.MindView.com.

Generiki tipovi
O BIN E KLASE I METODE RADE S POTPUN O ODREENIM TIPOVIMA: BILO PROSTIM, BILO

tip o v im a klasa. A ko piete k o d k o ji se m o e u p o tre b iti s vie tip o v a, ta k ru to s t u m e p revie d a o g ran iav a.1 Jedan o d n a in a n a koji o b je k tn o o rije n tisa n i jezici o m o g u u ju u o p tav an je jeste p o lim orfizam . P rim e ra rad i, m o ete n a p isa ti m e to d u k o ja kao a rg u m e n t p rim a objekat o sn o v n e klase, i zatim u p o tre b iti tu m e to d u s b ilo k o jo m klaso m izv ed en o m iz te o sn o v n e klase. Tako vaa m e to d a p o staje n eto o p tija i m o e se u p o tre b lja v ati na vie m esta. Isto vai i u n u ta r klasa - gde g o d u p o tre b lja v a te o d re e n i tip, o sn o v n i tip p ru a veu fleksib iln o st. N aravno , m o e se p ro iriti (n asle d iti) sve sem finalne klase,2 p a se ta fleksibilnost v ein o m d o b ija au to m atsk i. P on ek ad o g ran ien o st n a je d n u h ije ra rh iju previe sputava. U koliko je a rg u m e n t m eto d e interfejs a n e klasa, o g ran ien ja se ub laav aju je r se o b u h v a ta sve to realizuje taj interfejs - ak i klase koje jo n isu n i n ap rav ljen e. T im e p ro g ra m e r k lijen t d o b ija m o g u n o st da realizuje interfejs kako bi se p rila g o d io vaoj klasi ili m eto d i. Tako interfejsi o m o g u u ju p rem oavanje hijerarh ije klasa, ukoliko im ate m o g u n o st d a n ap rav ite n o v u klasu za to. K atkada i interfejs previe o g ran iav a. Svaki interfejs zah tev a d a k o d rad i b a s tim k o n k re tn im in terfejso m . K od bi b io o p tiji kada b iste m o g li da zad ate da ra d i ,,s nekim n esp ecificiranim tip o m , a n e s bilo k o jim k o n k re tn im in terfe jso m ili klasom . To je zam isao u p o za d in i g e n erik ih tip o v a - o n i pred stav ljaju je d n o o d znaajnijih p o b o ljan ja koje je d o n ela Java SE5. G en e ri k i tip o v i realizu ju k o n c e p t param etrizovanili tipova - o n i o m o g u u ju p isanje k o m p o n e n a ta (o b in o k o n te jn e ra ) koje se lako u p o tre bljavaju s vie tip o va. T erm in ,,generiki znai koji o d g o v ara velikim g ru p a m a klasa ili vai za n jih . G enerik i tip o v i su u ved en i u p ro g ram sk e jezike da bi o m o g u ili p ro g ram erim a najveu m o g u u izraajn o st k ad a p iu klase ili m eto d e, ta k o to se ublaavaju ogranienja n a tip ove s kojim a te klase ili m eto d e rade. Kao to ete v ideti u o v o m pogiavlju, Javini generiki tip o v i n isu to lik o m o n i - tavie, m ogli biste se zap itati da Ii re generik i u o p te p rik la d n o opisu je te m o g u n o sti. A ko n ik ad a niste radili s m e h a n iz m o m p a ra m e triz o v a n ih tipova, Javini generiki tip o vi v ero v atn o e vam izgledati kao p o d e sa n d o d a ta k jeziku. Kada n a p ra v ite p rim e ra k para m etrizo v a n o g tip a, konverzije tip o v a se obavljaju a u to m atsk i, a isp rav n o st tipova prov erava u v rem e prev o en ja. To lii na pob o ljan je. M e u tim , uko liko ste p re th o d n o im ali posla s n ek im m e h a n iz m o m p a ram etrizo v an ih tip ov a, recim o u jeziku C + + , v id eete d a s Javinim g en erik im tip o v im a ne m o ete da u ra d ite ba sve to b iste o d njih oekivali. K orienje tu ih gen erik ih tipova je prilino lako, ali pravljenje so p stv en ih je p u n o izn en a en ja . Izm e u ostaloga, p o k u au da varn o b jasn im kako je dolo d o takve realizacije g en erik ih tip o v a u Javi.
1 Angelika L anger pie Java Generics FAQ (O dg ov ori na najea pitanja o Javinim generikiin tipovim a, videti www.langer.camelot.de). Ti i d ru g i njeni tekstovi (napisani zajedno s K lausom K rettom ) bili su m i o d neprocenjive vred n o sti tokom p rip re m e ovog poglavlja. Ili klasa koja im a sam o privatne k o n stru k to re.

Poglavlje 15: Generiki tipovi

485

N e kaem d a su Javini g en erik i tip o v i b esk o risn i. U m n o g o sluajeva, o n i k o d ine je d n o sta v n ijim , p a ak i eleg a n tn im . Ali ako ste k o ristili jezik u k ojem je realizovana istija verzija g en erik ih tip o v a, m o g li b iste d a se razoarate. U o v o m poglavlju istraiem o i jake i slabe stra n e Javinih gen erik ih tipo va, d a b iste d elo tv o rn ije m ogli d a ih koristite.

Poreenje sa C++-om
P ro je k ta n ti Jave kau d a je d o b a r d eo tog jezika b io reakcija n a C + + . U p rk os to m e , Javu je m o g u e p re d av ati u g la v n o m b ez sp o m in ja n ja C + + -a , i ja sam se tru d io d a ra d im tako, sem k ad a p o re e n je daje d u b lji uvid. G en erik i tip o v i z ah te v aju vie p o re en ja s C + + -o m iz dva razloga. P rvo, kad a shvatite o d re e n e asp ek te C + + -o v ih ablona (engl. tem plate) - ko ji su bili glavno n a d a h n u e za generike tip o ve, ak i za n jih o v u o sn o v n u sin ta k su - b o lje ete shvatiti tem elje to g k o n cepta, k ao i - a to je v e o m a v an o - o g ra n i en ja p rilik o m u p o tre b e Javinih g en erik ih tip o v a i razloge za njih . K rajnji cilj je d a stek n ete ja sn u p red stav u o to m e gde su granice, p o to z n a m iz so p stv en o g iskustva: k ad a shv atite gde su granice, p o stajete bolji p ro g ra m er. K ada zn ate ta n e m o e d a se u ra d i, bo lje ete isk o ristiti o n o to m oe (d elo m i zato to ne g u b ite v re m e su d a ra ju i se sa z ido vim a). D ru g i razlog je znaajan n e sp o ra z u m koji u n u ta r Javine zajednice vlada kad a je re o C + + a b lo n im a . Taj n e sp o ra z u m m o e jo vie d a vas zb u n i u p o g led u p re d v i en e u p o treb e g e n erik ih tipova. Stoga u u ovo m po glavlju d a ti tek m in im a la n broj p rim e ra s C + + ab lo nim a.

Jednostavni generiki tipovi


Jedna o d n ajvan ijih p o e tn ih m otiv acija za u v o en je gen erik ih tip ov a bila je pravljenje kotitejnerskih klasa , koje ste u p o zn ali u poglavlju uvanje objekata (a nauiete jo u poglavlju D etaljno razm atranjc kontejnera ). K o n tejn er je m esto za skladitenje o b jek ata d o k ra d ite s njim a. Isto vai i za nizove, ali k o n te jn e ri su fleksibilniji i im aju d rug aija o beleja o d o b i n ih nizova. G o to v o svi p ro g ra m i zah te vaju d a negde uvate g ru p u o b jekata d o k rad ite s n jim a, pa k o n te jn e ri sp ad aju m e u najee u p o treb ljav an e b iblioteke klasa. P ogledajm o klasu koja uva je d a n objekat. N aravno, klasa bi m ogla da zada taan tip tog o b jek ta, ovako:
//: ge ne ri cki/Skladistel.java class Automobil (} { { this.a = a; }

public class Skladistel private Automobil Automobil get() a;

public S k l a d i s t e l (Automobi1 a) { return a; }

} ///:Ali tu a latk u nije lako p o n o v n o u p o tre b iti, p o to se ne m o e koristiti za skladitenje iega d ru g o g a. Bilo bi bolie da ne m o ra m o da p iem o novu za svaki tip na koji nai em o.

48 6

Misliti na Javi

Pre Jave SE5, je d n o sta v n o b ism o joj dali d a uva jed a n Object:
//: genericki/Skladiste2.java public class Skladiste2 { private Object a; public Skladiste2(0bject a) { this.a = a; ) public void set(Object a) { this.a = a; ) public Object get() { return a; } args) { public static void main(String[]

Skladiste2 h2 = new Skladiste2(new A u t o m o b i l ()); Automobil a = (Automobi 1 ) h 2 . g e t (); h2.set("Nije Automobil"); String s = (String)h2.get(); h2.set(l); /'/ Automatskim pakovanjem postaje Integer Integer x = (Integer)h2.get();

} } ///= Sada Skladiste2 m oe da p rim i b ilo ta - a u ovom p rim e ru isto Skladiste2 uva tri razliita tip a objekata. Im a sluajeva kada hoete da k o n tejn er sldaditi vie tipova objekata, ali se u k o n tejn er o b in o stavlja je d a n tip objekata. Jedna o d o sn o v n ih m otivacija za generike tipove bilo je zadavanje tipa objekta koji k o n tejner sadri, i da tu specifikaciju pod ri i proveri prevodilac. Z ato b ism o u m esto tip a Object hteli d a u p o tre b im o nespecificiran tip, koji m oe biti o d re e n k asnije. To se rad i tako to n ak on im ena klase u n u ta r znakova m anje od i vee o d n ap iete p a ra m eta r tipa, i zam e n ite ga stv arn im tip o m kada tu klasu u p o tre b ite. Za klasu Skladiste to bi izgledalo ovako (T je p a ra m e ta r tipa):
//: genericki/Skladiste3.java public class Skladiste3<T> { private T a; public Skladiste3(T a) { this.a = a; } public void set(T a) { this.a = a; } public T get() { return a; } args) { public static void main(String[] Skladiste3<Automobi1> h3 = new Sk ladiste3<Automobi1>(new A u to mo bi1()); Automobil a = h3.get(); // Konverzija tipa nije potrebna // h3.set("Nije Automobil"); // Greka // h 3 . s e t ( l ) ; // Greka

} } ///:K ada sada n aprav ite neko Skladiste3, istom sintak so m sa znakov im a m an je od i vee od m o rate zadati tip koji ho ete da uvate u n jem u , kao to vidite u m eto di m a in ( ). U Skladiste je dozvoljeno stavljati sam o objekte tog tip a (ili po d tip a, poto p rin cip zam ene

Poglavlje 15: Generiki tipovi

487

fu n k cio n ie i u generikim tip o v im a). A k a d a iz njega izvadite neto, o n o je a u to m atsk i isp ravno g tipa. To je o sn o v n a ideja Javinih generik ih tipova: vi zadate tip koji h o ete d a u p o treb ljavate, a Java se dalje stara za detalje. Po p rav ilu , generike tipove m o ete tre tira ti kao da su bilo koji d ru g i tip - sa m o to im aju i p a ra m e ta r tipa. Ali kao to ete v ideti, generiki tip up otreb ljav ate n av o en jem njegovog im e n a i liste njegovih a rg u m e n a ta tipo va.

Veba 1: ( 1) U p o treb ite Skiadiste3 s b ib lio tek o m podaciotipu.ljubim ci da biste pokazali da Skladiste3, specificirano da uva o sn o v n i tip , m o e da uva i njegov izvedeni tip. Veba 2: (1) N ap rav ite klasu skladista koje uva tri objekta istog tipa, m e to d e za skladiten je i vaenje tih o bjekata, i k o n stru k to r za inicijalizaciju sva tri objekta.

Biblioteka n-torki
esto bi b ilo p o eljno v ra titi vie objekata iz je d n o g poziva m etod e. N ared b a retu rn do zvoljava zadavanje sam o je d n o g objekta, p a je reenje n a p ra v iti o bjekat koji sad ri vie o b jek a ta koje h o ete da v ratite. N arav n o , m o ete da piete p o seb n u k lasu svaki p u t kada n ai e te na tak v u situaciju , ali p o m o u g en erik ih tipova p ro b le m m o ete reiti je d n o m zauvek i tak o sebi uted eti tru d u b u d u e. Isto v rem en o , u vrem e p rev o en ja svi generiki tipo vi e biti provereni. O vaj k o n c e p t se naziva n-torka (engl. tuple ), a rad i se o g ru p i objek ata o m o ta n ih zaje d n o u je d a n objekat. P rim alac objekta m oe d a ita njegove elem en te, ali ne i d a stavlja nove u njega. (O vaj k o n cep t nazivaju i D ata Transfer Object ili Messenger, O b jek at za p renos p o d a ta k a ili D ostavlja p o ru k a.) n -to rk e m o g u biti p roizvoljne du in e i svaki n jih o v o b jek at m o e b iti razliitog tipa. M ed u tim , m i h o em o da z ad am o tip svakog o bjek ta i o bezb ed im o da p rim a la c - kada p ro ita njegovu v re d n o st-d o b ije ispravan tip. P rob lem zbog razliitih d u in a reiem o prav ljen jem razliitih n -to rk i. Evo jed n e koja uva dva objekta:
//: net/ mi nd vi ew /u ti1/Dvojka.java package net.mindview.util; public class Dvojka<A,B> { publi c fi nal A p r v i ; public fi nal B d r u g i ; public Dvojka(A a, B b) { prvi = a; drugi = b; } public String toString() { return "(" + prvi + ", " + drugi + ")";

} } ///= K o n stru k to r hvata ob jek at koji treba sauvati, a to S trin g ( ) je p rig o d n a funkcija za p rikazivanje v red n o sti iz liste. O b ra tite p a n ju na to da n -to rk a svoje elem ente im p lic itn o uva u p o re tk u .

488

Misliti na Javi

N ak o n p rv o g itanja ovog p rim e ra , m o d a ste p o m islili d a se n jim a kre p rin c ip i bezb e d n o sti u o b iajen i u Java p ro g ra m ira n ju . Z ar n e bi p rv i i drugi treb alo d a b u d u p riv a tn i i da im p ristu p a ju sam o m eto d e n azv an e d a jP rv i( ) i d a jD ru g i( )? R a zm o trim o bezbedn o st k o ju biste postigli u to m sluaju: k lijen ti b i i dalje m o g li d a itaju objekte prvi i drugi i d a ra d e s n jim a ta g o d hoe, ali n e b i m o g li d a ih d o d e le n ie m u d ru g o m . Istu bezb ed n o st daje d ek laracija final, ali je g o rn ji o b lik k rai i jed n o stav n iji. D ru g a p ro je k tn a opask a jeste d a b iste m o d a hteli d a d o p u stite p ro g ra m e ru klijen tu da o b jek at p rvi ili drugi u sm eri k a d ru g o m o b jek tu . M e u tim , b ezb en ije je da ga ostavite u g o rn je m o b lik u i sam o p rim o ra te k o risn ik a d a n a p ra v i n o v u klasu Dvojka ukoliko eli n e k u s d ru g aijim elem en tim a. D ue n -to rk e se p rave n asle iv an jem . V ideete d a je d o d av an je veeg b ro ja p a ra m e ta ra tip a jed n o stav n o :
//: net/mindview/util/Trojka.java package net.mindview.util; public class T r o j k a < A , B , 0 extends Dvojka<A,B> { public final C t r e c i ; public Trojka(A a, B b, C c) { super(a, b); treci = c;

}
public String t o S t r i n g O { return "(" + prvi + ", " + drugi + ", " + treci +")";

} } ///://: ne t/ mi nd vi ew /u ti1/Cetvorka.java package net.mindview.util; public class Cetvorka<A,B,C,D> extends Trojka<A,B,C> { publi c fi nal D c e t v r t i ; public Cetvorka(A a, B b, C c, D d) { super(a, b, c); cetvrti = d; {

}
public String toString() return "(" + prvi + ", 1 1 + drugi + ", " + treci + ", " + cetvrti + ")";

} } ///://: net/mindview/util/Petorka.java package net.mindview.util; public class Petorka<A,B,C,D,E> extends Cetvorka<A,B,C,D> {

Poglavlje 15: Generiki tipovi

489

public final

E peti;

public Petorka(A a, B b, C c, D d, E e) { super(a, b, c, d); peti = e;

}
public String toString() { return "(" + prvi + ", " + drugi + ", + treci + ", " + cetvrti + ", " + peti + )";

} } ///= n- to rk u k o ristite tak o to n -to rk u eljene d u in e d efiniete kao p o v ra tn u v re d n o st svoje funkcije i zatim je n ap rav ite i v ra tite n a re d b o m re tu rn :
//: ge ne ri cki/IspitivanjeEntorki.java import net.mindview.util.*; class Amfibija {} class Vozilo {} public class IspitivanjeEntorki { {

static Dv oj ka <String,Integer> f()

// Automatsko pakovanje pretvara int u Integer: return new Dv oj ka <S tr in g, In te ger >( "z dr av o , 47);

}
static Tr oj ka<Amfibija ,S tr ing,Integer> g() return new Trojka<Amfibija, String, new A m f i b i j a ( ) , "zdravo", 47); { Integer>(

}
stati c Ce tv or ka <V oz il o, Amfibija,String,Integer> h() return new Ce tv or ka <V oz il o. Am fib ij a . S t r i n g , Integer>( new Vozilo(), new Amfibijaf), "zdravo", 47); {

}
stati c Pe to rk a< Vo zi lo ,A mf ibi ja ,S tr in g, In te ge r, Dou bl e> k() { return new Petorka<Vozilo,Amfibija,String,Integer,Double>( newVozilo(), new Amfi bij a ( ) , zdravo", 47, 11.1); args) {

}
public static void main(String[] Syst em .o ut .p ri nt ln (ie si); // iesi.prvi = "tamo"; // Greka u prevoenju: final System.out.pri n t ln (g ()); System.out.pri n t ln ( h ( ) ); System.out.pri n t ln (k ()) ; D v oj ka<Strin g , Integer> iesi = f();

490

Misliti na Javi

} /* Ispis: (zdravo, 47)

(80% podudaranja)

(Amfibija@lf6a7b9, zdravo, 47) (Vozilo@35ce36, Am fi bi j a @ 7 5 7 a e f , zdravo, 47) (Vozilo@9cabl6, Amfibija(?la46e30, zdravo, 47, 11.1)

* ///:P o m o u gen erik ih tip o v a lako m o e te p o stii d a svaka n -to rk a v ra a p ro izv o ljn u g ru p u tipova; d o v oljno je d a n ap iete o d g o v araju i izraz. M oete v id e ti kako o d re d b a final za jav n a p o lja spreava d a o n a b u d u p o n o v n o d o deljena n ak o n k o n stru k cije, je r se n a re d b a iesi.prvi = "tamo" ne m o e prevesti. Treba p rili n o m n o g o p isati u new izrazim a. U n astav k u poglavlja v ideete kako da ih p o jed n o stav ite generikim m etodam a.

Veba3: (1) N ap rav ite i isp ro b ajte g en erik u Sestorku. Veba4: (3) U op tite p ro g ra m unutrasnjeklase/Sekvenca.java p o m o u generikih tipova.

Klasa steka
P ogledajm o neto to je m alo kom pliko vanije: klasini stek s kojeg se p rv o u zim a o n o to je n a njega p o sled n je stavljeno. U p o glavlju uvatije objekata realizovali sm o stek p o m o u u lan a n e liste (LinkedList) kao klasu net.m indview.util.Stack (n a stra n a m a 320-323). U to m p rim e ru v id ite d a u lan a n a lista ve im a m e to d e p o tre b n e za prav ljen je steka. Klasu Stack n ap ravili sm o slaganjem je d n e gen erik e klase (Stack<T>) s d ru g o m g enerikom klasom (LinkedList<T>). N a to m p rim e ru vid ite da je generiki tip kao i svaki d ru g i, (uz nekoliko izuzetaka koje em o ra z m o triti k asnije). U m esto d a u p o tre b im o LinkedList, m o e m o d a realizu jem o so p stv en i u n u tra n ji u lan a n i m eh a n iz a m za skladitenje.
//: g e n e ri ck i/ U1 an ca ni Ste k.java // Stek realizovan pomou unutranje ulanane strukture. public class UlancaniStek<T> ( private static class Cvor<U> ( U stavka; Cvor<U> sledeca; Cvor() { stavka = n u l l ; sledeca = null; { } Cvor(U stavka, Cvor<U> sledeca) this.stavka = stavka; this.sledeca = sledeca;

}
boolean kraj() { return stavka == null && sledeca == null; }

}
private Cvor<T> vrh = new Cvor<T>(); // Oznaka kraja public void stavina(T stavka) { vrh = new Cvor<T>(stavka, v r h ) ;

Poglavlje 15: Generiki tipovi

491

public T skinisa() if(!vrh.kraj())

T rezultat = vrh.stavka; vrh = vrh.sledeca; return rezultat;

}
public static void main(String[] args) { Ul ancaniStek<String> uss = new U l a n c a ni St ek <S tr in g>( ); for(String s : "Phasers on stun!".split(" ")) u s s . st av in a( s); String s; while((s = uss.skinisa()) System.o ut .p ri nt ln (s); != null)

}
} /* Ispis: stun! on Phasers

* ///:-

I u n u tra n ja klasa Cvor je gen erik a i im a v lastiti p a ra m e ta r tipa. U ovom p rim e ru , oznaka k raja (engl. e n d sentinel) u tv r u je kada je stek p razan . O znaka se pravi p rilik o m k o n stru isa n ja o b jekta tip a UlancaniStek, i svaki p u t kada pozovete stav in a( ), pravi se nov Cvor<T> i u lanava s p re th o d n im o b jek to m tip a Cvor<T>. Kada pozovete sk in isa( ), uvek vraate vrh.stavka i p o to m o d b a cu je te tek u i Cvor<T> i p relazite n a sledei - s e m kada n ai ete na o zn a k u k raja, je r s e u to m sluaju ne p o m erate. Stoga e k lijent koji staln o poziva sk in isa( ) staln o d o b ija ti null, to p o kazuje da je stek p razan .
V eba 5: (2) U k lo n ite p a ra m e ta r tip a iz klase C v o r i izm en ite o statak k o da u p ro g ra m u U la n c a n iS te k .ja v a tako da p o kaete da u n u tra n ja klasa im a p ris tu p gen erik im p ara m e trim a tip a svoje sp o ljne klase.

NasumicnaLista
Kao d ru g i p rim e r za skladiste, p re tp o sta v im o da h o ete p o se b n u v rstu liste koja n asu m ino bira je d a n o d svojih elem en ata k ad a se pozove iz a b e ri( ). Poto hoete alatk u koja radi sa svim o b jek tim a, k o ristite g enerike tipove:
//: ge ne ricki/NasumicnaLista.java import j a v a . u t i l .*; public class NasumicnaLista<T> { private ArrayList<T> skladiste = new A r r a y L i s t < T > ( ) ; private Random slucajan = new Random(47); public void a d d (T stavka) public T select() { { s k l a d i s t e. ad d( st av ka); }

return sk la di st e. da j(s lu ca ja n. ne xt ln t( skl ad is te .s iz e( )) );

492

Misliti na Javi

public static void main(String[]

args)

Na sumicnaLista<String> nls = new Nasu mi cn aL is ta <S tr ing >( ); for(String s: ("The quick brown fox jumped over " + "the lazy brown dog").split(" nls.add(s); for(int i = 0; i < 11; i++) System.out.print(nls.izaberi () + 1 1 "); "))

) } /* Ispis:
brown over fox quick quick dog brown The brown lazy brown

* ///:-

Veba 6: (1 ) U p o tre b ite k lasu NasumicnaLista s dva tip a vie n ego to je p o k az an o u m eto d i m a in ().

Generiki interfejsi
G eneriki tip o v i su p rik la d n i i za interfejse. N a p rim er, generator je klasa koja p rav i o b jekte. Z apravo, ra d i se o specijalizaciji p ro je k tn o g o b rasca Factory M eth o d (P roizvo dn a m e to d a ), ali k a d a o d g e n e ra to ra zatra ite n o v objekat, n e p ro sle u jete m u arg u m en te, d o k ih P ro izv o d n a m e to d a o b i n o p ro sle u jete. G e n e ra to r zna kako da pravi nove o bjekte b ez ikakvih d o d a tn ih in fo rm ac ija. G e n e ra to r o b i n o definie sam o je d n u m e to d u , o n u koja prav i nove objekte. O vde em o je nazvati sledeci( ) i u k lju iti m e u sta n d a rd n e uslun e klase:
//: n e t/ mi nd vi ew /u ti1/Generator.java // Generiki interfejs. package net.mindview.util; public interface Genera to r< T> { T s l e d e c i (); } ///:-

P o v ratn i tip m e to d e sledeci() p ara m e triz o v a n je u T. Kao to v id ite, generiki tip ov i se u in terfe jsim a u p o treb lja v a ju kao i u klasam a. R ealizaciju interfejsa G e n e ra to r p o k azaem o na klasam a u h ijerarh iji kafe:
//: ge ne ri ck i/kafa/Kafa.java package g e n e r i c k i .kafa; public class Kafa { private static long brojac = 0; private final long id = brojac++; { public String toString()

return g e t C l a s s ().getSimpleName() + " " + id;

} } ///://: ge ne ri ck i/ ka fa /S Ml eko m. ja va package genericki.kafa; public class SMlekom extends Kafa {} ///:-

Poglavlje 15: Generiki tipovi

493

//: ge nericki/kafa/Moka.java package genericki.kafa; public class Moka extends Kafa {} ///://: genericki/kafa/Cappuccino.java package genericki.kafa; public class Cappuccino extends Kafa {} ///:/ / : generi cki/kafa/Ameri cano.java package genericki.kafa; public class Americano extends Kafa {} ///://: genericki/kafa/Kratka.java package genericki.kafa; public class Kratka extends Kafa {} ///:-

Saa m o em o da realizu jem o G e n e ra to r< K a fa > koji p rav i razliite tipove K afa ob jekata:
//: genericki/kafa/GeneratorKafe.java // Generie razliite tipove Kafa: package g e n e r i c k i .kafa; import ja v a . u t i 1.*; import net.mindview.util.*; public class GeneratorKafe implements Generator<Kafa>, private Class[] Iterable<Kafa> { }; tipovi = { SMlekom.class, Moka.class,

Cappuccino.class, Americano.class, Kratka.class, private static Random slucajan = new Random{47); public GeneratorKafe() // Za iteraciju: private int velicina = 0; public GeneratorKafe(int vel) public Kafa sledeci() try { return (Kafa) { { velicina = v e l ; } {}

t i p o v i [sluc aj an . n e x t l n t (tipovi.1 ength)] ,n ewlnstance(); // U vreme izvravanja, prijavi greke programera: } catch(Exception e) { throw new RuntimeE xc ep ti on (e );

} }
class IteratorKafa implements Iterator<Kafa> { int broj = velicina; public boolean hasNext() public Kafa s l e d e c i () { broj ; return Gene ra to rK af e. th is .sl ed ec i(); { return broj > 0; }

494

Misliti na Javi

public void ukloni()

{ // Nije realizovano

throw new U n s u pp or te dO pe ra ti onE xc ep ti on ();

} };
public Iterator<Kafa> iterator() return new It er at or Ka fa (); {

}
public static void main(String[] for(int i = 0; i < 5; 1++) S y s t e m . o u t .pri n t l n ( g e n .s l e d e c i ()); for(Kafa c ; new GeneratorKafe(5)) S y s t em .o ut .p ri nt ln (c); args) { GeneratorKafe gen = new Ge n e r a t o r K a f e ( ) ;

}
} /* Ispis: Americano 0 SMlekom 1 Americano 2 Moka 3 Moka 4 Kratka 5 Americano 6 SMlekom 7 Cappuccino 8 Cappuccino 9

* ///:P aram etrizo v an in terfejs G enerator sta ra se d a m eto d a sledeci( ) vraa p a ra m e ta r tipa. I GeneratorKafe realizuje in terfejs Iterable, p a ga m o em o u p o tre b iti u foreach naredbi. M e u tim , da bi z n a o k a d a da se zaustavi, tre b a m u oznaka k raja, a njega pravi dru g i k o n stru k to r. Evo d ru g e realizacije interfejsa G enerator<T> koji ovoga p u ta pravi brojeve Fibonaccijevog niza:
//: g e n e r i c k i/ Fi bo na cc i.java // Pravljenje Fibonaccijevog niza. import net.mindview.util.*; public class Fibonacci private int broj = 0; public Integer sledeci() private int fib(int n) { if(n < 2) return 1; return fib(n-2) + fib(n-l); { return fib(broj++); } implements Ge ne rator<Integer> {

}
public static void main(String[] for(int i = 0; i < 18; i++) Sy s t e m . o u t . p ri nt (g en. sl ed ec i() + " "); args) { Fibonacci gen = new Fibonacci();

Poglavlje 15: Generiki tipovi

495

} /* Ispis: 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584

* ///:Iako s p ro s tim tip o m in t ra d im o i u n u ta r i izvan klase, p a ra m e ta r tip a je Integer. To je je d n o o d o g ra n i e n ja Javinih gen erik ih tipova: p ro sti tipovi n e m o g u b iti p a ra m e tri tipa. M e u tim , Java SE5 je veo m a p o d e sn o uvela au to m a tsk o pakovanje i raspakivanje, za k o n verziju p ro s tih tip o v a u o m o ta k e i nazad. R ezultat toga vid ite u pravo ovde, je r klasa bez p ro b le m a u p o treb lja v a i prav i brojeve tip a int. M o em o o tii k o ra k dalje i n a p ra v iti F ibonaccijev g e n erato r koji realizuje in terfejs Ilerable. M ogli b ism o d a p o n o v o realizu jem o klasu i d o d a m o joj interfejs Iterable, ali n e m ate uvek k o n tro lu n a d izv o rn im k o d o m , n iti h o ete d a p o n o v o piete o n o to n e m o ra te. U m esto toga, n ap ra v i em o adapter koji e d ati eljeni interfejs. (Taj p ro je k tn i o b razac p red stavili sm o u p re th o d n o m delu knjige). A d a p teri se m o g u realizovati n a vie nain a. N a p rim er, a d a p tira n u k lasu m o ete d a generiete n asleivanjem :
//: ge ne ri ck i/ It er ab il niF ib on ac ci.java // Adaptacija Fibonaccijeve klase koja je ini iterabilnom. import j a v a . u t i l .*; public class IterabilniFibonacci extends Fibonacci implements Iterable<Integer> { private int n; public Iter ab il ni Fi bo na cc i(int broj) public Iterator<Integer> iterator() return new Iterator<Integer>() public boolean hasNext() public Integer sledecif) { { { { n = broj; }

{ return n > 0; }

n ;
return Iterabi1niFibona cc i. th is .s led ec i();

}
public void ukloni() { // Nije realizovano throw new Un su pp or te dO pe ra ti onE xc ep ti on ();

} }; }
public static void main(String[] args) System.out.print(i + " "); { for(int i : new Iterabi1 n i Fi bo na cc i(18))

}
} /* Ispis: 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584

* ///:U fo reach n ared b i, klasu Ite ra b iln iF ib o n a c c i upotreb ljav ate tako to n je n o m kon s tru k to ru zadajete g ran ic u , da bi m eto d a h a s N e x t( ) znala kada treb a da v ra ti false.

496

Misliti na Javi

Veba 7: (2) N apravite klasu Fibonacci ite ra b iln o m p o m o u k o m p o z i je , a n e nasleivanja.

Veba 8: (2) Po u z o ru n a p rim e r Kafa, n ap ra v ite h ije ra rh iju o b jek ata tip a Likovi iz vaeg om iljen o g film a; podelite ih na Pozitivce i Negativce. N ap rav ite g e n e ra to r za o b jek te tip a Likovi, p o u z o ru n a GeneratorKafe.

Generike metode
D o sad sm o p a ram etrizo v ali cele klase. M oete p ara m e triz o v a ti i p o je d in e m e to d e u n u ta r klase. S am a klasa m oe, ali n e m o ra b iti gen erik a - to ne zavisi o d g en e ri n o sti m eto d e. G en erin o st m eto d e znai da o n a m o e da se m en ja n ezav isn o o d klase. Po p rav ilu , generike m e to d e bi treb alo d a u p o treb ljav ate ,,kad g o d m o e te . D ru g im reim a, ako se u m e sto cele klase sam o m eto d a m o e n a p ra v iti d a b u d e g en erik a, v e ro v a tn o e tak o i k o d b iti jasniji. Pored toga, ako je m e to d a statin a, o n a n e m a p ristu p a g e n erik im p a ra m e trim a tip a (svoje generike) klase, p a u koliko tre b a d a k o risti g en e ri n o st, m o ra sam a da b u d e generika. G enerika m e to d a se definie stavljanjem liste g en erik ih p a ra m e ta ra isp red p o v ra tn e v red n o sti, ovako:
//: genericki/GenerickeMetode.java public class GenerickeMetode { public <T> void f(T x) { System.out.println(x.getClass().getName());

}
public static void main(String[] args) { GenerickeMetode gm = new GenerickeMetode(); gm.f (""); gm.f(l); gm.f(1.0); gm.f(l.OF); gm.f('c'); gm.f(gm);

}
} /* Ispis: java.lang.String java.lang.Integer java.lang.Double java.lang.Float java.1ang.Character GenerickeMetode

* ///:Klasa GenerickeMetode nije p a ra m e triz o v a n a , iako i klasa i n je n e m eto d e m o g u biti p a ra m e triz o v a n e istovrem eno. Ali u ovom sluaju, p a ra m e ta r tip a im a sam o m e to d a f ( ), to se vidi iz liste p a ra m etara ispred p o v ra tn o g tip a m eto d e.

Poglavlje 15: Generiki tipovi

497

V odite ra u n a o to m e d a p a ra m e tre tip a g enerike klase m o ra te d a zad ate p rilik o m pravljenja n jen o g p rim e rk a . Ali tip o v e p a ra m e ta ra generike m eto d e o b i n o n e m o ra te d a zadajete, p o to p rev o d ilac to m o e d a sh v ati i u ra d i u m e sto vas. To se n aziva zakljuivartje o tipu argum enta (engl. type a rg u m en t inference). Stoga pozivi m e to d e f ( ) izgledaju kao pozivi o b in e m eto d e; izgleda k ao da je f ( ) p rek lo p ljen a b esko naan broj p u ta . O n a p rim a ak i a rg u m e n t tip a GenerickeMetode. P ri p o ziv im a m e to d e f ( ) u k o jim a su u p o tre b lje n i p ro sti tip ov i, n a scen u stu p a a u to m atsk o pak o vanje koje a u to m a tsk i o m o ta v a p ro ste tip o ve u o d g o varaju e objekte. U stvari, g enerike m e to d e i a u to m a tsk o p ak o v an je m o g u d a z am e n e d eo k od a koji je p reth o d n o zahtev ao ru n u k o nverziju tipo va.

Veba 9: (1 ) Izm en ite GenerickeMetode.java tak o da f ( ) p rim a tri arg u m e n ta razliitih


p a ra m e triz o v a n ih tipova.

Veba 10: (1) Izm en ite p re th o d n u v ebu tak o d a je d a n o d a rg u m e n a ta m eto d e f ( ) ne


b u d e p ara m etrizo v a n .

Korienje zakljuivanja o tipu argumenta


Jed n a o d p rim e d a b a n a ra u n generik ih tip o v a glasi: zbog n jih k o d po staje jo o p irniji. P ogledajm o p ro g ra m cuvanje/MapaLista.java iz poglavlja uvanje objekata. O vako izgleda pravljenje Mape Lista:
Map<0soba, List<? extends L j u b i m c i 1ju di SLjubimcima = new H a s h M a p O s o b a , List<? extends L j u b i m c i ( ) ;

(O vu u p o tre b u rezervisane rei extends i zn ak a p ita n ja o b jasn iem o u nastavku poglavlja.) Izgleda da se p o navljate i d a bi p rev o d ilac treb alo d a iz je d n e liste generikih a rg u m e n a ta shvati ta d a stavi u d ru g u . N aalost, o n to ne m o e, ali zakljuivanje o tip u a rg u m e n ta u generikoj m e to d i m oe d a d o n e se neka po jed no stav ljen ja. P rim e ra radi, n ap rav i e m o u slu n u klasu s ra z n im sta ti n im m e to d a m a koja prav i najee u p o tre b ljavane realizacije razn ih k o n tejn era:
//: net/mindview/util/Nova.java // Uslune metode koje pravljenje generikih kontejnera // pojednostavljuju tako to same zakljuuju o tipu argumenta. package net.mindview.util; import j a v a . u t i l .*; public class Nova { public static <K,V> Map<K,V> map() return new H a s h Ma p< K, V> (); {

}
public static <T> List<T> list() return new Ar ra yL i s t < T > ( ) ; {

}
public static <T> LinkedList<T> 1L i s t () { return new LinkedLis t <T >();

498

Misliti na Javi

public static <T> Set<T> set() return new Ha sh Se t< T> ();

)
public static <T> Queue<T> queue() return new Li nk edList<T>(); {

}
// Primeri: public static void main(String[] List<String> ls = Nova.list(); LinkedList<String> lls = No va .1L i s t (); Set<String> ss = Nova.set(); Queue<String> qs = Nova.queue(); args) { Map<String, L i s t < S t r i n g sls = N o v a . m a p O ;

} } /.//U m eto d i m a i n ( ) m o ete vid eti p rim e re korienja ove klase - zak ljuivanje o tip u arg u m e n ta uklanja p o tre b u za p o n av ljan jem liste gen erik ih p a ra m e ta ra . To se m oe p rim e n iti n a cu v an je /M ap aL ista.jav a:
//: ge ne ricki/JednostavnijiLjubimci.java import podaciotipu.ljubimci.*; import ja v a . u t i l .*; import net.mindview.util.*; public class JednostavnijiLjubimci { {

public static void m a i n ( S t r i n g [] args) // Ostatak koda je isti...

Map<0soba, List<? extends L j u b i m c i 1judiSLjubimcima = N o v a . m a p O ;

} } ///M ada je ovo zanim ljiv p rim e r zakljuivanja o tip u arg u m e n ta , teko je rei koliko je to zapravo korisno. O soba koja ita ko d m o ra da analizira i shvati o v u d o d a tn u b ib lio tek u i njen e posledice, pa je m o d a jed n ak o p ro d u k tiv n o ostaviti p rv o b itn u d efin iciju (m ad a im a m n o g o ponav ljanja) - d a ironija b u d e vea, jed n o sta v n o sti rad i. M e u tim , k ad a bi u s ta n d a rd n u Javinu b ib lio tek u bila d o d ata u slu n a klasa p o p u t g o rn je N o v a.jav a, bilo bi p a m e tn o koristiti je. Z akljuivanje o tip u arg u m e n ta fun k cio nie iskljuivo za d odeljivanje. U koliko rezultat poziva m e to d e kao to je N o v a .m a p () prosled ite kao a rg u m e n t d ru g o j m eto d i, prevodilac neep o k u ati da zakljui koji je tip a rg u m e n ta. Poziv te m e to d e o n e tre tira ti kao da je n jena p o v ra tn a v red n o st dod eljen a p ro m en ljiv o j tip a O b je c t. Evo je d n o g takvog (n eu speno g ) p rim era :
//: genericki/GraniceZakljucivanja.java import po da ci otipu.ljubimci.*; import ja v a . u t i l .*; public class GraniceZakljucivanja {

Poglavlje 15: Generiki tipovi

499

static voi f(Map<Osoba, List<? extends L j u b i m c i 1judiSLjubimcima) public static void main(String[] args) { // f ( N o v a . m a p O ) ; // Nee biti prevedeno {}

} } ///:-

Veba 11: ( 1) Ispitajte klasu Nova.java p rav ljen jem so p stv en ih klasa. D o kaite d a Nova s
njim a rad i ispravno.

Eksplicitno zadavanje tipa


T ip generike m eto d e m o ete zad ati eksplicitno, iako se to retk o koristi. To se ra d i tako to se tip u p ie u n u ta r znakova m an je o d i vee o d , iza tak e i n e p o sre d n o isp red im en a m eto de. Kada iz iste klase p ozivate m e to d u , m o ra te p isati this isp red take, a kad a ra d ite sa sta ti n im m e to d a m a , isp red take m o ra te pisati im e klase. P ro b le m p rik azan u p ro g ra m u GraniceZakljucivanja.java m o em o reiti tak v o m sin tak so m :
//: genericki/IzricitoZadavanjeTipa.java import po da ci ot ip u. lj ub im ci.*; import ja v a . u t i l .*; import net.mindview.util.*; public class IzricitoZadavanjeTipa { static void f(Map<0soba, L i s t < L j u b i m c i 1judiSLjubimcima) public static void main(String[] args) f (Nova.<0soba, L i s t < L j u b i m c i m a p ( ) ) ; { {}

} } ///= N aravno, tim e se gubi p re n o st korienja klase Nova koja sm an ju je k o liin u pisan ja, ali je d o d a tn a sintaksa p o tre b n a sam o kada ne piete n are d b e dodeljivan ja. V eba 12: (1) P onovite p re th o d n u vebu uz eksp licitn o zadavanje tipova.

Argumenti promenljive duine i generike metode


G enerike m eto d e i prom en ljiv e liste arg u m e n a ta lepo rade zajedno:
//: ge nericki/GenerickiArgumentiPromenljiveDu zine.java import ja v a . u t i l .*; public class Ge ne ri ck iA rg um en tiPromenljiveDuzine { public static <T> List<T> napraviLi s t u ( T . .. args) List<T> rezultat = new A r ra yL is t< T> (); for(T stavka : args) r e zu lt at .a dd (s ta vk a); return rezultat; {

500

Misliti na Javi

public static void main(String[] args) { List<String> 1s = napraviListuC'A"); System.out.println(ls); 1s = napraviListu("A", "B", "C"); System.out.println(ls); 1s = napravi Li stu("ABCDEFFHIJKLMNOPQRSTUVWXYZ".split ("")); System.out.println(ls);

}
} /* Ispis: [A] [A, B, C] [, A, B, C, D, E, F, F, H, I, J, K, L, M, N, 0, P, Q, R, S, T, U, V, W, X, Y, Z]

* ///:O vde p rik a z a n a m eto d a napraviL istu( ) im a istu fu n k c io n a ln o st k ao m eto d a java.util.A rrays.asList( ) iz s ta n d a rd n e biblioteke.

Generika metoda za upotrebu s Generatorima


Za p o p u n jav an je k o n te jn e ra (p o d tip o v a klase C o lle c tio n ) p o d e sn o je u p o tre b iti g enerator. O v u o p erac iju je u m e sn o u o p titi:
//: genericki/Generatori.java // Usluna metoda za korienje s Generatorima. import genericki.kafa.*; import java.util.*; import net.mindview.util.*; public class Generatori { public static <T> Co11ection<T> popuni(Collection<T> kntnr, Generator<T> gen, int n) { for(int i = 0; i < n; i++) kntnr.add(gen.sledeci()); return kntnr;

}
public static void main(String[] args) { Collection<Kafa> kafa = popuni( new ArrayList<Kafa>(), new GeneratorKafeO , 4); for(Kafa c : kafa) System.out.println(c); Collection<Integer> fbrojevi = popuni( new ArrayList<Integer>(), new Fibonacci(), 12); for(int i : fbrojevi) System.out.print(i + ", ");

}
} /* Ispis: Americano 0 SMlekom 1

Poglavlje i S: Generiki tipovi

501

Am ericano 2 Moka 3 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144,

* ///:O b ra tite p a n ju n a to d a se m e to d a p o p u n i( ) m oe tra n sp a re n tn o p rim e n iti i n a k o n te jn e r Kafa i na g e n e ra to r Integer.

Veba 13: (4) P rck lo p ite m e to d u p o p u n i() tak o d a arg u m en ti i p o v ra tn i tip o v i re d o m b u d u List, Queue i Set, p o d tip o v i klase Collection. N a taj n a in neete izgubiti tip kontejn e ra . M oete li p re k lap a n jem u in iti d a se List i LinkedList razlikuju?

Generator opte namene


O va klasa p rav i G enerator za svaku klasu koja im a p o d raz u m e v an i k o n stru k to r. D a b i se pisalo m an je koda, klasa o b u h v ata i g eneriku m e to d u koja prav i ElementarniGenerator:
//: ne t/ mi nd vi ew/util/E1ementarniGenerator.java // Automatski pravi Ge nerator date klase konstruktor (bez argumenata). // koja ima podrazumevani package net.mindview.util; public class ElementarniGenerator<T> implements Generator<T> { private Class<T> tip; public El em entarniGenerator(Class<T> tip){ this.tip - tip; } publ ic T sledeci () { try { // Pretpostavlja da je tip javna klasa: return t i p. ne wl ns ta nc e( ); } catch(Exception e) { throw new R u n t im eE xc ep ti on (e );

} }
// Pravi podrazumevani generator kada joj se da leksema tipa: public static <T> Genera to r< T> n a p r a v i (C1ass<T> tip) return new E l em en ta rn iG en er at or< T> (t ip ); {

} } ///:O va klasa p red stavlja e le m e n ta rn u realizaciju koja pravi objekte klase koja je (1) javna (p o to je E lem entarniG enerator u zaseb n o m p ak etu , klasa o kojoj se rad i m o ra im ati javni, a ne sa m o p ak e tn i p ristu p ), i (2) im a p o d ra zu m e v an i k o n s tru k to r (onaj koji ne p rim a a rg u m e n te ). Da biste na p rav ili jed an o d o b jek ata tip a ElementarniGenerator, pozivate m e to d u n a p ra v i( ) i p ro sle u jete joj leksem u (engl. token) tip a koji h o ete d a generiete. G enerika m e to d a n a p ra v i( ) o m o g u u je d a kaete Elem entarniG enerator.napravi(MojTip.cIass) u m esto nevv ElementarniGenerator<M ojTip>(M ojTip.cIass), to bi bilo runije. Na p rim e r, ova je d n o sta v n a klasa im a p o d raz u m e v an i k o n stru k to r:

502

Misliti na Javi

//: genericki/PrebrojaniObjekat.java public class PrebrojaniObjekat { private static long brojac = 0; private final long id = brojac++; public long id() { return id; } public String t o S t r i n g O { return "PrebrojaniObjekat " + i d ;}

} ///:Klasa PrebrojaniO bjekat p ra ti koliko je so p stv en ih p rim e ra k a n ap rav ila i to p rijavljuje svojom m e to d o m to S trin g (). P o m o u klase Elem entarniG enerator lako je n ap rav iti G enerator za PrebrojaniOb-

jekat:
//: ge nericki/PrimerElementarnogGeneratora.java import net.mindview.util.*; public class PrimerElementarnogGeneratora { public static void main(String[] args) { Generator<PrebrojaniObjekat> gen = El em en ta rn iG en er at or. na pr av i( Pr eb ro ja ni Obj ek at .c la ss ); for(int i = 0 ; i <5; i++) System .o ut .p ri nt ln (ge n. sl ed ec i());

}
} /* Ispis: PrebrojaniObjekat 0 PrebrojaniObjekat 1 PrebrojaniObjekat 2 PrebrojaniObjekat 3 PrebrojaniObjekat 4

* ///:Iz ovoga v idite koliko generika m eto d a sm an ju je koliinu pisanja kada se pravi objekat

Generator. Javin generiki m eh an izam vas p rim o ra v a da ipak pro sled ite objekat tipa Class, pa ga o n d a m oete u p o treb iti i za zakljuivanje o tip u arg u m en ta u m eto d i napravi( ). Veba 14: ( 1) Izm en ite PrimerElem entarnogGeneratora.java tako da se Generator pravi eksplicitno (tj. u m esto generike m eto d e n a p ra v i( ) u p o tre b ite eksplicitan k o n stru k to r).

Pojednostavljenje upotrebe n-torki


Z akljuivanje o tip u arg u m e n ta z ajed n o sa u v o zo m sta ti n ih m e to d a o m o g u u je da se p re th o d n o prik a za n e n -to rk e p re ra d e u b ib lio te k u o p tije n am en e. O vde em o n -to rk e praviti p rek lo p lje n o m statin o m m e to d o m :
//: net/mindview/util/N_torka.java // Biblioteka n-torki u kojoj se upotrebljava // zakljuivanje o tipu argumenta. package net.mindview.util;

Poglavlje 15: Generikl tipovi

503

public class N_torka { public static <A,B> Dvojka<A,B> n_torka(A a, B b) { return new Dvojka<A,B>(a, b);

}
public static <A,B,C> Trojka<A,B,C> n_torka(A a, B b, C c) { return new Trojka<A,B,C>(a, b, c ) ;

}
public static <A,B,C,D> Cetvorka<A,B,C,D> n_torka(A a, B b, C c, D d) { return new Cetvorka<A,B,C,D>(a, b, c, d);

}
public static <A,B,C,D,E> Petorka<A,B,C,D,E> n_torka(A a, B b, C c, D d, E e) { return new Petorka<A,B,C,D,E>(a, b, c, d, e ) ;

} } ///= D a b ism o ispitali p ro g ra m N_torka.java, p rilag o d iem o p ro g ra m IspitivanjeEntor-

ki.java:
//; genericki/IspitivanjeEntorki2.java import net.mindview.util.*; import static n e t. mi nd vi ew .u ti 1.N_torka.*; public class IspitivanjeEntorki2 { static Dv ojka<String,Integer> f() return n_ to r k a ( " z d r a v o " , 47); {

}
static Dvojka f2 () { return n _ to rk a( "z dr av o", 47); | static Tr oj ka <A mf ibija,String,Integer> g() { return n_torka(new Amfi bij a ( ) , "zdravo", 47);

}
static Ce tv or ka <V oz i1o,A m f ibij a ,S tr in g , Integer> h() { "zdravo", 47); return n_torka(new Vozilo(), new A m f i b i j a O ,

}
static P e to rk a< Vo zi lo ,A mfibija,String,Integer,Double> k() { return n_torka(new Vozilo(), new Amfibija(), "zdravo", 47, 11.1);

}
public static void main(String[] S y s t e m . o u t .pr in tl n( ie si); S y s t e m . o ut .p ri nt ln (f2 () ); S y s t e m . o ut .p ri nt ln (g( )); S y s t e m . o ut .p ri nt ln (h( )); S y s t e m . o u t .p r i n t l n ( k ()); args) { Dv ojka<String,Integer> iesi = f();

504

Misliti n a Javi

} /* Ispis: (zdravo, 47) (zdravo, 47)

(80% podudaranja)

(Amfibija@7d772e, zdravo, 47) (Vozilo757aef, Amfibija@d9f9c3, zdravo, 47) (Vozilo@la46e30, Amfibija@3e25a5, zdravo, 47, 11.1)

* ///:V odite ra u n a o to m e da f ( ) vraa p aram etrizov an objekat tip a Dvojka, d o k f2 ( ) vraa n ep aram etrizo v an objekat tipa Dvojka. U ov om sluaju prevodilac ne u p o zo rav a na f2 (), zato to se p aram etrizo v an a v red n o st ne up otreb ljav a na p aram etrizo v an nain; n a neki nain , o n a se svodi navie" n a n ep aram etrizo v an tip Dvojka. M e u tim , kada biste pokuali da rezu ltat f2 ( ) uhvatite u p aram etrizo v an tip Dvojka, prevodilac b i d ao upozorenje.

Veba 15: (1) Prov erite p re th o d n u tv rd n ju . Veba 16: (2) D o d ajte Sestorku p ro g ra m u N_torka.java i ispitajte ga p ro g ra m o m IspitivanjeEntorki2.java.

Usluna metoda za Set


Kao jo jed an p rim e r u p o tre b e g en erikih m eto d a, ra z m o tri e m o m a te m a tik e o d n o se koji se m o g u izraziti sk u p o v im a (o b jek tim a tip a S et). N jih je p o d e sn o definisati generik im m e to d a m a koje se m o g u u p o treb ljav a ti sa svim razliitim tip o v im a:
/ / : net/mi ndview /u ti1/ S k u p o v i .java package net.mindview.util; import j a v a . u t i l .*; public class Skupovi {

public static <T> Set<T> unija(Set<T> a, Set<T> b) { Set<T> rezultat = new Ha sh S e t < T > ( a ) ; r e z u l t at .a dd Al l(b); return rezultat;

}
public static <T> Set<T> presek(Set<T> a, Set<T> b) r e zu lt at .r et ai nA ll(b); return rezultat; { Set<T> rezultat = new Ha sh Se t< T> (a );

}
// Oduzmi podskup od nadskupa: { public static <T> Set<T> r a z l i ka(Set<T> nadskup, Set<T> podskup) r e z u l t a t . r e m o v e A U (p odskup); return rezultat; Set<T> rezultat = new H a s h Se t< T> (n ad sk up );

Poglavlje 15: Generiki tipovi

505

// Refleksivno--nije sve u preseku: public static <T> Set<T> komplement(Set<T> a, Set<T> b) { return razlika(unija(a, b), presek(a, b));

} } ///:Prve tr i m e to d e d u p lira ju p rv i a rg u m e n t k o p ira n je m n jeg o v ih referenci u n o v o b jek at tip a HashSet, pa se a rg u m e n ti Skupovi n e m e n ja ju d ire k tn o . P o v ra tn a v re d n o st je n o v o b jek at tip a Set. O ve etiri m e to d e predstavljaju m atem atik e o p eracije sa sk u p o v im a: u n ija ( ) vraa Set koji sadri k o m b in aciju dva a rg u m e n ta, p resek ( ) vraa Set koji sad ri zajed n ik e elem e n te dvaju a rg u m en ata , razlik a( ) o d u z im a elem en te sk u p a podskup o d e lem en ata sk u p a nadskup, a kom plem ent( ) vraa Set svih elem en ata koji n isu u p resek u . Kao je d n o stav an p rim e r u p o tre b e ovih m eto d a, sledei enum (n a b ro ja n i tip ) sad ri im e n a razn ih v o d e n ih boja:
//: genericki/vodeneboje/Vodeneboje.java package genericki.vodeneboje; public enum Vodeneboje { ZINC, LEM0N_YELL0W, MEDIUM_YELLOW, DEEP_YELLOW, ORANGE, BRILLIANT_RED, CRIMSON, MAGENTA, ROSE_MADDER, VIOLET, CERULEAN_BLUE_HUE, PHTHALO_BLUE, ULTRAMARINE, COBALT_BLUE_HUE, PERMANENT_GREEN, V I R I D I A N H U E , SAP_GREEN, YELL0W_0CHRE, BURNT_SIENNA, RAW_UMBER, BURNT_UMBER, PAYNES_GRAY, IVORY_BLACK

> ///:O d g o v ara n am (kako ne b ism o m o ra li d a n a v o d im o p u n a im en a ) d a p re th o d n u listu n ab ra ja n ja statin o uvezem o u sledei p rim er. U n je m u se u p o tre b lja v a EnumSet, alatka Jave SE5 za lako pravljenje sk u p o v a (o b jek ata tip a Set) o d n a b ro ja n ih tip o v a (enum). (O klasi EnumSet vie p ri a m o u poglavlju N a brojani tipovi.) O vde se sta ti n o j m e to d i Enum Set.range( ) daju p rv i i posled n ji elem en t opsega (engl. rangc) koji treb a n ap rav iti u rezu itu ju em skupu:
/ / : genericki/SkupoviVodenihBoja.java import genericki.vodeneboje.*; import j a v a . u t i l .*; import static net.mind vi ew .u ti l.Print.*; import static net.mind vi ew .u ti l.Sku po vi.*; import static genericki.vodeneboje.Vodeneboje.*; public class SkupoviVodenihBoja { public static void main(String[] args) Set<Vodeneboje> skupl = E n u m S e t .range(BRILLIANT_RED, VIRIDIAN H U E ) ; Set<Vodeneboje> skup2 = EnumSet.range(CERULEAN BLUE HUE, BURNT U M B E R ) ; {

506

Misliti na Javi

print("skupl: print("skup2:

" + skupl); " + skup2); skup2): " + unija(skupl, skup2)); " + podskup); " + " + " +

print("unija(skupl,

Set<Vodeneboje> podskup = presek(skupl, skup2); print("presek(skupl, skup2): razlika(skupl, podskup)); print("razlika(skup2, podskup): razlika(skup2, podskup)); print("kompleinent(skupl, skup2): komplement(skupl, skup2)); print("raz1ika(skupl, podskup):

}
} /* Ispis: skupl: (primer) [BRILLIANT_RED, CRIMSON, MAGENTA, ROSE_MADDER, VIOLET,

CERULEAN_BLUE_HUE, PHTHALO_BLUE, ULTRAMARINE, COBALT_BLUE_HUE, PERMANENT_GREEN, VIRIDIAN_HUE] skup2: [CERULEAN_BLUE_HUE, PHTHALO_BLUE, ULTRAMARINE, COBALT_BLUE_HUE, SAP_GREEN, YELLOW_OCHRE, BURNT_SIENNA, PERMANENT_GREEN, V I R I D I A N J U E , RAW_UMBER, BURNT_UMBER] unija(skupl, skup2): [SAP_GREEN, ROSE_MADDER, VELLOW_OCHRE, PERMANENT_GREEN, BURNT_UMBER, COBALT_BLUE_HUE, VIOLET, BRILLIANT_RED, RAW_UMBER, ULTRAMARINE, BURNT_SIENNA, CRIMSON, CERULEAN_BLUE_HUE, PHTHALO_BLUE, MAGENTA, VIRIDIAN_HUE] presek(skupl, skup2): [ULTRAMARINE, PERMANENT_GREEN, COBALT_BLUE_HUE, PHTHALO_BLUE, C E R U LE AN _B LU E_ HU E, VIRIDIAN_HUE] razl i ka ( s k u p l , p o d s k u p ) : [ROSE_MADDER, CRIMSON, VIOLET, MAGENTA, BRILLIANT_RED] razlika(skup2, podskup): komplement(skupl, MAGENTA] skup2): [RAW_UMBER, SAP_GREEN, VELLOW_OCHRE, [ S A P G R E E N , ROSE_MADDER, YELLOW_OCHRE, RAW_UMBER, BURNT_SIENNA, CRIMSON, BURNT_SIENNA, BURNT_UMBER] BURNTJJMBER, VIOLET, BRILLIANT_RED,

* ///:R ezu ltate svake operacije v id ite u rezu ltatu p ro g ra m a. U n a re d n o m p rim e ru u p o tre b ljen a je m e to d a S k u p o v i.r a z lik a () da bi se pokazale razlike izm edu ra zn ih C o lle c tio n i M ap ldasa u p ak etu ja v a .u til:
/ / : net/mindview/util/RazlikeKontejnerskihMetoda.java package net.mindview.util; import java.lang.reflect.*; import j a v a . u t i l .*; public class RazlikeKontejnerskihMetoda { static Set<String> skupMetoda(Class<?> tip) for(Method m : tip. ge tM et ho ds ()) rezultat .a dd (m .g et Nam e( )) ; return rezultat; { Se t<String> rezultat = new T r ee Se t< St ri ng >( );

Poglavlje 15: Generiki tipovi

507

static void interfejsi(C1ass<?> tip) System.out.print("Interfejsi u " + tip.getSimpleName() + ");

List<String> rezultat = new A r r a yL is t< St ri ng >( ); for(Class<?> c : ti p. ge t l n t e r f a c e s O ) r e z u l t a t .a d d ( c .getSi mpl e N a m e ()); Sy st em .out.println(rezultat);

}
static Set<String> objekat = skupMeto da (O bj ec t. cla ss ); static { ob je ka t. ad d( "k lo n" ); } static void razlika(Class<?> nadskup, Class<?> podskup) S y st em .o ut.pri nt(nadskup.getSi m p l e N a m e () + 1 1 extends " + podskup.getSimpleName() + ", dodaje: Set<String> komp = Skupovi.razlika( s k upMetoda(nadskup), sk up Me to da (p od sk up )); k omp.removeAll(objekat); // Ne prikazuj metode klase 'Object' S y st em .o ut.println(komp); in te rf ej si (n ad sk up ); "); {

}
public static void main(String[] args) System.out.println("Collection: skupMetoda(Collection.class)); i nt er faces(Col1ecti o n .cla s s ) ; razlika(Set.class, Collecti on .c la ss ); razlika(HashSet.class, Set.class); ra z l i ka(Li nkedHashSet.class, H a s h Se t. cl as s); razlika(TreeSet.class, Se t. c l a s s ) ; razlika(List.class, Collection.cla s s ) ; ra zl ika (A rr ay Li st.class, L i s t .class); ra zli ka (LinkedList.class, Li st.class); razlika(Queue.class, Co ll ec ti on .c la ss ); razlika(PriorityQueue.class, Queu e.c l a s s ) ; S y s t e m . o u t .p r i n t l n ("Map: " + skupMetoda(Map.cla s s ) ); razlika(HashMap.class, Ma p. cl as s); razli ka(LinkedHashMap.class, H a s h Ma p. cl as s); razlika(SortedMap.class, M a p. cl as s); razlika(TreeMap.class, M a p. cl as s); " + {

} III-Re7Ailtat ovog p ro g ra in a bio je u p o treb ljen u Saetku poglavlja uvanje objekata.

Veba 17: (4) P rouite JDK d o k u m en ta ciju za EnumSet. V ideete da je d efin isan a i m eto d a c lo n e ( ). M e u tim , n jo m e ne m oete k lo n irati referen cu interfejsa Set p ro sle e n u u p ro g ra m u Skupovi.java. U m ete Ii da izm enite p ro g ra m Skupovi.java tako d a rad i i opti sluaj interfejsa Set (kao to je p rik azan o ) i specijalni sluaj interfejsa EnumSet (u p o tre bite c lo n e ( ) u m e sto da p rav ite nov HashSet)?

508

Misliti na Javi

Anonim ne unutranje klase


G eneriki tip o v i se m o g u k o ristiti i sa u n u tra n jim klasarna i sa a n o n im n im u n u tra n jim klasam a. Evo p rim e ra koji realizuje in terfejs G e n e ra to r p o m o u a n o n im n ih u n u tra n jih klasa:
//: genericki/S1uzbeni k u B a n c i .java // Pojednostavljena simulacija slubenika u banci. import j a v a . u t i l .*; import net.mindview.util.*; class Klijent { private static long brojac = 1; private final long id = brojac++; private Klijent() {} { return "Klijent " + id; } { } public String t o S t r i n g O

// Metoda za pravljenje objekata tipa Generator: public static Ge ne ra to r< Kl ij en t> generator() return new Generator<Klijent>() public Klijent sledeci() {

{ return new Klijent();

}; } }
class Sluzbenik { private static long brojac = 1; private final long id = brojac++; private Sluzbenik() {} { return "Sluzbenik " + id; } public String t o S t r i n g O

// Samo jedan objekat tipa Generator: public static Ge ne ra to r< Sl uz be ni k> ge nerator = new Generator<Sluzbenik>() { { return new Sluzbenik(); } public Sluzbenik sledeci()

}; }
public class SluzbenikuBanci {

public static void us lu zi(Sluzbenik t, Klijent c) { System.out.println(t + " usluuje klijenta " + c);

}
public static void m a i n (S tr in g[] args) Random slucajan = new Random(47); Q u e u e < K l ijent> redZaCekanje = new LinkedList<Klijent>(); Generatori.popuni(redZaCekanje, K1 ij en t. ge ne ra to r( ), 15); List<Sluzbenik> sluzbenici = new ArrayList<Sluzbenik>(); G e n e r a t o r i . p op un i( slu zb en ic i, S 1 uzbe ni k.generator, 4); for(Klijent c : redZaCekanje) us lu zi (s lu zb en ic i. get (s lu c a j a n .nextlnt (s lu zb en ic i.s i ze ())), c ) ; {

Poglavlje 15: Generiki tipovi

509

) /* Ispis: Sluzbenik 3 usluuje kl ijenta K1ijent 1 Sluzbenik 2 usluuje kl ijenta K1 ijent 2 Sluzbenik 3 usluuje kl ijenta K1 ijent 3 Sluzbenik 1 usluuje kl ijenta K1 ijent 4 Sluzbenik 1 usl uuje kl ijenta K1 ijent 5 Sluzbenik 3 usluuje kl ijenta K1 ijent 6 Sluzbenik 1 usluuje kl ijenta K1 ijent 7 Sluzbenik 2 usluuje kl ijenta K1ijent 8 S1uzbeni k 3 usluuje kl i jenta K1 ijent 9 Sluzbenik 3 usluuje kl ijenta K1ijent 10 Sluzbenik 2 usluuje kl ijenta Klijent 11 Sluzbenik 4 usluuje kl ijenta K1 ijent 12 Sluzbenik 2 usluuje kl ijenta K1ijent 13 Sluzbenik 1 usluuje kl ijenta K1 ijent 14 Sluzbenik 1 usluuje klijenta Klijent 15 *///:-

I Klijent i Sluzbenik im aju p riv a tn e k o n stru k to re , im e vas p rim o ra v a ju d a k o ristite o b jek te tip a Generator. Klijent im a m e to d u g en e ra to r( ) koja p rav i n o v objek at tip a Generator<K Iijent> k ad god je pozovete. M oda v am nee b iti p o tre b n i svi ti Generatori, a SluzbeniJk pravi sam o je d a n javni generator. O b a p ristu p a m o ete v ideti na d elu u m eto d a m a p o p u n i( ) u n u ta r m e to d e m a in ( ). Poto su statin i i m eto d a gen erato r( ) o b jek ta Klijent i o b jek at tipa Generator u klasi Sluzbenik, o n i ne m o g u biti d eo interfejsa, pa ovaj id io m n e m o ete d a u o p tite p o m o u g enerik ih tipov a. U prko s to m e , o n rad i p rili n o d o b ro s m e to d o m p o p u n i( ). O stale verzije p ro b lem a ekanja u red u ra z m o tri em o u poglavlju Paralclno izvravanje. Veba 18: (3) N a o sn o v u p ro g ra m a SluzbenikuBanci.java, n a p rav ite Okean gde p o sto je VelikaRiba i MalaRiba, i p rv a jed e d ru g u .

Pravljenje sloenih modela


G en erik i tip o v i o m o g u u ju je d n o sta v n o i b ezb ed n o pravljenje sloenih m odela. N a p rim er, lako m o em o da n a p ra v im o L istu n -to rk i:
//: ge ne ri ck i / L i s t a N _ t o r k i .java // Kombinovanje generikih tipova da bi se dobili // sloeni generiki tipovi. import j a v a . u t i l .*; import net.mindview.util.*; public class ListaN_torki<A,B,C,D> extends A r r a y L i s t < C e t v o r k a < A , B , C , D { public static void main(String[] args) { ListaN_torki<Vozilo, Amfibija, String, Integer> tl = new ListaN_torki<Vozilo, Amfibija, String, Integer>(); t l .add(Ispiti v a n j eE nt or ki .h ()); tl .add (I sp it iv an je En tor ki.h ());

510

Misliti na Javi

for(Cetvorka<Vozilo,Amfibija,String,Integer> i: tl) Sy st em .o ut .p ri nt ln (i);

}
} /* Ispis: (75% podudaranja) (Vozilo@llb86e7, Amfibija@35ce36, zdravo, 47) (Vozilo@757aef, Amfibija@d9f9c3, zdravo, 47)

* ///:Jeste da se m o ra lo p rilin o p isati (n a ro ito u p rav ljen ju ite ra to ra ), ali sm o dobili m o n u stru k tu ru p o d ata k a u k ra tk o m p ro g ra m u . Evo jo je d n o g p rim e ra koji p o k azu je ko lik o je je d n o sta v n o prav ljen je sloenih m o d ela p o m o u generikih tipova. Iako je svaka klasa n ap rav ljen a kao zaseb an b lo k , celina im a m n o g o delova. U ovom sluaju, m o d el je p ro d av n ica s pasaim a, p o lica m a i pro izv o d im a:
//: genericki/Prodavnica.java // Pravljenje sloenog modela pomou generikih kontejnera. import ja v a . u t i l .*; import net.mindview.util.*; class Proizvod { private final int id; private String opis; private double cena; public Proizvod(int IDbroj, String ops, double cena){ id = IDbroj; opis = ops; this.cena = cena; S y st em .o ut .println(toString());

}
public String t o S t r i n g O return id + { " + opis + ", cena: $" + cena; {

}
public void promenaCene(double change) cena += change;

}
public static Generator<Proizvod> ge nerator = new Generator<Proizvod>() { { * 1000.0) + 0.99); private Random slucajan = new Random(47); public Proizvod sledeci() return new Pr oi zv od (s l u c a j a n , n e x t I n t (1000), "Test", Math.round(slucajan.nextDouble()

} }; }
class Polica extends ArrayLis t< Pr oi zv od > { public Polica(int nProizvoda) { nProizvoda); G e ne r a t o r i .po pu n i ( t h i s , Proizvod.generator,

} }

Poglavlje 15: Generiki tipovi

511

class Pasaz extends Ar ra yL ist<Polica> { public Pasaz(int nPolica, int nProizvoda) for(int i = 0; i < nPolica; i++) add(new P o l i ca (n Pr oi zv od a) ); {

} }
class Blagajna {} class Kancelarija {} public class Store extends Ar ra yL is t< Pa sa z> { private ArrayList<Blagajna> blagajne = new A r r a yL is t< Bl ag aj na >(); private Kancelarija kancelarija = new Ka nc el ar ij a( ); public Store(int nPasaza, int nPolica, int nProizvoda) for(int i = 0; i < nPasaza; i++) add(new Pasaz(nPolica, n P r o i z v o d a ) ) ; {

}
public String toString() for(Pasaz a : this) for(Polica s : a) for(Proizvod p : s) { rezulta t. ap pe nd (" \n "); rezu lt at .a pp en d( p); { StringBuilder rezultat = new StringBuilder();

}
return r e z u l t a t . t o S t r i n g O ;

}
public static void m a i n ( S t r i n g [] args) { System.out.println(new P r o d a v n i c a (14, 5, 10));

}
} /* Ispis: 258: Test, cena: $400.99 861: Test, cena: $160.99 868: Test, cena: $417.99 207: Test, cena: $268.99 551: Test, cena: $114.99 278: Test, cena: $804.99 520: Test, cena: $554.99 140: Test, cena: $530.99

* ///:Kao to v id ite u m eto d i P ro d a v n ic a .to S tr in g ( ), dobili sm o m n o g o slojeva k o n tejn era koji su u p rk o s to m e upravljivi i o m o g u u ju b ezb e d an rad s tip o v im a. Im p resiv n o je to to sastavljanje takvog m o d ela nije teko. Veba 19: (2) Na o sn o v u p ro g ram a P ro d a v n ic a .ja v a , n a p ra v ite m o d el tere tn o g b ro d a pod eljen o g na skladita.

512

MislitinaJavi

Tajanstveno brisanje
K ako b u d e te sve tem eljnije u p o zn av ali generike tipove, nailaziete na stvari koje v am sp oetk a nee biti logine. N a p rim er, iako se m oe rei ArrayList.class, n e m o e se rei ArrayList<Integer>.class. I p o g led ajte ovo:
//: genericki/EkvivalentnostTipovaZbogBrisanja.java import j a v a . u t i l .*; public class EkvivalentnostTipovaZbogBrisanja { public static void main(String[] args) { Class cl = new Ar ra yL is t< St ri ng >( ).g et Cl as s( ); Class c2 = new Arra yL is t< In te ge r> (), ge tC 1a ss (); System.out.println(cl == c2);

}
} /* Ispis: true

* ///:Lako je d o k azati d a su ArrayList<String> i ArrayList<Integer> razliiti tipo vi. Razliiti tip o v i se p o n aa ju razliito, i ako p o k u ate, p riin e ra rad i, da stavite Integer u ArrayList<String>, d o biete razliito p o n a an je (to nee u speti) nego k a d a Integer stavite u ArrayList<Integer> (to e u sp eti). Pa ipak, go rn ji p ro g ra m kae d a su o b a isti tip. Evo p rim e ra koji e vas z b u n iti jo vie:
//: ge ne ricki/Izgubljenelnformacije.java import j a v a . u t i l .*; class Frob {} class Fnorkle {} class Kvark<Q> {} class Ce st ica<P0L0ZAJ,M0MENAT> {} public class Izgubljenelnformacije { public static void main(String[] args) { List<Frob> lista = new Ar ra y L i s t < F r o b > ( ) ; Ma p < F r o b , Fnorkle> mapa = new HashMap< Fr ob ,F no rk le> (); Kvark<Fnorkle> kvark = new K v a r k< Fn or kl e> (); Cestica<Long,Double> p = new Cestic a< Lo ng ,D ou bl e>( ); S y s t e m .o ut .p ri nt ln (Ar ra ys.toStri n g ( l i s t a . g e t C l a s s O .g et Ty pe Pa ra me te rs ()) ); S y s t e m . o u t .pri n t l n ( A r r a y s .toStri n g ( m a p a . g e t C l a s s O .g et TypeParameters())); S y s t e m . o u t .pri n t 1n (A r r a y s .toStri n g ( k v a r k . g e t C l a s s O .getTy pe Pa ra me te rs())); System.out.println(Arrays.toString( p . g e t C l a s s ().g et Ty pe Pa ra me te rs ()));

Poglavlje 15: Generiki tipovi

513

} /* Ispis:

[E]
[K, V]

[Q]
[POL OZ AJ, MOMENAT]

* ///:U JDK d o k u m en tac iji pie d a m e to d a CIass.getTypeParameters( ) vraa niz o bjekata tip a TypeVariable koji pred stav ljaju p ro m en ljiv e tip a dek larisan e u generikoj deklaraciji . . . . Iz toga bi sledilo da je m o g u e o tk riti p a ra m e ta rsk e tipove. M e u tim , k a o to v id ite iz rezu ltata p ro g ra m a , otk rili sm o sam o id en tifik ato re koji uv aju m esta p a ra m e ta ra , to i nije p re te ra n o zanim ljivo. P rava istin a glasi:

U generikom kodu uopte nisu dostupne informacije o tipovima generikih param etara.
D akle, m o ete saznati stv ari kao to su id e n tifik a to r p a ra m e tra tip a i o g ran ie n ja gen erik o g tip a - ali n e m o ete o tk riti stv arn e p a ra m e tre tip o v a u p o tre b lje n e za p ravljenje o d re e n e instance. Ta injenica n a ro ito sm eta o n im a koji su ko ristili C + + i pred stav lja o sn o v n i p ro b le m p ri ra d u s Javinim g en erik im tip o v im a. Javini generiki tip o vi realizuju se uz brisanje (engl. erasure). To zn ai sledee: kad a u p o tre b ite generiki tip, b riu se sve specifine in fo rm ac ije o tip u . U n u ta r gen erik o g tip a m o ete znati sam o da u p o treb ljav ate objekat. Stoga List<String> i List<Integer> u v rem e izvravanja i jesu, zapravo, isti tip. O b a o blika bivaju o brisana" d o n jih ov og sirovog tipa , a to je List. T okom uenja Javinih g en erik ih tipova, gotovo d a e n ajtee b iti shvatiti b risa n je i kako se s n jim o p h o d iti. T im e em o se b av iti u n a re d n o m odeljku.

Pristup u C++-u
P o gledajm o p rim e r iz C + + -a u kojem su u p o tre b lje n i abloni (engl. tem plates). V ideete da je sin tak sa za p a ra m e trizo v a n e tip o v e p o tp u n o jed n ak a, p o to je Java n ap ra v ljen a na o sn o v u C + + -a :
//: g e n e ri ck i/ Sa bl on i.cpp #include <iostream> using namespace std; template<class T> class Manipulator { T obj; public: Manipulator(T x) { obj = x; } void manipulisi() { obj.f(); }

};
class ImaF { publi c : void f() { cout "ImaF::f()" endl; }

514

Misliti na Javi

int main()

ImaF imaf; Manipulator<ImaF> m a ni pu la to r( im af ); ma ni pu la to r. ma ni pu lis i(); } /* Ispis: ImaF: :f ()

III-K lasa M anipulator sadri o b jek at tip a T. Z an im ljiva je m e to d a m an ip u lisi( ) koja p o ziva m e to d u f ( ) za obj. Kako o n a m oe zn ati d a m eto d a f ( ) p o sto ji za p a ra m e ta r tip a T? P revodilac C + + -a p roverava tipove k ad a p rav ite p rim e ra k ab lo n a, p a u tre n u tk u p ra vljenja klase M anipulator<Im aF> vidi d a ImaF im a m e to d u f ( ). D a je n em a, do b ili biste g rek u u v rem e p rev o en ja, i b e z b ed n o st tip o v a bi bila ouvana. Pisan je ovakvog k o d a u C + + -U je jed n o stav n o , zato to k o d a b lo n a zn a tip p a ra m e ta ra svog ab lo n a u tre n u tk u k ad a treb a d a ga n ap ravi. Javini generiki tip o v i su drugaiji. N apisali sm o ImaF u Javi:
//: genericki/ImaF.java public class ImaF { public void f() { Sy stem.out.println ( " I m a F . f ()"); }

} III'-A ko i osta ta k p rim e ra n ap ie m o n a Javi, o n nee m oi d a se prevede:


//: ge nericki/Manipulacija.java // {Compi1eTimeError} class Manipulator<T> { private T obj; public Manipulator(T x) { obj = x; } // Greka: cannot find s y m b o l : method f(): public void mani pul i si () { obj.f(); } (Ne moe da se prevede)

}
public class Manipulacija { public static void main(String[] ImaF imaf = new ImaF(); Manipulator<ImaF> manipulator = new M a ni pu la to r< Im aF >( ima f); ma ni pu lator.manipuli s i (); args) {

III--

Z ah tev da m eto d a m a n ip u lis i( ) m o ra m oi da poziva f ( ) za o b j, Javin prevodilac zbog brisan ja ne m oe d a dovede u vezu sa in jenico m d a Im a F im a m eto d u f ( ). Da b ism o pozvali f ( ), m o ra m o p o m o i generikoj klasi dajui joj granicu (engl. bound) koja prev o d io cu k azuje da prihvata sam o tipove usaglaene s to m g ran ico m . Za to em o p o n o v o u p o treb iti rezervisanu re e x ten d s. Sledei p ro g ram e se usp en o prevesti, u pravo zbog granice:

Poglavlje 15: Generiki tipovi

515

//: genericki/Manipulator2.java class Manipulator2<T extends ImaF> { private T o b j ; public Ma nipulator2(T x) { obj = x; } public void manipulisi() { obj.f(); }

1 ///:G ran ica <T extends ImaF> kazuje d a T m o ra b iti tip a ImaF ili njegovih p o d tip o v a . A ko je to istin a, o n d a je b ezb ed n o pozvati f ( ) za obj. K aem o d a se p a ra m e ta r generikog tip a brie do svojeprve granice (g ran ica m o e b iti vie, to ete v ideti u n astav k u ) ili d a se brie p a ra m eta r tipa. P revodilac zapravo z a m e n ju je p a ra m e ta r tip a o n im to je o b risan o , p a u g o rn je m sluaju T b risa n jem p o staje ImaF, to je isto kao d a se T u telu klase zam e n i sa ImaF. M o d a ete isp rav n o p rim e titi d a u p ro g ra m u ManipuIator2.java, g eneriki tip o v i ne ra d e nita. M ogli ste i sam i d a obavite b risan je i d a n a p rav ite k lasu b ez g en erik ih tip o v a:
//: genericki/Manipulator3.java class Manipulator3 { private ImaF obj; public Ma ni pu la to r3 (I ma F x) { obj = x; } public void manipulisi() { obj.f(); }

} ///:T im e sm o doli do v an o g zakljuka: generiki tip o v i su k o risn i sam o k ad a h o ete d a u p o tre b ite p a ra m e tre tip a koji su optiji o d o d re e n o g k o n k retn o g tip a (i n jeg o v ih p o d tip o v a) - dakle, kada ho ete d a kod radi s razliitim klasam a. Z ato su, u k o risn o m generik om k o d u , p a ra m e tri tip a i n jihova p rim e n a o b i n o sloeniji o d p ro ste z am e n e klasa. M e u tim , to ne znai da je n eisp rav n o sve to je o blika <T extends ImaF>. N a p rim e r, ako klasa im a m e to d u koja vraa T, o n d a je generiki k o d k o rista n je r e v ra titi ta a n tip:
/ / : genericki/VracanjeGenerickogTipa.java class VracanjeGenerickogTipa<T extends ImaF> { private T obj; public Vr acanjeGenerickogTipa(T x) { obj = x; } public T get() { return obj; }

} ///Da biste p ro su d ili da li je korienje generik o g k o d a o p rav d a n o , m o ra te p ro ita ti sav kod i p ro cen iti d a li je d o v o ljn o sloen. G ran ice e m o d etaljn ije ra z m o triti u n astavku poglavlja. V cba 20: (1) N ap ravite interfejs s dve m eto d e i klasu koja ga realizuje i d o d a je jo je d n u m e to d u . U d ru g o j klasi, n ap rav ite gen erik u m e to d u iji je a rg u m e n t tip a o g ran ie n interfejso m i pok aite da se m eto d e interfejsa m o g u pozivati iz te generike m eto d e. U m eto di m a i n ( ), g enerikoj m eto d i p rosledite p rim e ra k klase koja je obavila realizaciju.

516

Misliti na Javi

Migracijska kompatibilnost
D a b ism o spreili m og u e z a b u n e u vezi s b risa n je m , m o ra te n ed vo sm isleno shvatiti da b risan je n/j'eobeleje jezika. R adi se o k o m p ro m isu u realizaciji Javinih g enerikih tipova, p o tre b n o m zato to je jezik n a p o e tk u b io nap rav ljen bez n jih . Taj k o m p ro m is e vam sm etati, p a se m o ra te navii n a njega i sh vatiti zato je u in jen. D a su generiki tip o v i b ili d eo Jave o d verzije 1.0, n e b i bili realizovani p o m o u b risanja - koristila b i se konkretizacija za p a m e n je p a ra m e ta ra tip a k ao p rv o ra z re d n ih en titeta, p a biste s n jim a m o g li d a o b avljate jezike i refleksivne o p eracije zasno van e na tip o v im a. V ideete u n astav k u poglavlja d a b risan je sm a n ju je ,,op tost generik ih tipova. O n i u Javi jesu korisni, ali n e toliko koliko bi m o gli b iti, a razlog je u p rav o brisanje. U realizaciji zasnovanoj n a b risan ju , g en eriki tip o v i se tre tira ju kao d ru g o ra z re d n i tip o v i koji se n e m o g u u p o tre b lja v ati u n ek im v a n im k o n tek stim a . G en eriki tip ov i p o stoje sam o to k o m statike pro v ere tip o v a. N ak o n toga, svi generiki tip ov i u p ro g ra m u bivaju o b risa n i i zam enjen i n e k o m n eg en e rik o m g o rn jo m g ra n ico m . N a p rim er, a n o ta cije tip a k ao to je L ist< T > b iv aju b risa n je m sved en e n a L ist, a p ro m en ljiv e o b in o g tipa svedene na O b je c t, ukoliko neka g ran ica n ije zadata. O sn o v n a m o tivacija za b risa n je jeste to to o m o g u u je ko rienje g enerik ih klijenata s n eg en erik im b ib lio tek am a i o b rn u to . To se esto naziva migracijska kom patibilnost. U id e a ln o m svetu, sav kod b i u isto m tre n u tk u p o stao generiki. U stv arn o sti, ak i da p ro g ra m e ri p iu sam o generiki k o d , m o rali bi da vode ra u n a o n eg en erik im b ib lio te k a m a n a p isa n im pre Jave SE5. A u to ri tih b ib lio tek a m o d a n ik ad a nee p rep rav iti svoj k o d tako da p o sta n e generiki, ili e to m o d a u ra d iti polako. Z ato Javina realizacija g en erik ih tip o v a m o ra da p o d rav a n e sam o vertikalnu kom patibilnost - postojei ko d i d ato tek e klasa m o ra ju o stati legalne i znaiti isto to su znaile pre - nego i m igracijsku k o m p a tib iln o st, d a bi b ib lio tek e m ogle p o stati generike kada to n jim a b u d e odgovaralo, i d a ta d a n e bi sru ile k o d i aplikacije koje o d njih zavise. Kada su to sebi zadali k ao cilj, p ro je k ta n ti Jave i raz n e g ru p e koje su rad ile na to m p ro b le m u o d luili su d a je b risan je je d in o ostvarivo reenje. B risanje o m o g u u je m igraciju ka generikim tip o v im a tako to dozvoljava p o sto ja n je n eg en eriko g k o da zaje dn o s generikim . Na p rim e r, recim o da o d re e n a aplikacija koristi dve biblioteke, X i Y, i da Y koristi bib lio te k u Z. Poto je u m e u v re m e n u o bjavljena Java SE5, tvorci te aplikacije i tih biblio teka vero vatn o e, je d n o g d an a , o d lu iti da p re u n a generiki kod. M e u tim , svaki od njih e im ati d ru g aiju m o tiv aciju i o g ra n ien ja u p o g led u v re m e n a tog prelaska. D a bi se postigla m igracijska k o m p a tib iln o st, svaka b iblio teka i ap likacija m o ra biti nezavisna od svih o stalih u o d n o su na to d a li k o risti g en erik i kod. Stoga n e sm eju im ati m o g u n o st da o tk riju da li d ru g e biblioteke koriste ili n e k o riste generiki kod. Z ato do k az da o d re en a bib lio tek a k o risti generiki k o d m o ra biti obrisan". Bez neke vrste p u tanje za m igraciju, sve biblioteke n apisan e tok om vrem ena bile su u o p asn o sti da b u d u odseene o d p ro g ram era koji su odluili da p red u na Javin generiki kod. T vrdi se d a su biblioteke deo jezika koji najvie u tie na p ro d u k tiv n o st, pa toliki rizik nije bio prihvatljiv. Da li je brisan je bilo jed in i ili n ajbolji p u t za m igraciju, pokazae vrem e.

Poglavlje 15: Generiki tipovi

517

Problem s brisanjem
D akle, o sn o v n i razlog za b risa n je jeste prelazak s n eg en eriko g n a generiki k o d i n a m e ra da se gen eriki k o d u kljui u jezik b ez ru e n ja p o sto jeih b ib liotek a. B risanje o m o g u u je p o sto jeem n eg en erik o m k lijen tsk o m k o d u d a n astav i d a rad i bez ikakve izm en e, sve d o k klijen ti ne b u d u sp re m n i d a p re ra d e k o d n a generiki. Ta m otiv acija je p lem en ita, zato to ne rui o d je d n o m sav p o sto jei kod. C ena b risan ja je zn a tn a . G en e rik i tip o v i se n e m o g u u p o treb ljav ati u o p eracijam a koje u v rem e izvravanja izriito u p u u ju n a tipove, k ao to su eksplicitne konverzije tipova, o p eracije instanceof i izrazi new. P oto se g u b e sve inform acije o tip u p a ra m e ta ra , kad a piete generiki k o d m o ra te se staln o p o d seati d a sam o izgleda kao da im ate in fo rm acije o tip u p a ra m e ta ra . D akle, k ad a piete ovakvo p are koda:
class Nesto<T> { T promenljiva;

i uz to n ap rav ite p rim e ra k klase Nesto:


Nesto<Macka> f = new N e st o< Ma ck a> ();

ini se d a k od u klasi Nesto treb a da zn a kako sada radi sa o b jek to m tip a Macka. Sintaksa vas jak o navodi na p o m isa o d a tip T biva zam e n jen u celoj klasi. Ali to nije istina, i kad god piete ko d za tu klasu, m o rate sebi rei: ,,Ne, to je sam o objekat. Pored toga, b risa n je i m ig racijsk a k o m p a tib iln o st znae d a se generiki kod ne koristi ni ta m o gde biste to m o d a eleli:
/ / : ge nericki/BrisanjelNasledjivanje.java class GenerickaOsnovna<T> { private T element; public void set(T arg) public T get() { arg = element; } } { return element;

}
class Izvedenal<T> extends Ge ne ri ck aO sn ov na <T > {} class Izvedena2 extends GenerickaOsnovna {} // Nema upozorenja // class Izvedena3 extends Generick aO sn ov na <? > {} // udna greka: // // unexpected type found : ? (pronaen neoekivan tip) required: class or interface without bounds

// (zahteva se: klasa ili interfejs bez granica) public class Br isanjelNasledjivanje { @SuppressWarni n g s ("unchecked") public static void main(String[] Izvedena2 d2 = new Izvedena2(); args) {

518

Misliti na Javi

Object obj = d 2 . g e t ( ) ; d2.set(obj); // Ovde biste dobili upozorenje!

} } ///= -

Izvedena2 nasleuje k lasu GenerickaOsnovna b ez gen erik ih p a ra m e ta ra i prev o d ilac ne daje u p o zo renje. U p o zo ren ja n e m a d o poziva m eto d e s e t( ). Za iskljuivanje u p o zo re n ja Java im a an o tac iju , o n u ko ju v id ite u listin g u (ta an otac ija nije bila p o d r a n a u ran ijim v erzijam a Jave SE5):
@SuppressWarnings("unchecked")

V odite ra u n a o to m e d a sm o o v u n a re d b u stavili n e p o sre d n o p re m e to d e koja generie u p o zo re n je , a ne p re cele klase. K ada iskljuujete u p o z o re n je , b o lje je d a se u sred sred ite na to ,,ue p o d ru je, d a ne biste p reiro k im iskljuivanjem u p o z o re n ja sluajno sakrili neki p rav i p ro blem . G reka k oju proizvodi Izvedena3 valjda znai d a je p rev o d ilac o ekivao sirovu o snovn u klasu. D o d ajte o vo m e tr u d zbog ra d a s g ran ica m a u slu aju k ad a p a ra m e ta r tip a ho ete da tre tira te kao neto vie o d o b in o g objekta, i dob ili ste m n o g o vie p o sla za m n o g o m anje rezu lta ta nego to d o b ijate o d p a ram etriz o v an ih tip o v a u jezicim a kao to su C + + , Ada ili Eiffel. To ne zn ai da su ti jezici bolji o d Jave za v ein u p ro g ra m e rsk ih zad atak a, nego da su n jih o v i m e h a n iz m i p a ra m e triz o v a n ih tip o v a fleksibilniji i m o n iji o d Javinog.

ta se zbiva na granicama
Z bog b risanja, sm a tra m d a asp ek t generikog k o d a koji najvie z b u n ju je jeste injenica da m oete nap isati o n o to n em a sm isla. N a p rim er:
/ / : genericki/TvoracNizova.java import j a v a .1a n g .r e f l e c t .*; import j a v a . u t i l .*; public class TvoracNizova<T> { private Class<T> vrsta; public Tv oracNizova(Class<T> vrsta) @SuppressWarni ngs("unchecked") T[] napravi(int velicina) { return (T[])Array.newInstance(vrsta, velicina); { this.vrsta = vrsta; }

}
public static void main(String[] args) { TvoracNizova<String> tvoracStringova = new TvoracNizova<S tr in g>( St ri ng .c la ss ); StringO nizStringova = tvoracStringova.napravi (9); System.out.println(Arrays.toString(nizStri n g ov a));

}
} /* Ispis: [null, null, n u l l , null, null, null, n u l l , null, null]

* ///:-

Poglavlje 15: Generiki tipovi

519

Iako je klasa vrsta n a p isa n a kao Class<T>, b risan je znai d a e o n a b iti usklad iten a sam o kao Class, b ez p a ra m e tra . Z ato, k ad a s n jo m n eto n ap rav ite, recim o n ek i niz, m eto d a A rray.new lnstance( ) ne d o b ija in fo rm ac ije o tip u koje vrsta im p licira; zbog to g a ta m e to d a n e m oe d ati rezu ltat specifinog p o d tip a , te ga m o ra te e k sp licitn o k o nvertovati, to proizvo di u p o zo ren je koje n e m o ete d a o tk lo n ite. Im ajte u v idu d a je m e to d a A rray.new lnstance( ) p re p o ru e n a za p ravljenje nizova u g en erik o m kodu. U koliko u m esto n iza n a p ra v im o k o n tejn er, stv ari izgledaju d rugaije:
//: genericki/TvoracListi.java import j a v a . u t i l .*; public class TvoracListi<T> { List<T> napravi() { return new A r r a y L i s t < T > ( ) ; } { public static void main(String[] args)

TvoracListi<String> tvoracStringova= new Tv or ac Li s t i < S t r i n g > ( ) ; List<String> listaStringova = t v or ac St ri ng ov a. na pra vi();

} } ///:Prevodilac ne daje nikak v o u p o zo ren je, iako (o d b risa n ja ) z n a m o d a <T> u new A rrayList<T>( ) u n u ta r m e to d e n a p ra v i( ) biva u k lo n je n - u v rem e izvravanja u n u ta r klase n em a nikakvog <T>, p a o n kao d a ne zn ai nita. U k oliko d o sled n o tak v o m shvatan ju , p ro m e n ite izraz u new A rrayL ist( ), p rev o d ilac e d a ti u p o zo ren je. Da Ii u o vom sluaju p a ra m e ta r tip a zaista n e zn ai nita? ta bi se desilo d a n a sledei nain u listu stavite neke objekte p re nego to je v ratite:
//: genericki/TvoracPopunjeneListe.java import j a va .u ti1 .*; public class TvoracPopunjeneListe<T> { List<T> n a p r a v i (T t, int n) { List<T> rezultat = new Ar r a y L i s t < T > ( ) ; for(int i = 0; i < n; i++) rezult at .a dd (t ); return rezultat;

}
public static void main(String[] args) { TvoracPopunjeneListe<String> tvoracStringova = new TvoracPopu nj en eL is te< St rin g > ( ) ; List<String> lista = tvoracStringova.napravi("Zdravo", 4); System.out.println(l i s t a ) ;

}
} /* Ispis: [Zdravo, Zdravo, Zdravo, Zdravo]

* ///:lako prevodilac ne m o e nita da zna o tip u T u n u ta r m e to d e n a p ra v i( ), ip ak m oe da proveri - u vrem e p rev o en ja d a li je o n o to ste stavili u rezultat tip a T pa m oe biti

520

Misllti na Javi

u sk la e n o sa A rra y L is t< T > . D akle, iako b risan je u k lan ja in fo rm a c ije o stv a rn o m tip u u n u ta r m e to d e ili klase, p rev o d ilac ip a k m oe da o b ezb ed i u n u tra n ju d o sle d n o st u n a in u n a koji je taj tip u p o tre b lje n u n u ta r m eto de, o d n o s n o klase. P oto b risan je u k lan ja in fo rm ac ije o tip u iz tela m eto d e , u v re m e izvravanja vane su granice. take gd e o b jek ti ulaze u m e to d u i izlaze iz nje. U tim ta k a m a p rev o d ilac u v rem e p rev o e n ja p ro v erav a tip o v e i u m e e k o d za njihove konverzije. P ogledajm o sledei n eg eneriki p rim e r:
//: ge ne ri ck i/ Je dnostavnoSkladiste.java public class Je dn os tavnoSkladiste { private Object obj; public void set(Object obj) { this.obj = obj; } args) { public Obje ct g e t () { return obj; } public static void main(String[] S k la di st e. se t( "S ta vka "); String s = (S tr in g) Sk la di st e. get (); J e dn os ta vn oS kl ad is te Skladiste = new JednostavnoSk1adiste();

} } ///:A ko re z u lta t prev ed em o u n a z ad k o m a n d o m ja v a p -c Je d n o sta v n o S k la d iste , d o b iem o (n a k o n u re iv an ja):


public void s e t ( j a v a .l an g. Ob je ct); 0: 1: 2: 5: aload_0 aload_l putfield #2; //Field obj:0bject; return

public ja va .lang.Object get(); 0: 1: 4: public 0: 3: 4: 7: 8: 9: 11: 14: 15: 18: 21: 22: aload_0 getfield #2; //Field obj:0bject; areturn static void mai n(java.l an g. St ri ng []); new #3; //class Je dnostavnoSkladiste dup invokespecial astore_l aloadl ldc #5; //String Stavka invokevirtual aload_l invokevirtual astore_2 return #7; //Method get:()0bject; checkcast #8; //class java/lang/String #6; //Method set:(0bject;)V #4; //Hethod "< init> : ()V

Poglavlje 15: Generiki tipovi

521

M eto d e s e t( ) i g e t( ) sam o zadaju, o d n o sn o itaju v re d n o st, a konverzija tip a se p ro verava n a m estu poziva m eto d e g e t(). U baciem o sada generike tip o v e u g o rn ji kod:
//: genericki/GenerickoSkladiste.java public class GenerickoSkladiste<T> ( private T obj; public void set(T obj) public T get() { this.obj = obj; ) { { return obj; }

public static void main(String[] args) new Ge ne ri ck oS kl ad is te <St ri ng >( ); Skla di st e. se t( "S ta vka "); String s = S k l a d i st e. ge t( );

Ge ne rickoSkladiste<String> Skladiste =

} } ///= Vie n e m a p o tre b e d a rezu ltat m eto d e g e t( ) ek sp licitn o k o n v e rtu jem o . U z to tak o e z n a m o da se tip v re d n o sti p ro sle en e m eto d i s e t( ) p roverava u v rem e p rev o en ja. O vo je relev an tn i bajtkod:
public void set(java .l an g. Ob je ct); 0: 1: 2: 5: aload_0 aload_l putfield #2; //Field obj:0bject; return

public java.lang.Object get(); 0: 1: 4: aloadO getfield #2; //Field obj:0bject; areturn

public static void main(java.lang.String[]); 0: 3: 4: 7: 8: 9: 11: 14: 15: 18: 21: 22: new #3; //class Ge nerickoSkla d iste dup invokespecial astore_l aload_l ldc #5; //String Stavka invokevirtual aload_l inv ok ev irtual #7; //Method get:()0bject; checkcast #8; //class java/lang/String astore_2 return #6; //Method set:(0bject;)V #4; //Method "<init>":()V

522

Misliti na Javi

D o bija se id e n ti an k o d . D o d a tn a p ro v e ra u lazn o g tip a u m e to d i s e t ( ) ne k o ta nas n ita, p o to je obavlja p revodilac. T u je i k onverzija tip a izlazne v red n o sti m e to d e g e t(), ali toliko biste i sam i m o ra li d a u ra d ite - a o vu a u to m a tsk i u m ee prevodilac, p a je k o d koji m o ra te d a n ap iete (i p ro ita te) istiji. Poto m e to d e g e t( ) i s e t( ) p ro izv o d e isti b ajtk o d , u gen erik o m k o d u se sve to je vano odigrava n a g ran ic am a - d o d a tn a pro v era u lazn ih v red n o sti u v rem e p rev o en ja i u m etan je konverzije izlaznih v red n o sti. S m anjiete z b rk u o k o b risanja ako ne zaboravite d a se ,,na g ran icam a odigrava sve to je vano.

Kompenzacija za brisanje
Kao to sm o videli, b risa n jem se g u b i m o g u n o s t ob avljanja o d re e n ih operacija u generik o m k odu . Sve o n o za ta je p o tre b n o p o zn av an je stv a rn ih tip o v a u v rem e izvravanja, nee raditi:
//: genericki/Obrisana.java // {CompileTimeError} (Nee biti prevedena)

public class Obrisana<T> { private final int VELICINA = 100; public static void f(0bject arg) if(arg instanceof T) T var = new T(); T[] niz = new T [ V E L I C I N A ] ; {} { // Greka // Greka // Greka // Upozorenje koje nije zaustavljeno

T[] niz = (T)new O b j e c t [V E L I C I N A ] ;

} } ///:D eava se da p ro g ra m ira n je m zao b i e te ove p ro b lem e, ali p o n e k ad m o ra te d a k o m p en zu jete b risa n je u v o en jem oznake tipa (engl. type tag). To znai da eksp licitn o p ro sle ujete C lass o b jek at za svoj tip, d a biste m o g li da ga u p o treb ljav ate u izrazim a s tip o m . P rim e ra radi, u p re th o d n o m p ro g ra m u nije usp eo po k u aj da se u p o tre b i n ared b a insta n ceo f, zato to su in fo rm ac ije o tip u o b risan e . A ko uvedete o zn ak u tipa, u m e sto te naredb e m o i ete da u p o tre b ite d in a m i k u m e to d u is ln s t a n c e ( ):
//: genericki/HvatanjeTipaKlase.java class Zgrada {} class Kuca extends Zgrada {} public class HvatanjeTipaKlase<T> { Class<T> vrsta; public Hv at anjeTipaKlase(Class<T> vrsta) this.vrsta = vrsta; {

Poglavlje 15: Generiki tipovi

523

public boolean f(0bject arg)

return vrst a. is ln st an ce (a rg);

}
public static void main(String[] args) HvatanjeTipaKlase<Zgrada> cttl = new Hv at an je Ti pa Kl as e< Zgr ad a> (Z gr ad a. cl as s); S y stem.out.println(cttl.f (new Z g r a d a O ) ); Sy st em.out.println(cttl.f(new Ku c a ( ) ) ) ; Hv at an jeTipaKlase<Kuca> ctt2 = new Hv at an je Ti pa Kl as e< Kuc a> (K uc a. cl as s); System.out.println(ctt2.f(new Z g r a d a ( ) ) ) ; System.out.println(ctt2.f(new Ku c a ( ) ) ) ; {

}
} /* Ispis: true true fal se true

* ///:P revod ilac proverav a da li ozn ak a tip a o d g o v ara g en erik o m a rg u m e n tu .

Veba 21: (4) Izm en ite p ro g ra m HvatanjeTipaKlase.java tako to ete d o d a ti m a p u M ap< S trin g,C lass< ?, m e to d u dodajTip(String imetipa, Class<?> vrsta) i m e to d u napraviN ovu(String imetipa). M eto d a n apraviN ovu( ) p roizvodi n o v u in sta n c u klase
p rid ru e n e z n a k o v n o m n izu svog a rg u m e n ta ili p o ru k u o greci.

Pravljenje instanci tipova


P okuaj pravljenja nove generike m eto d e n a re d b o m n e w T ( ) u p ro g ra m u Obrisana.jav a ne u speva, d elo m zbog b risan ja, a delom zbog toga to p revodilac ne m o e d a p ro v eri da li T im a p o d ra z u m e v a n i k o n stru k to r (bez a rg u m en ata). U C ++-U je ova o p eracija p riro d n a, jed n o stav n a i bezb ed n a (proverava se u v rem e p revoenja):
/ / : genericki/PravljenjelnstanceGenerickogTipa.cpp // C++, ne Java! template<class T> class Nesto { T x; // Pravljenje polja tipa T T* y; // Pokaziva na T p u b l ic: // Inicijalizacija pokazivaa: Nesto() { y = new T ( ) ; }

class Bar {}; int main() {

Nesto<Bar> nb; Nesto<int> ni; // ... radi i s prostim tipovima

} III--

524

Misliti na Javi

U Javi je reenje proslediti p ro izv o d n i o b jek at i p o m o u njega n ap rav iti n o v u instancu. Podesan p roizvod n i objekat je u p ravo ob jekat tip a Class, p a ako k o ristite o z n ak u tip a, za pravljenje novog objekta tog tip a m o ete u p o tre b iti m e to d u new lnstance( ):
//: genericki/PravljenjelnstanceGenerickogTipa.java import static net.mind vi ew .u ti l.Print.*; class K1asaKaoProizvodjac<T> { T x; public KlasaKaoProizvodjac(Class<T> vrsta) try { x = vrsta.ne wl ns ta nc e( ); } catch(Exception e) { throw new RuntimeE xc ep ti on (e ); {

} } }
class Zaposleni {}

public class PravljenjelnstanceGenerickogTipa { public static void main(String[] args) { KlasaKaoProizvodjac<Zaposleni> fz = new Kl as aK ao Pr oi zv od ja c<Z ap os le ni >( Za po sl en i.c la ss ); print("KlasaKaoProizvodjac<Zaposleni> u s p e l a " ) ; try { KlasaKaoProizvodjac<Integer> fi = new K1asaKaoProizv od ja c<I nt eg er >( In te ge r. cl ass ); } catch(Exception e) { print("KlasaKaoProizvodjac<Integer> z a k a z a l a " ) ;

} }
} /* Ispis: K1 asaKaoProizvodjac<Zaposleni> uspela K1asaKaoProizvodjac<Integer> zakazala

* // /= O vo e se prevesti, ali e KIasaKaoProizvodjac<Integer> zakazati zato to Integer n e m a p o d ra zu m e v an i k o n stru k to r. Poto se greka ne hvata u v rem e p rev o en ja, ovaj p ristu p se ne svia ekipi u k o m p a n iji Sun. O n i p red la u da u p o tre b ite ek sp licitn o g p ro izvodaa i o g ran iite tip, tako da p rim a sam o klasu koja realizuje to g proizvodaa:
//: genericki/OgranicenjeProizvodjaca.java interface ProizvodjacI<T> { T n a p r a v i ();

}
class Nesto2<T> { private T x;

Poglavlje 15: Generiki tipovi

525

public <F extends P r o i z v o d j a c I < T Nesto2(F proizvodjac) x = proi zv od ja c. na pr av i();

} / / }
class IntegerProizvodjac implements Pr oi zvodjacI<Integer> { public Integer napravi() return new Integer(O); {

} }
class Spravica { public static class Proizvodjac implements ProizvodjacI<Spravica> { public Spravica napravi() return new Spravica(); {

} } }
public class OgranicenjeProizvodjaca { public static void main(String[] args) { new Nesto2<Integer>(new I n t e g e r P r o i z v o d j a c O ) ; new Nesto2<Spravica>(new S p r a v i c a . P r o i z v o d j a c O ) ;

} } ///= Im ajte u v id u da je ovo sam o v arijan ta p ro sle iv an ja C la ss< T > . N a o b a nain a p ro sle u ju se p ro izv o d n i objekti; C la ss< T > je ig ro m sluaja u g ra e n p ro izv o d n i objekat, d o k se u g o rn jem p ristu p u pravi eksplicitan p ro izv o d n i objekat. Uz to se obavlja i prov era u v rem e prev oenja. D rug i p ristu p je p ro je k tn i o b razac Tem plate M eth o d ( ablon ska m e to d a ). U n ared n o m p rim e ru ablonska m eto d a je g e t ( ), a m eto d a n a p r a v i ( ) definie se u potk lasi tako d a pro izvodi ob jek at to g tipa:
/ / : g e ne ricki/T vo ra cG en er ic kih .java abstract class GenerickiUzNapravi<T> { final T element; GenerickiLlzNapravi () { element = napravi(); abstract T napravi(); }

class X {} class Tvorac extends GenerickiUzNapravi<X> { X napraviO void f() { { return new X(); }

System.out.println(elem en t. ge tC la ss () .g etS im pl eN am e( )) ;

526

Misliti na Javi

public class TvoracGenerickih { public static void main(String[] args) Tvorac c = new Tvorac(); c.f 0 ; {

}
} /* Ispis:

X * ///:-

Veba 22: (6) U p o treb ite o zn ak u tip a i refleksiju za pravljen je m e to d e koja k o risti verziju sa a rg u m e n to m m e to d e new lnstance( ) koja tre b a da p rav i o b jek te klase iji k o n stru k to r
p rim a arg u m en te.

Veba 23: (1) P rep rav ite p ro g ram OgranicenjeProizvodjaca.java tako d a m e to d a n a p ra v i( ) p rim a jed an arg u m en t. Veba 24: (3) Izm en ite vebu 21 tako da se p ro iz v o d n i ob jek ti d re u Mapi u m esto u Class<?>.

Nizovi generikih tipova


Kao to ste videli u p ro g ra m u Obrisana.java, ne m o ete p rav iti nizove g en erik ih tipova. O p te reenje je u p o tre b iti ArrayList gde g o d ste u isk u en ju da n a p ra v ite niz g enerikih tipova:
//: genericki/ListaGenerickih.java import j a v a . u t i l .*; public class ListaGenerickih<T> { private List<T> niz = new A r ra yL is t< T> (); public void add(T stavka) public T get(int indeks) { n i z. ad d( st av ka ); } { return n i z. ge t( in de ks ); }

} ///= O vako ste dob ili p o n aan je niza, ali i p ro v eru tipova u v rem e p re v o en ja koju do n o si generiki kod. K atkada e v am ipak zatreb ati niz g enerikih tipova (na p rim e r, A rra y L ist u p o treb ljava nizove in te rn o ). Da stv ar b u d e zanim ljivija, m oete definisati referencu tak o da prevodilac b u d e zadovoljan. Na p rim er:
//: genericki/NizGenerickihReferenci.java class Generic<T> {} public class NizGenerickihReferenci static Generic<Integer>[] gia; {

} /// = -

Poglavlje 15: Generiki tipovi

527

Prevodilac e to p rih v a titi bez ikakvog u p o zo ren ja . Ali p o to neete m o i da n ap ra v ite niz ta n o tog tip a (u k lju u ju i tu i p a ra m e tre tip a ), stv ari su p o m a lo zb u n ju ju e. P oto svi n izovi im a ju istu stru k tu r u (veliinu svakog e le m e n ta i ra sp o red elem en ata) bez o b zira na tip koji sadre, izgleda kao da bi se m o g ao n a p ra v iti niz tip a Object i ko n v erto v ati u niz eljenog tipa. To e se d o d u e prevesti, ali nee m o i da se izvrava, je r pro izv o d i Class-

CastException:
//: generi cki/NizGeneri ckogTi pa.java public class NizGenerickogTipa { static final int VELICINA = 100; static Generic<Integer>[] gia; @SuppressWarnings("unchecked") public static void main(String[] args) { // Prevee se, ali pravi ClassCastException: //! gia = (Generic<Integer>[])new O b j e c t [ V E L I C I N A ] ; // U vreme izvravanja, tip je sirov (obrisan): gia = (Generic<Integer>[])new G e ne ri c[ VE LI CI NA ]; System.out.println(gia.getClass() . g e t S i m p l e N a m e O ) ; g i a [0] = new G e ne ri c< In te ge r> (); //! g i a [1] = new 0bject(); // Greka u vreme prevoenja // Otkriva neslaganje tipova u vreme prevoenja: /'/! gia[2] = new Generi c< 0o ub le >( );

}
} /* Ispis: Generic[]

* ///:P rob lem je u to m e to nizovi p am te svoj stv arn i tip, a o n biva u tv r e n u tre n u tk u p ra vljenja niza. D akle, iako je gia k onvertovana u Generic<Integer>[], ta in fo rm ac ija p o sto ji sam o u vrem e p rev o en ja (a da n em a an o tac ije @ SuppressV V arnings, do b ili biste u p o zo ren je za tu konverziju). U v rem e izvravanja, to je sam o n iz tip a Object, i to stvara p ro b le m e. Jedini n ain da u sp en o n ap rav ite niz g en erik o g tip a jeste d a n a p rav ite nov niz o b risan o g tip a i da njega k onvertujete. P ogledajm o neto tei p rim er. R azm o triem o je d n o sta v a n g en erik i o m o ta oko niza:
//: genericki/GenerickiNiz.java public class GenerickiNiz<T> { private T[] niz; @SuppressWarnings("unchecked") public GenerickiNiz(int vel) niz = (T[])new Object[vel]; {

}
public void put(int indeks, T stavka) ni z[i ndeks] = stavka; {

528

Misliti na Javi

public T get(int indeks) public T[] rep()

{ return ni z[ i n d e k s ] ; )

// Metoda koja eksponira kako je niz predstavljen: { return niz; } args) { public static void main(String[] GenerickiNiz<Integer> gai = new G e n e r i ck iN iz <I nt eg er> (1 0); // Ovo prouzrokuje ClassCastException: //! Integer[] ia = g a i . r e p O ; // Ovo je u redu: Object[] oa = g a i . r e p O ;

} } ///:Kao p re , ne m o em o rei T[] niz = new T[vel], p a p ra v im o n iz objek ata i njega k o n v ertujem o . M eto d a r e p ( ) vraa T[], koji b i u m e to d i m a in ( ) treb alo da b u d e Integer[] za gai, ali ako ga p o zo vete i rezu ltat p o k u a te d a u h v atite kao referen cu n a Integer[], dob iete ClassCastException, i o p e t zato to je stv arn i tip u v rem e izvravanja Objectf], A ko p ro g ra m GenerickiNiz.java prev ed ete n ak o n to ste k o m en ta risali an o tac iju @SuppressWarnings, p rev o ilac e d a ti upozo ren je:
Note: GenerickiNiz.java uses unchecked or unsafe operations. Note: Recompile with -XIint:unchecked for details.

U ovo m sluaju, d o b ili sm o sa m o je d n o u p o zo ren je i m islim o da se o n o o d n o si na konverziju tip a. Ali ako zaista h o ete da b u d e te sig u rn i, prev ed ite sa

-Xlint:unchecked:
GenerickiNiz.java:7: warning: found : java.lang.Object[] required: T[] niz = (T[])new Object[vel]; 1 warning [unchecked] unchecked cast

U p o zo ren je se stv arn o o d n o si n a konverziju. Poto u p o zo ren ja sm etaju , najbolje je da u tv rd im o kako sm o o d re e n o u p o zo re n je oekivali pa d a ga isk lju im o p o m o u @SuppressW arnings. Tako em o zaista isp itati u p o zo ren je kada se pojavi. Z bog b risan ja , tip niza u v rem e izvravanja m o e b iti sam o Object[]. U koliko ga o dm ah k o n v e rtu jem o u T[], o n d a se stv arn i tip niza gubi u v rem e p rev o en ja, pa ga ni p revoilac nee prov erav ati i tim e spreiti p o ten cijaln e greke. Z ato je bolje u p o tre b iti Object[] u n u ta r k o n te jn era i d o d a ti konverziju u tip T na m e stu gde u potrebljavate elem e n t niza. P o g ledajm o kako bi to izgledalo u p rim e ru GenerickiNiz.java:
//: genericki/GenerickiNiz2.java public class GenerickiNiz2<T> { private O b j e c t [] niz;

Poglavlje 15: Generiki tipovi

529

public GenerickiNiz2(int vel) niz = new O b j e c t [ v e l ] ;

}
public void put(int indeks, T stavka) niz[indeks] = stavka; {

}
@S uppressWarnings("unchecked") public T g e t (i nt indeks) public T[] rep() { { return (T)ni z[i n d e k s ] ; } @S uppressWarnings("unchecked") return (T[])niz; // Upozorenje: neproverena konverzija

}
public static void main(String[] args) Ge ne ri ck iN iz 2< In te ger > gai = new Ge ne ri ck iN iz 2< In te ger >( 10 ); for(int i = 0; i < 10; i ++) gai.put(i, i); for(int i = 0; i < 10; i ++) System.out.print(gai.get(i) + " "); Sy st em .o ut .p ri nt ln (); try { Integer[] ia = g a i . r e p O ; } } catch(Exception e) { System.out.println(e); {

}
} /* Ispis: (primer) [Ljava.lang.Object; cannot be cast to 0 1 2 3 4 5 6 7 8 9 java.lang.ClassCastException: [Ljava.lang.Integer;

* ///:Isprv a ovo ne izgleda b itn o drugaije, sa m o to je konverzija p rem ete n a. Bez an o tacija

@SuppressWarnings, d o b ijaete u p o z o ren ja u n ch eck ed " (n ep ro v eren o ). M e u tim , in te rn o je niz sad a pred stav ljen kao Object[], a ne kao T [|. Kada se pozove m eto d a g e t( ),
o n a k o n v e rtu je o b jek at u tip T, to u stv ari i jeste taan tip, pa je to b ezb ed n o . M e u tim , ako pozovete re p ( ), o n a e p o n o v o p o k u ati da Object[] k o n v ertu je u T [], to je i dalje n eta n o , i p ro izv o di u p o zo ren je u v rem e p rev o en ja i izuzetak u v rem e izvravanja. D akle, n e m a n ain a da se o b o ri tip p rip a d n o g in te rn o g niza koji m oe biti sa m o Object[]. P re d n o st in te rn o g tre tira n ja niza kao tip a Object[] u m esto T [] jeste u to m e to je m an je v ero v a tn o da ete u v rem e izvravanja zab o rav iti koji je tip niza i zato slu ajn o n a p rav iti g reku (iako se veina tak v ih greaka, a m o d a i sve, b rzo o tk rije u v rem e izvravanja). U n o v o m k o d u , tre b a lo bi d a p ro sled ite leksem u tipa. U to m sluaju, GenerickiNiz bi izgledao ovako:
//: ge ne ri ck i/ Ge nerickiNizSLeksemomTipa.java import java.lang.reflect.*; public class Ge ne rickiNizSLeksemomTipa<T> { private T[] niz; @SuppressWarni ngs("unchecked")

530

Misliti na Javi

public Ge nerickiNizSLeksemomTipa(Class<T> tip, int v e l) { niz = (T[])Array.newInstance(tip, vel);

}
public void put(int indeks, T stavka) niz[indeks] = stavka; {

}
public T get(int indeks) public T[] rep() { return nizfindeks]; } // Eksponiraj kako je niz interno predstavljen: { return niz; } args) { public static void main(String[]

GenerickiNizSLeksemomTipa<Integer> gai = new GenerickiNizSLeksemomTipa<Integer>( Integer.class, 10); // Ovo sada radi: Integer[] ia = gai.rep();

} } L eksem u n iza C la ss< T > p ro sle u je m o k o n s tru k to ru k ako b ism o se opo rav ili o d b risanja i m og li d a n a p ra v im o n iz p rav o g tip a koji n a m je p o tre b a n , iako u p o z o re n je zbog konverzije m o ra m o da p o tisn e m o sa < SuppressW arnings. Kada d o b ije m o prav i tip, m o em o ga v ratiti i d o b iti eljene rezu ltate, kao to vidite u m eto d i m a i n ( ). U vrem e izvravanja, tip niza je taa n tip T [ ] . N aalost, u izv o rn o m k o d u sta n d a rd n ih b ib lio tek a Jave SE5, svuda ete nai konverzije O b je c t nizova u p a ra m e triz o v a n e tipove. P rim e ra radi, ovako izgleda (n ak o n ienja i po jedn o stavljen ja) k o n stru k to r za k o p iraj-A rray L ist-iz-k o lek cije:
public ArrayList(Collection c) { velicina = c . si ze (); elementPodataka = (E[])new O b j e c t [ v e l i c i n a ] ; c . to Ar ra y(elementPodataka);

} A ko pregledate klasu A rra y L ist.ja v a , n ai ete m n o tv o takv ih eksp licitnih konverzija. I ta se deava kada klasu prevedem o?
Note: ArrayList.java uses unchecked or unsafe operations. Note: Recompile with -XIint:unchecked for details.

Kao to sm o i m islili, s ta n d a rd n e b ib lio tek e p ro u z ro k u ju m n o tv o u p o zo ren ja. Ako ste pisali n a C -u , po g otovo o n o m e p re izdavan ja sta n d a rd a ANSI C, seate se je d n e posledice upo zo ren ja: kada shvatite da m oete d a ih zan e m a rite, tako i radite. Z ato je najbolje da p revodilac ne daje nikakve p o ru k e uk o lik o p ro g ra m e r na njih ne m o ra da reaguje. U svom vveblogu,3 Neal G after (jed an o d v o d eih p ro g ra m e ra Jave SE5) istie da je bio lenj to k o m p rerad e Javinih b ib lio tek a i d a m i ostali ne bi treb alo da sledim o njegov prim er. N eal kae i da nije m o g ao da p o p ra v i d eo k oda Javine b iblioteke, a da p ri to m ne
'

http://gafter.blogspot.com/2004/U9/puzzling-through-erasurc-cinswcr.html

Poglavlje 15: Generiki tipovi

531

sru i postojei interfejs. D akle, ak i ako se u izv o rn o m k o d u Javinih b ib lio tek a pojav lju ju n ek i id io m i p ro jek to v an ja, n e znai d a je to p rav i n ain rad a. D o k itate k o d b ib lio tek a, ne sm ete u zim ati zdravo za g otovo d a je to u z o r koji treb a d a sledite u svom k o d u .

Granice
Granice su u k ra tk o p red stav ljen e u p re th o d n o m elu poglavlja (v id eti stran ice 514 i 515). G ran ice o m o g u u ju d a n am e ete o g ran ien ja p a ra m e ta rsk im tip o v im a koje m o ete u p o treb ljav ati u g en erik o m k o d u . Iako tim e d o b ijate m o g u n o st da n am eete p ravila o tip o v im a n a koje m oete p rim e n iti svoj gen eriki k od , p o ten cijaln o vaniji efekat je d a m o ete p ozivati m e to d e d efin isan e u vaim o g ra n i e n im tip o v im a. P oto b risan je u k lan ja in fo rm a c ije o tip o v im a, jed in e m e to d e koje m o ete poziv ati za n eo g ra n i e n generiki p a ra m e ta r jesu o n e o stu p n e za O b je c t. M e u tim , ukoliko taj p ara m e ta r m o ete o g ran i iti tak o da b u d e p o d sk u p tipova, o n d a m o ete poziv ati m e to d e to g p o d sk u p a . D a b i sproveo to o g ran ien je, Javin gen erik i k o d p o n o v o u p o treb ljav a rezerv isan u re e x te n d s . N e sm ete s m e tn u ti s u m a d a u k o n te k stu generik ih g ran ica, exte n d s im a sasvim d ru g aije zn aen je n ego o b in o . U n a re d n o m p rim e ru p rik azaem o o sn o v n o o g ran icam a:
//: ge ne ri ck i/O snovnoOGranicama.java interface ImaBoju { ja va .awt.Color getColor(); class Obojen<T extends ImaBoju> { T stavka; Obojen(T stavka) T getltem() { this.stavka = stavka; } } { return stavka; }

// Granica omoguuje da pozovete metodu: ja va .a wt .C ol or boja() { return s t a v ka .g et Co lo r( ); }

}
class Dimenzija { public int x, y, z; } // Ovo nee raditi -- prve moraju biti klase, a tek onda interfejsi: // class Ob oj enaDimenzija<T extends ImaBoju & imenzija> { // Vie granica: class ObojenaDimenzija<T extends Dimenzija & ImaBoju> { T stavka; Ob oj enaDimenzija(T stavka) T getltemO int getX() int getZ() ja va.awt.Color boja() { this.stavka = stavka; } } } } } { return stavka; { return stavka.x; { return stavka.z;

{ return s t av ka .g et Co lo r( ); }

int g e t Y () { return stavka.y;

532

Misliti na Javi

interface Tezinu { int tezina{); } // Kao pri nasleivanju, moete imati samo jednu // konkretnu klasu, ali vie interfejsa: class Predmet<T extends Dimenzija & ImaBoju & Tezinu> { T stavka; Predmet(T stavka) T getltem() int getX() int getY() int getZ() { this.stavka = stavka; } { return stavka; } { return s t a v ka .g et Co lo r( ); } } } } { return stavka.x; { return stavka.y; { return stavka.z;

java.awt.Color boja()

int tezina()

{ return st av ka .t ez in a( ); }

}
class Ogranicen extends Dimenzija implements ImaBoju, Tezinu { public java.awt.Color getColor() public int tezina() { return 0; } { return null; }

}
public class OsnovnoOGranicama { public static void main(String[] Predmet<Ogranicen> predmet = new Predmet<Ogranicen>(new O g r a n i c e n O ) ; predmet.bojaO; p r e d me t. ge tY (); predmet.tezinaO ; args) {

} } ///:M oda ste p rim e tili da O sn o v n o O G ra n ic a m a .ja v a sadri p o n avljan ja koja se m og u izbei nasledivanjem . Sada ete v ideti kako svaki nivo nasleivanja d o d a je svoja og ranienja:
//: ge nericki/NasledjivanjeGranica.java class DrziStavku<T> { T stavka; DrziStavku(T stavka) T getltemO { this.stavka = stavka; } } { return stavka;

}
class 0bojena2<T extends ImaBoju> extends DrziStavku<T> { 0bojena2(T stavka) { su pe r( st av ka ); } { return st av ka .g et Co lo r( ); } ja va .a wt .C ol or boja()

}
class ObojenaDimenzija2<T extends Dimenzija & ImaBoju> extends 0bojena2<T> { 0b ojenaDimenzija2(T stavka) int getX() { } s u pe r( st av ka ); } { return stavka.x;

Poglavlje 15: Generiki tipovi

533

int g e t Y () { return stavka.y; int getZ() { return stavka.z;

} }

}
class Predmet2<T extends Dimenzija & ImaBoju & Tezinu> extends ObojenaDimenzija2<T> { Predmet2(T stavka) { int tezina() su pe r( st av ka ); } { return s t a v k a .t ez in a( ); }

}
public class NasledjivanjeGranica { public static void main(String[] args) Predmet2<0granicen> predmet2 = new Predmet2<0granicen>(new Ogranicen()); p r ed me t2 .b oj a( ); p r ed me t2 .g et Y( ); predme t2 .t ez in a( ); {

} III-Klasa DrziStavku sadri neki objekat, pa se to p o n a an je nasle u je u klasi Obojena2 koja zahteva i da n jen p a ra m e ta r b u d e usaglaen sa in terfe jso m ImaBoju. ObojenaDimenzija2 i Predmet2 dalje p ro iru ju h ije ra rh iju i d o d a ju g ran ice n a svakom nivou. Sada se m eto d e nasleuju , pa se n e m o ra ju p o n av ljati u svakoj klasi. Evo p rim e ra s vie slojeva:
//: genericki/EpskaBitka.java // Primer granica u Javinom generikom kodu. import ja v a . u t i 1 .*; interface SuperSila {} interface RentgenskiVid extends SuperSila { void vidi Kr oz Zi do ve ();

}
interface SuperSluh extends SuperSila { void cuje Tih e Z v u k o v e O ;

}
interface SuperNjuh extends SuperSila { void pr at iPoMiris u ( ) ;

}
class SuperJunak<SNAGA extends SuperSila> { SNAGA snaga; SuperJunak(SNAGA snaga) SNAGA getPower() { this.snaga = snaga; } } { return snaga;

}
class SuperUhoda<SNAGA extends RentgenskiVid> extends SuperJunak<SNAGA> {

534

Misliti na Javi

SuperUhoda(SNAGA snaga) void vidi()

{ su pe r( sn ag a); }

{ snaga. vi di Kr oz Zi do ve( ); }

}
class PasJunak<SNAGA extends SuperSluh & SuperNjuh> extends SuperJunak<SNAGA> { PasJunak(SNAGA snaga) void cuje() void njusi() { su pe r( sn ag a); } { snaga.cu je Ti he Zv uk ove (); } { snag a. pr at iP oM ir is u(); }

}
class SuperCujeNjusi implements SuperSluh, SuperNjuh { public void cujeTiheZvukove() public void pratiPoMirisu() {} {}

}
class Ker extends PasJunak<SuperCujeNjusi> { Ker() { super(new S u pe rC uj eN Ju si()); }

}
public class EpskaBitka { // Granice u generikim metodama: static <SNAGA extends SuperSluh> void koristiSuperSluh(SuperJunak<SNAGA> junak) ju na k. ge tP ow er () ,c uje Ti he Zv uk ov e( ); {

}
static <SNAGA extends SuperSluh & SuperNjuh> void superTragac(SuperJunak<SNAGA> junak) juna k. ge tP ow er () ,c uje Ti he Zv uk ov e( ); ju na k . g e t P o w e r ( ) .pr at iP oM ir is u(); {

}
public static void m a i n ( S t r i n g [] args) Ker ker = new K e r ( ) ; kori sti S u p e rS lu h( ke r); supe rT ra ga c( ke r); // Ovo moete da uradite: List<? extends SuperSluh> audioFili; // Ali ovo ne moete: // List<? extends SuperSluh & SuperNjuh> k e r o v i ; {

} } ///:V odite ra u n a o to m e da se dokerski a rg u m e n ti (koje em o sledee p ro u iti) m ogu u p o treb ljav a ti sa m o za je d n u g ran icu . V eba 25: (2) N aprav ite dva interfejsa i klasu koja ih realizuje. N ap rav ite dve generike m e to d e , je d n u iji je a rg u m e n t p a ra m e ta r o g ran ien p rv im interfejsom i d ru g u iji je arg u m e n t p a ra m e ta r og ran ien d ru g im in terfejso m . N ap rav ite p rim e ra k klase koja realizuje oba interfejsa i pokaite da se o n a m oe u p o treb ljav ati sa oba interfejsa.

Poglavlje 15: Generiki tipovi

535

Dokerski argumenti
Ve ste videli neke jed n o sta v n e u p o treb e dokerskih argum enata -z n a k o v a p ita n ja u izrazim a generikih a rg u m en a ta - u poglavlju uvanje objekata, a jo vie u poglavlju Podaci o tipu. U ovom odeljk u ra z m o tri em o ih detaljnije. Poeem o od p rim e ra koji po k azuje o d re e n o p o n a a n je nizova: n iz izvedenog tip a m oete da d o d elite referenci niza o sn o v n o g tipa:
//: ge ne ricki/KovarijantniNizovi.java class Voce {} class Jabuka extends Voce {} class Jonathan extends Jabuka {} class Naranda extends Voce {} public class KovarijantniNizovi { {

public static void main(String[] args) Voce[] voce = new Jabuka[10]; voce[0] = new Jabuka(); // OK voce[l] = new Jonathan(); /,/ OK

// U vreme izvravanja tip je Jabuka[], ne Voce[] niti Naranda[]: try { // Prevodilac dozvoljava da dodate Voce: voce[0] = new Voce(); // ArrayStoreException } catch(Exception e) { Sy st e m . o u t .pr in t l n ( e ) ; } try { // Prevodilac dozvoljava da dodate Narande: voce[0] = new Naranda(); // ArrayStoreException } catch(Exception e) { System.o ut .p ri nt ln (e); }

}
} /* Ispis: j a va .lang.ArrayStoreException: Voce j a v a .1a n g .ArrayStoreExcepti on: Naranda

* ///:Prvi red u m eto d i m a in ( ) pravi niz Jabuka i d o elju je ga referenci na niz tip a Voce. To im a sm isla - Jabuka je v rsta voa, pa niz Jabuka treb a isto vrem eno da b u d e i niz tip a Voce. M e u tim ,d a je pravi tip niza Jabuka[], u njega b iste m ogli da stavite sam o o b jek at tip a Jabuka ili p o d tip a o d Jabuka, to bi zaista delovalo i u v rem e p rev o en ja i u v re m e izvra vanja. Ali o b ra tite p a n ju na to da p revodilac dozvoljava stavljanje ob jek ta tip a Voce u niz. P revo dio cu to im a sm isla, poto o n im a referencu n a Voce[] - zato da ne dozv oli d a se u niz stavi o bjek at tip a Voce ili bilo ta izvedeno iz tip a Voce, kao to je Naranda? Stoga je u v rem e prev o enja to dozvoljeno. M e u tim , u v rem e izvravanja, m e h an izam nizova zna da radi s nizom Jabuka[] i generie izuzetak kada se stra n i tip stavi u taj niz. Svoenje navie ovde nije p rav a re. Vi zaprav o je d an niz do d elju jete d ru g o m . Svaki niz se p o n aa kao d a sadri d ru g e objekte, ali p o to m o em o d a svedem o navie, jasn o je da objekti niza m o g u da p o tu ju p ravila o tip u o b jek ata koje niz sadri. To je kao d a nizovi zn aju ta sadre, pa ne m oete da ih p revarite zbog pro v era u v rem e p re v o en ja i u v rem e izvravanja.

536

Misliti na Javi

Takvo p o n aan je nizova nije to lik o stra n o , p o to u v rem e izvravanja vi ip ak saznajete d a ste u m e tn u li p o grean tip . Ali je d a n o d o sn o v n ih ciljeva gen erik o g k o d a jeste o tk riv anje tak v ih greaka ve u v rem e p rev o e n ja . ta e se d esiti ako u m esto nizova p o k u am o da u p o tre b im o kontejnere?
//: genericki/NeKovari j a n t m ' G e n e r i c k i T i p o v i .java // {CompileTimeError} import java.util public class NeKovarijantniGenerickiTipovi { (Ne mo e se prevesti)

// Greka u vreme prevoenja: neusklaeni tipovi: List<Voce> listav = new A r r a y L i s t < J a b u k a > ( ) ;

} III-Iako ete isprva ovo m o d a p ro tu m a iti k ao K o n te jn e r Jabuka se ne m o e d o d eliti k o n te jn e ru za Voce ne zab o rav ite d a g en erik i k o d n isu sam o k o n tejn eri. G reka n a m zap ravo kae: G eneriki tip za d a t tipotn Jabuka n e m o ete d o d e liti g en erik o m tip u koji je za d a t tipom Voce. Kada bi, k ao u slu aju nizova, p rev o d ilac o k o d u znao dov o ljn o da m o e u tv rd iti d a se rad i o k o n te jn e rim a , m o d a b i b io m alo p o p ustljiviji. Ali o n to ne zna, te o d b ija d a dozvoli svoenje navie. To zap rav o i n ije svoenje navie - L ista Jabuka nije L ista tip a Voce. Lista Jabuka m o e d a sad ri o b jek te tip a Jabuka i p o d tip o v e o d Jabuka, a L ista Voce m oe d a sadri sve p o d tip o v e tip a Voce. D a, u k lju u ju i i objekte tipa Jabuka, ali o n a tim e ne p o staje L ista Jabuka - i dalje je L ista tip a Voce. Lista Jabuka nije istog tip a kao L ista tipa Voce, iako je Jabuka p o d tip tip a Voce. Pravi p ro b le m je to to g o v o rim o o tip u k o n te jn era , a ne o tip u o n o g a to k o n tejn er sadri. Z a razliku od nizova, generiki k o d n e m a u g ra e n u kovarijansu. Razlog je to to su nizovi p o tp u n o definisani u jezik u i m o g u im se u g ra d iti pro v ere i u v rem e p rev o en ja i u v rem e izvravanja; s d ru g e stran e , u g en e ri k o m k o d u , p rev o d ilac i izvrno o k ru en je ne m o g u zn ati ta hoete da u ra d ite sa svojim tip o v im a i kakva bi tre b a lo da b u d u pravila. M e u tim , p o n ek a d b ism o hteli d a izm e u k o n te jn e ra i o n o g a to k o n tejn eri sadre usp o stav im o n ek u v rstu svodenja navie. T om e slue d okerski arg u m e n ti.
//: ge ne ri ck i/ Ge ne ri ck iTi po vi IK ov ar ij an sa .java import j a v a . u t i l .*; public class Ge ne ri ck iTipoviIKovarijansa { public static void main(String[] args) { // Dokerski argumenti omoguuju kovarijansu: List<? extends Voce> listav = new Ar r a y L i s t < J a b u k a > ( ) ; // Greka u vreme prevoenja: nije mogue // dodavanje objekta b i 1o kojeg tipa: // listav.add(new J a b u k a O ) ; // listav.add(new Voce()); // 1 istav.add(new O b j e c t O ) ; listav.add(null); // Dozvoljeno, ali nekorisno // Znamo da vraa barem Voce: Voce f = 1 i stav.get (0);

} ///:-

Poglavlje 15: Generiki tipovi

537

T ip o b jek ta listav sad a je List<? extends Voce>, to m o ete itati kao lista bilo kojeg tip a koji n asled u je Voce. M e u tim , to ne znai da ova Lista p rim a sve p o d tip o v e klase Voce. D ok ersk i a rg u m e n t o znaava o d re e n tip , p a g o rn ji izraz znai o d re en tip koji referenca listav ne specificira Stoga d o d eljen a Lista m o ra d a uva sp e c ifk ira n tip k ao to su Voce ili Jabuka. D a bi svoenje navie n a listavbilo m og u e, taj tip je n ije v an o koji. U koliko je je d in o o g ran ien je da ta L ista sadri o d re e n o Voce ili p o d tip klase Voce, ali zapravo v am nije v ano koji, ta m o ete d a u ra d ite s tak vo m L istom ? A ko ne zn ate koji tip ta L ista sadri, kako d a joj b ezb ed n o d o d ate neki objekat? N e m oete, kao to ni u p ro g ra m u KovarijantniNizovi.java niste m ogli da niz svedete navie, sem to u o v o m sluaju to spreava prevodilac, a ta m o sistem za izvravanje. Sada p ro b le m otk riv ate ranije. M od a ste po m islili d a je ovo o tilo p redaleko, je r sada Listi za koju ste u p rav o rekli da sad ri o b jek te tip a Jabuka, n e m o ete d a d o d a te o b jek at tip a Jabuka. U p ra v u ste, ali p revodilac to ne zna. O b je k a t tip a List<? extends Voce> m o e legalno da u k azuje n a o b jek at tip a List<Naranda>. N ak o n to u ra d ite ovu v rstu svoenja navie, g u b ite m o g u n o st d a bilo ta p ro sled ite u listu, ak i Object. S d ru g e stra n e, ako po zo v ete m e to d u koja v raa Voce, to je b ezb ed n o je r z n a te da sadraj L iste m o ra u n a jm a n ju ru k u b iti tip a Voce, p a e p rev o d ilac to dozvoliti.

Veba 26: (2) P okaite k o v arijan su nizova p o m o u klasa N um ber i Integer. Veba 27: (2) P okaite d a k o v arijan sa ne rad i s Listama i klasam a Number i Integer, a zatim uvedite d okerske arg u m e n te .

Koliko je prevodilac pametan?


A ko sada m islite da n e m o ete pozivati m eto d e koje p rim a ju a rg u m en te, pog led ajte ovo:
//: ge nericki/PametPrevodioca.java import j a v a . u t i 1 .*; public class PametPrevodioca { public static void main(String[] List<? extends Voce> listav = Arrays.a sL is t( ne w J a b u k a O ) ; Jabuka a = (Jabuka)listav.get(O); // Nema upozorenja 1 istav.contains(new Jabuka(j); // Argument je Object' 1 i s t a v .indexOf(new J a b u k a O ) ; // Argument je 'Object' args) f

} 1 ///:Pozvali sm o m e to d e c o n tain s( ) i ind ex O f( ) koje p rim a ju objekte tip a Jabuka kao arg u m e n te , i sve d o b ro radi. Z nai li to d a prevo dilac isp itu je k o d i proverava da li o d re e n a m e to d a m o d ifik u je svoj objekat? Iz d o k u m e n ta c ije za ArrayList viclim o da p rev o d ilac nije toliko p a m e ta n . D o k m e to d a a d d ( ) p rim a a rg u m e n t tip a g enerikog p a ra m e tra , m eto d e c o n tain s( ) i indexO f( ) p rim a ju a rg u m e n te tip a Object. Stoga k ad a zad ate ArrayList<? extends Voce>, a rg u m e n t za a d d ( ) p o staje ? extends Voce. Iz to g o pisa p rev od ilac n e m o e znati koji bi p o d tip klase Voce tre b a lo da d o e na to m esto, te stoga ne p rih v ata n ijed an od tih p o d tip o v a. Nije

538

Misliti na Javi

vano da li ste na jp re tip Jabuka sveli navie n a Voce - prev o d ilac p ro s to o d b ija da pozove m e to d u (kao to je a d d ( )) uk o liko je u listi a rg u m e n a ta do kersk i a rg u m en t. A rg u m en ti m eto d a co n tain s( ) i ind ex O f( ) tip a jesu Object, n e m a nikak vih d okerskih arg u m en a ta, i prev o d ilac dozvoljava poziv. To zn ai d a je na p ro je k ta n tu generike klase d a o d lu i koji p ozivi su b ez b e d n i i d a za njih ov e arg u m e n te u p o tre b i tip Object. U koliko hoete d a o n e m o g u ite o d re e n i p oziv k ad a se u tip u u p o treb lja v a dokerski arg u m en t, u listu a rg u m e n a ta stavite p a ra m e ta r tip a. To m o ete vid eti u ovoj v eom a je d n o sta v n o j klasi Skladiste:
//: genericki/Skladiste.java public class Skladiste<T> { private T vrednost; public Skladiste() {} public Skladiste(T vre) { vrednost = vre; } public void set(T vre) { vrednost = vre; } public T get() { return vrednost; } public boolean equals(Object obj) { return vrednost.equals(obj);

}
public static void main(String[] args) { Skladiste<Jabuka> Jabuka = new Skladiste<Jabuka>(new Jabuka()); Jabuka d = Jabuka.get(); Jabuka.set(d); // Skladiste<Voce> Voce = Jabuka; // Svoenje navie nije mogue Skladiste<? extends Voce> voce = Jabuka; // OK Voce p = v o c e . g e t O ; d = (Jabuka)voce.get(); // Vraa 'Object' try { Naranda c = (Naranda)voce.get(); // Nema upozorenja } catch(Exception e) { System.out.println(e) , } // voce.set(new Jabuka()); // Ne moete pozvati set() // voce.set(new Voce()); // Ne moete pozvati set() System.out.println(voce.equals (d)) ; // OK

}
} /* Ispis: (primer) java.lang.ClassCastException: Jabuka cannot be cast to Naranda true

* ///:-

Skladiste im a m e to d u s e t( ) koja p rim a ob jekat tip a T, m e to d u g e t( ) koja vraa objek at tip a T, i m e to d u eq u als( ) koja p rim a Object. Kao to ste ve videli, ako n ap rav ite ob jek at tip a Skladiste<Jabuka>, n e m o ete ga svesti navie na Skladiste<Voce>, ali ga m o ete svesti navie n a Skladiste<? extends Voce>. A ko po zov ete g e t( ), o n a vam vraa sam o o b jek at tip a Voce - to lik o zna u z d a tu g ran icu bilo ta to n asle u je Voce. U koliko zn ate stvarni tip, m o ete ga svesti navie n a o d re e n p o d tip klase Voce i za to neete d obijati u p o zo ren ja, ali rizikujete da se p o jav i ClassCastException. M eto d a s e t( ) ne radi ni

Poglavlje 15: Generiki tipovi

539

sa o b je k to m tip a Jabuka n iti sa o b jck to m tip a Voce, p o to je njen a rg u m e n t tak o e ? Extends Voce, to znai d a m oe b iti b ilo ta, a p rev o d ilac ne m oe da p ro v eri b e zb ed n o st tip a bilo ta. M e u tim , m eto d a eq u a ls( ) rad i d o b ro , zato to kao a rg u m e n t p rim a Object, a ne T. D akle, prevod ilac pazi sa m o na tip o v e o b jek ata koji bivaju p ro sle en i ili vraeni. O n ne an alizira k o d kako b i v id eo d a li zaista u p isu jete ili uitavate.

Kontravarijansa
M oe se o tii i s u p ro tn im p u te m i u p o tre b iti dokerski argum ent nadtipa. Tada kaete da je do kersk i a rg u m e n t o g ran ien bilo k o jo m n a tk laso m o d re e n e klase, tako to zad ate <? super MojaKlasa> ili ak u p o tre b ite p a ra m e ta r tipa: <? super T> (iako g eneriki p aram e ta r ne m o ete zad ati kao g ra n ic u n a d tip a , tj. n e m o ete rei <T super MojaKlasa>). O vo o m o g u u je d a b ezb ed n o p ro sled ite o b jek at n ek o g tip a u generiki tip. D akle, sa dokerskim a rg u m e n tim a n a d tip o v a m o ete u p isiv ati u k o n te jn e re (objekte tip a Collection):
//: genericki/DzokeriNadtipova.java import java.util.*; public class DzokeriNadtipova { static void upisiU(List<? super Jabuka> jabuke) { jabuke.add(new JabukaO); jabuke.add(new Jonathan()); // jabuke.add(new Voce()); // Greka

} } ///:A rg u m e n t jabuke je L ista n ekog tip a koji je n a d tip klase Jabuka; zato zn ate d a je toj listi b ezb e d n o d o d a ti ob jek at tip a Jabuka ili p o d tip a klase Jabuka. M e u tim , p o to je Jabuka donja granica , ne zn ate da li je takvoj L isti b ezb ed n o d o d a ti Voce, p o to biste na taj nain dozvolili da se L ista o tv a ra za d o d av an je tip o v a koji n isu Jabuka, to bi ugrozilo b e z b e d n o st statin ih tipova. D akle, g ranice p o d tip o v a i n ad tip o v a m o ete sm a tra ti n a in im a upisivanja" (p ro sleivanja u m e to d u ) u generiki tip, o d n o sn o ,,uitavanja (vraanja iz m eto d e) iz generikog tipa. G ran ice n a d tip o v a u b lau ju o g ran ien ja o n o g a to m o ete da p ro sled ite u m eto d u :
//: generi cki/GenerickoUpi sivanje.java import java.util.*; public class GenerickoUpisivanje { static <T> void tacnoUpisi(List<T> lista, T stavka) { lista.add(stavka);

}
static List<Jabuka> jabuke = new ArrayList<Jabuka>(); static List<Voce> voce = new ArrayList<Voce>(); static void fl() { tacnoUpisi(jabuke, newJabuka());

540

Misliti na Javi

// tacnolipisi (voce, new JabukaO); // Greka: // Incompatible types: foun Voce, required Jabuka // (Neusklaeni tipovi: naao Voce, oekivao Jabuka)

}
static <T> void upisivanjeSDzokerom(List<? super T> lista, T stavka) { lista.add(stavka);

}
static void f2() { upisivanjeSDzokerom(jabuke, new JabukaO); upisivanjeSDzokerom(voce, new JabukaO);

}
public static void main(String[] args) { fl(); f2(); }

} ///:M eto d a tacnoU pisi( ) u p o treb ljav a taa n p a ra m e ta r tip a (n e m a do k ersk o g arg u m e n ta). U f l ( ) m o ete vid eti d a to lepo ra d i - d o k le g o d u listu List<Jabuka> stavljate sam o o bjekte tip a Jabuka. M e u tim , tacnoU pisi( ) n e dozvoljava d a u listu List<Voce> stavite o b jek at tip a Jabuka, m ad a vi zn ate d a b i to treb alo d a b u d e m ogue. U m eto d i upisivanjeSD zokerom ( ) a rg u m e n t je lista List<? super T>, p a o n a sadri o d re e n i tip izveden iz T; stoga je n je n im m e to d a m a kao a rg u m e n t b e z b e d n o p ro sled iti T ili n eto izvedeno iz T. To m o ete v id eti u f2 ( ), gde je i dalje m o g u e staviti o b jek at tip a Jabuka u listu List<Jabuka>, kao p re, ali sada je objek at tip a Jabuka m o g u e staviti i u listu List<Voce>, kao to sm o oekivali. Istu v rstu analize m o em o sprovesti kao pregled kovarijanse i d o k ersk ih arg u m e n ata :
//: genericki/GenerickoCitanje.java import java.util.*; public class GenerickoCitanje { static <T> T tacnoProcitaj(List<T> lista) { return 1ista.get(O);

}
static List<Jabuka> jabuke = Arrays.asList(new JabukaO); static List<Voce> voce = Arrays.asList(new Voce()); // Statina metoda se prilagoava svakom pozivu: static void fl() { Jabuka a = tacnoProcitaj(jabuke); Voce f = tacnoProcitaj(voce); f = tacnoProcitaj(jabuke);

}
// Meutim, ako imate klasu, njen tip se utvruje // prilikom pravljenja njene instance: static class Citac<T> { T tacnoProcitaj(List<T> lista) { return 1ista.get(0); }

}
static void f2() { Citac<Voce> citacVoca = new Citac<Voce>(); Voce f = citacVoca.tacnoProcitaj(voce);

Poglavlje 15: Generiki tipovi

541

// Voce a = citacVoca.tacnoProcitaj(jabuke); // Greka: // tacnoProcitaj(List<Voce>) ne moe biti // primenjeno na (List<Jabuka>).

}
static class KovarijantniCitac<T> { T citajKovarijantno(List<? extends T> lista) { return lista.get(O);

} }
static void f3() { KovarijantniCitac<Voce> citacVoca = new KovarijantniCitac<Voce>(); Voce f = citacVoca.citajKovarijantno(voce); Voce a = citacVoca.citajKovarijantno(jabuke);

}
public static void main(String[] args) { fl(); f2(); f3();

} III-Kao pre, p rv a m eto d a tacnoP rocitaj( ) up otreb ljav a p recizan tip. Taj p recizan tip bez do k erskog arg u rn e n ta m o ete i upisivati u listu i itati iz nje. P o red toga, za p o v ra tn u v re d n o st, statin a generika m e to d a tacnoP rocitaj( ) zapravo se p rilag o av a svakom p o zivu i vraa o b jek at tipa Jabuka iz liste List<Jabuka> i o b jekat tip a Voce iz liste List<Voce> , kao to m o ete videti u f l ( ). D akle, ako m o ete da n a p rav ite sta ti n u g en erik u m e to d u , sam o za itan je nije vam p o tre b n a kovarijansa. M e u tim , ako im ate g en erik u klasu, p a ra m e ta r tip a te klase u tv r u je se p rilik o m p ra vljenja njene instance. Kao to v id ite u f2 ( ), in stan ca klase citacVoca m o e da p ro ita o b je k at tip a Voce iz liste List<Voce>, p o to je to n jen taan tip. Ali bi i lista List<Jabuka> tre b a lo d a proizvede objekte tip a Voce, a citacVoca to ne dozvoljava. D a bi se reio p ro b lem , m e to d a K ovarijantniCitac.citajK ovarijantno( ) p rim a List<? extends T>, pa je b ezb ed n o p ro itati T iz te liste (znate da je u njoj sve b a re m tip a T ili izved en o iz tipa T). U f 3 ( ) v idite da je sada b ez b ed n o p ro ita ti o b jek at tip a Voce iz liste

List<Jabuka>. Veba 28: (4) N aprav ite g eneriku klasu G enerickal<T> ija je d in a m e to d a p rim a a rg u m e n t tip a T. N aprav ite d ru g u g en erik u klasu Genericka2<T> ija je d in a m e to d a v raa a rg u m e n t tip a T. N apiite gen eriku m e to d u s k o n tra v a rija n tn im a rg u m e n to m p rv e generike klase koja poziva svoju m e to d u . N apiite d ru g u g en erik u m e to d u s k o v a rija n tnim a rg u m e n to m d ru g e g enerike klase koja poziva svoju m e to d u . T estirajte k o d p o m o u b iblio tek e poaciotipu.ljubimci.

Neogranieni dokerski argumenti


Neogm nieni dokerski a rg u m en t< i> p riv id n o oznaava bilo ta, p a izgleda k ao d a je korienje n eo g ran ie n o g dokerskog a rg u m e n ta ek v ivalen tn o k o rien ju sirovog tipa. Zaista, p revod ilac se na prv i p o gled slae s to m tv rd n jo m :

542

Misliti na Javi

//: genericki/NeograniceniDzokeri1.java import java.util public class NeograniceniDzokeril { static List listal; static List<?> lista2; static List<? extends Object> 1ista3; static void assignl(List lista) { listal = lista; 1ista2 = lista; // 1ista3 = lista; // Upozorenje: unchecked conversion (neproverena // konverzija) // Found: List, Required: List<? extends Object>

}
static void assign2(List<?> lista) { listal = lista; 1ista2 = lista; 1ista3 = lista;

}
static void assign3(List<? extends Object> lista) { listal = lista; lista2 = lista; 1ista3 = lista;

}
public static void main(String[] args) { assignl(new ArrayList()); assign2(new ArrayList()) ; // assign3(new ArrayList()); // Upozorenje: // unchecked conversion (neproverena konverzija). // Found: ArrayList, Required: List<? extends Object> assignl(new ArrayList<String>()); assign2(new ArrayLi st<Stri ng>()); assign3(new ArrayList<String>()); // Oba oblika su prihvatljiva kao List<?>: List<?> divljaLista = new ArrayList(); divljaLista = new ArrayList<String>(); assignl(divljaLi sta); assign2(divljaLista); assign3(di vljaLi sta);

} } ///:Im a m n o g o ovakvih sluajeva gde b i p revo dilac m ogao da se m alo m anje b rin e o to m e da li u p o treb ljav ate neki sirov tip ili <?>. U tim sluajevim a, m o ete sm atra ti d a je <?> dekoracija; pa ipak, taj dokerski a rg u m e n t je v re d an , zato to u stvari kazuje: Ovaj k od sam n ap isao im aju i u v id u Javin generiki m eh an iza m , i ovde n e oznaava da u p o tre bljavam n eki siro v tip, nego d a u o v o m sluaju generiki p a ra m e ta r m oe da sadri bilo koji tip .

Poglavlje 15: Generiki tipovi

543

U d ru g o m p rim e ru p o k azan a je je d n a vana p rim e n a n e o g ran i e n ih d o k ersk ih arg u m e n a ta . K ada rad ite s vie g en erik ih p ara m e ta ra , p o n e k a d tre b a d o zvoliti d a je d a n p a ra m e ta r b u d e p ro izv o ljn o g tip a, d o k za d ru g i m o ra te zadati o d re e n i tip:
//: genericki/NeograniceniDzokeri'2.java import java.util public class NeograniceniDzokeri2 { static Map mapal; static Map<?,?> mapa2; static Map<String,?> mapa3; static void dodelil(Map mapa) { mapal = mapa; } static void dodeli2(Map<?,?> mapa) { mapa2 = mapa; } static void dodeli3(Map<String,?> mapa) { mapa3 = mapa; } public static void main(String[] args) { dodelil(new HashMapO); dodeli2(new HashMapO); // dodeli3(new HashMapO); // Upozorenje: // Unchecked conversion (neproverena konverzija). // Found: HashMap, Required: Map<String,?> // (Naao: HashMap, Oekivao: Map<String,?>) dodel i1 (new HashMap<String,Integer>()); dodeli2(new HashMap<String,Integer>()); dodel i3(new HashMap<String,Integer>());

} } ///:I p o n o v o , k ad a su svi dokerski a rg u m e n ti n e o g ran ien i, kao u Map<?,?>, izgleda d a p rev o d ilac to ne razlikuje o d sirove M ape. Pored toga, p ro g ra m NeograniceniDzokeril.java p o kazu je da prev o d ilac razliito tre tira liste List<?> i List<? extends Object>. Z b u n ju je to to prev o d ilac ne pravi uvek razliku izm ed u , na p rim er, lista L ist i L ist< ?> , pa biste m o g li po m isliti da su o n e jed n ak e. Z aista, p o to se generiki a rg u m e n t brie d o svoje p rv e granice, izgledalo bi kao d a je List<?> ekvivalentna sa List<Object> i da je L ist isto to i L ist< O b je c t> - sem to n ijed n a o d tih tv rd n ji nije p o tp u n o tana. L ist zap ravo znai sirova L ista koja sadr.i bilo koji objek at tip a Object, d o k L ist< ?> znai nesirova L ista odredenog tipa , sam o to m i ne z n a m o kojega. Kada p rev o dilac pravi razliku izm eu sirovih tipova i tip o va koji su zadati n eo g ra n i e n im d o k ersk im a rg u m en tim a? U sledeem p rim e ru u p o treb lje n a je p re th o d n o d efin isan a klasa Skladiste<T>. U n je m u su m eto d e koje Skladiste p rim a ju kao arg u m en t, ali na razliite naine: kao sirov tip, sa o d re e n im p a ra m e tro m tip a i sa n eo g ran ien im o k ersk im a rg u m e n to m kao p a ra m e tro m :
//: genericki/Dzokeri.java // Istraivanje znaenja dokerskih argumenata. public class Dzokeri { // Sirov argument: static void siroviArgumenti(Skladiste skladiste, Object arg) {

544

Misliti na Javi

// skladiste.set(arg); // Upozorenje: // Unchecked call to set(T) as a // member of the raw type Skladiste // (Neproveren poziv metode set(T) kao // lana sirovog tipa Skladiste) // skladiste.set(new DzokeriO); // Isto upozorenje // Ovo nije dozvoljeno; nema tipa 'T': // T t = sk ladiste.getO; // OK, ali izgubljena je informacija o tipu: Object obj = skl ad is te.getO;

)
// Slina kao siroviArgumenti (), ali prouzrokuje // greke umesto upozorenja: static void neogArg(Skladiste<?> skladiste, Object arg) { // skladiste.set(arg); // Greka: // Metoda set((rezultat od) ?) u Skladiste<(rezultat od) ?> // ne moe biti primenjena na (Object) // skladiste.set(new DzokeriO); // Ista greka // Ovo nije dozvoljeno; nema tipa 'T': // T t = sk ladiste.getO; // OK, ali je izgubljena informacija o tipu: Object obj = skladiste.get();

}
static <T> T odredjenal(Skladiste<T> skladiste) { T t = skladiste.get(); return t;

}
static <T> T odredjena2(Skladiste<T> skladiste, T arg) { Skladiste.set(arg); T t = skladiste.get(); return t;

}
static <T> T divljiPodtip(Skladiste<? extends T> skladiste, T arg) { // skladiste.set(arg); // Greska: // Metoda set((rezultat od) ? extends T) u // Skladiste<(rezultat od) ? extends T> // ne moe da se primeni na (T) T t = s kladiste.getO; return t;

}
static <T> void divljiNadtip(Skladiste<? super T> skladiste, T arg) { skladiste.set(arg); // T t = skladiste.get(); // Greska: // Neusklaeni tipovi: naao Object, oekivao T

Poglavlje 15. Generiki tipovi

545

// OK, ali je izgubljena informacija o tipu: Object obj = skladiste.get();

}
public static void main(String[] args) { Skladiste sirovo = new Skladiste<Long>();

//

111 :

sirovo = new Skladiste(); Skladiste<Long> potpunoZadato = new Skladiste<Long>(); Skladiste<?> neograniceno = new Skladiste<Long>(); Skladiste<? extends Long> ograniceno = new Skladiste<Long>(); Long lng = 1L; siroviArgumenti(sirovo, lng); siroviArgumenti(potpunoZadato, lng); siroviArgumenti(neograniceno, lng); siroviArgumenti(ograniceno, Ing); neogArg(sirovo, lng); neogArg(potpunoZadato, lng); neogArg(neograniceno, lng); neogArg(ograniceno, Ing); // Object rl = odredjenal(sirovo); // Upozorenja: // Unchecked conversion from Skladiste to Skladiste<T> // (Neproverena konverzija tipa Skladiste u Skladiste<T>) // Unchecked method invocation: odredjenal(Skladiste<T>) // is applied to (Skladiste) // (Neproveren poziv metode: odredjenal(Skladiste<T>) // primenjena na (Skladiste)) Long r2 = odredjenal(potpunoZadato); Object r3 = odredjenal(neograniceno); // Mora vratiti Object Long r4 = odredjenal(ograniceno); // Long r5 = odredjena2(sirovo, Ing); // Upozorenja: // Unchecked conversion from Skladiste to Skladiste<Long> // (Neproverena konverzija tipa Skladiste u Skladiste<Long>) // Unchecked method invocation: odredjena2(Skladiste<T>,T) // is applied to (Skladiste.Long) // (Neproveren poziv metode: odredjena2(Skladiste<T>,T) // primenjena na (Skladiste,Long)) Long r6 = odredjena2(potpunoZadato, lng); // Long r7 = odredjena2(neograniceno, Ing); // Greska: // odredjena2(Skladiste<T>,T) ne moe biti primenjena // na (Skladiste<(rezultat od) ?>,Long) // Long r8 = odredjena2(ograniceno, lng); // Greska: // odredjena2(Skladiste<T>,T) ne moe biti primenjena // na (Skladiste<(rezultat od) ? extends Long>,Long) // Long r9 = divlj iPodtip(sirovo, Ing); // Upozorenja: // Unchecked conversion from Skladiste

546

Misliti na Javi

// // // // // // // // //

to Skladiste<? extends Long> (Neproverena konverzija tipa Skladiste

Skladiste<? extends Long >) is

Unchecked method invocation: divljiPodtip(Skladiste<? extends T>,T) applied to (Skladiste.Long) (Neproveren poziv metode: divljiPodtip(Skladiste<? extends T>,T) primenjena na (Skladiste.Long))

Long rlO = divljiPodtip(potpunoZadato, lng); // OK, ali moe da vrati samo Object: Object rll = divljiPodtip(neograniceno, lng); Long rl2 = divljiPodtip(ograniceno, Ing); // // // // // // // // // // // divljiNadtip(sirovo, lng); // llpozorenja: Unchecked conversion from Skladiste to Skladiste<? super Long> (Neproverena konverzija tipa Skladiste u Skladiste<? super Lorig >) Unchecked method invocation: divljiNadtip(Sk1adiste<? super T>,T) is applied to (Skladiste.Long) (Neproveren poziv metode: d ivljiNadtip(Skladiste<? super T>,T) primenjena na (Skladiste.Long))

divljiNadtip(potpunoZadato, lng); // divljiNadtip(neograniceno, lng); // Greska: // // // // divljiNadtip(Skladiste<? super T>,T) ne mo e biti primenjena na (Sk1adiste<(rezultat od) ?>,Long) divljiNadtip(Skladiste<? super T>,T) ne moe biti primenjena na (Skladiste<(rezultat od) ? extends Long>,Long)

// divljiNadtip(ograniceno, lng); // Greska:

} III-U m eto d i siroviA rgum enti( ), p revodilac zn a d a je Skladiste gen erik i tip, pa iako je o vde izraen k ao sirov tip, p revodilac zna d a p ro sled iv an je o b jek ta tip a Object m etodi s e t( ) nije b ezbedno . Poto je to sirov tip , m eto d i s e t( ) m o ete p ro sled iti o b jek at bilo kojeg tip a koji e biti sveden navie na Object. D akle, k ad g o d im a te sirov tip, n em ate p rov eru u v rem e prevoenja. Isto po k azu je poziv m eto d e g e t( ): n em a tip a T, pa rezultat m o e biti sam o Object. Lako je pom isliti da su sirovo Skladiste i Skladiste<?> p rib lin o ista stvar. M etoda neogA rg( ) istie njihovu razliitost - o n a otkriva istu vrstu p ro b lem a, ali ih prijavljuje kao greke, a ne kao upozorenja, p o to sirovo Skladiste p rim a sve kom b in acije svih tipova, d o k Skladiste<?> p rim a h o m o g en u kolekciju odreenog tipa , i ne m oete m u proslediti Object.

Poglav|je 15: Generiki tipovi

547

U m e to d a m a o d re d je n a l( ) i odredjena2( ), u p o tre b lje n i su ta n i generik i p a ra m e tri - n em a dok ersk ih a rg u m e n a ta . V ideete d a od red jen a2( ) zb og d o d a tn o g a rg u m e n ta im a drugaija o g ran ien ja n ego m eto d a o d re d je n a l( ). U m e to d i divljiP odtip( ), o g ran ien ja tip a Skladiste u b lae n a su na Skladiste koje p rim a sve to je izvedeno iz T. O p et, to zn ai d a T m o e biti Voce, d o k bi Skladiste m og lo biti Skladiste<Jabuka>. D a bi se spreilo stavljanje o b jek ta tip a N aranda u Skladiste<Jabuka>, z a b ra n je n je p o ziv m e to d e s e t( ) (i svih d ru g ih m e to d a koje kao a rg u m e n t p rim a ju taj p a ra m e ta r tip a). M e u tim , i d alje zn a te d a sve to izae iz o b jekta tip a Skladiste<? extends Voce> m o ra u n a jm a n ju r u k u b iti tip a Voce, p a je d ozvoljena m eto d a g e t( ) (i sve d ru g e m e to d e ija je p o v ra tn a v re d n o s t tog p a ra m e ta rsk o g tip a). D okerski a rg u m e n ti n a d tip o v a prik a zan i su u m e to d i divljiN adtip( ), koja se p o n a a su p ro tn o od m eto d e divljiP odtip(): skladiste m o e b iti k o n te jn e r koji p rim a svaki tip koji je n a d tip o d T. O tu d a m e to d a s e t( ) p rim a o b jek a t tip a T, p o to sve to ra d i sa osn ov nim tip o m , zbog p o lim o rfiz m a ra d i i s njeg ov im izveden im tip o m (dakle i sa T). M e u tim , poziv m eto d e g e t( ) n e p o m ae, p o to tip koji sad ri Skladiste m o e b iti b ilo koji n ad tip , p a je jed in i b e zb e d a n n a d tip Object. U m eto d i n eograniceno( ) iz ovog p rim e ra vide se i o g ran i en ja o n o g a to se m o e i ne m oe u ra d iti s n e o g ra n i e n im p a ra m e tro m : o b je k at tip a T n e m oete ni d o b iti o d m e to d e g e t( ) niti p ro sled iti m e to d i s e t( ), p o to tip T n em ate. U m etod i m a in ( ) vidite koja o d ovih m eto d a p rim a koje tipove arg u m en ata be/. greaka i u pozorenja. R adi m igracijske k o m p atib iin o sti, m eto d a siroviArgumenti( ) b ez ikakvih u p o zo renja p rim a sve varijante objekta Skladiste. I m eto d a neogA rg( ) jedn ak o prihv ata sve tipove, m ada ih, kao to je ve bilo reeno, u telu m eto d e o b ra u je n a razliite naine. Ako referencu sirovog tipa Skladiste prosledite m eto d i koja p rim a o d re en generiki tip (bez dokerskih a rg u m en ata), dob iete u p o zo ren je, zato to o d re e n i arg u m en t oekuje inform acije koje u sirovom tip u ne postoje. A ukoliko m eto d i o d red jen al() prosledite neo g ran ien u referencu, n em a in fo rm acija o tip u p o m o u kojih bi se u tv rd io p o v ratn i tip. V idite da odredjena2( ) im a najvie o g ranien ja, p o to zahteva u p rav o Skladiste<T> i a rg u m e n t tipa T, i zato generie greke i u p o zo ren ja ukoliko jo j ne d ate tan e arg u m en te. P onekad je takvo p on aan je d o b ro , ali ako vas previe sputava, m o ete u p o treb iti razliite dokerske arg u m en te, u zavisnosti o d toga da li h o ete p o v ra tn e v red n o sti tipo va o d re enih vaim gen erikim a rg u m e n to m - kao to je u ra en o u m eto d i divljiPodtip( ) ili svom generikom a rg u m e n tu hoete da p ro sle u jete a rg u m en te o d re en ih tipova - kao to je u ra e n o u m eto d i divljiN adtip( ). Dakle, p red n o st korienja o d re en ih (tan ih ) tip ov a u m esto dokerskih arg u m e n a ta jeste to to s generikim p a ra m e trim a m o ete vie d a u rad ite. Ali korienje dokerskih arg u m e n a ta o m o g u u je p rih v atan je ireg opsega p a ram etrizo v an ih tipo va k ao arg u m en ata. U svakom p o je d in o m sluaju saini o d lu u jete koji k o m p ro m is v am vie odgovara.

Konverzija hvatanjem
Jedna k o n k re tn a situacija zahteva da u p o tre b ite do k erski a rg u m e n t < ?> , a ne sirov tip. Ako sirov tip p ro sledite m e to d i koja u p o treb ljav a < ?> , p revo dilac m o e da zakljui koji je stvarn i p a ra m e ta r tipa, tako da i ta m eto d a m o e da pozove d ru g u m e to d u koja p rim a

548

Misliti na Javi

ta n o taj tip. Ta te h n ik a se naziva konverzija hvatanjem (engl. capture conversion ), je r se n esp ecificiran dokerski tip hvata i k o n v e rtu je u o d re e n tip . Pokazaem o konverziju h v a ta n je m u n a re d n o m p rim e ru , gde k o m e n ta ri o u p o z o re n jim a vae tek k ad a se u k lo n i a n o ta c ija @ S u p p re ssW arn in g s:
//: genericki/KonverzijaHvatanjem.java public class KonverzijaHvatanjem { static <T> void fl(Skladiste<T> skladiste) T t = s k la di st e. ge t( ); Sy stem.out.println(t.getClass() . g e t S i m p l e N a m e O ) ; {

}
static void f2(Skladiste<?> skladiste) { f l (s kl ad is te ); // Poziv sa uhvaenim tipom

}
@SuppressWarnings("unchecked") public static void main(String[] args) { Skladiste sirovo = new Skla di st e< In te ge r> (l); // fl(sirovo); // Prouzrokuje upozorenja f2(sirovo); // Nema upozorenja Skladiste sirovNadtip = new Skladiste(); si rovNadtip.set(new O b j e c t O ) ; // Upozorenje f 2 ( s i r ov Na dt ip ); // Nema upozorenja // Svoenje navie na Skladiste<?>, koje prevodilac ipak shvata: Skladiste<?> sa dzokerima = new S k l a di st e< Do ub le >( 1.0 ); f2(sa d z o k e r i m a ) ;

}
} /* Ispis: Integer Object Double

* ///:Svi p a ra m e tri tip a u f l ( ) o d re d en i su, bez d okerskih a rg u m e n a ta i granica. U f 2 ( ), p a ra m e ta r S k la d iste je n eo g ra n i en i dokerski a rg u m e n t, pa se ini da zapravo nije pozn a to ta p redstavlja. M e d u tim , u n u ta r f 2 ( ) poziva se f l ( ), a f l ( ) zahteva p o z n a t p a ra m e tar. D a bi se m o g ao u p o tre b iti u pozivu m e to d e f l ( ), taj p a ra m e ta r tip a se hvata u to ku p o ziv anja f 2 ( ). M o d a se p ita te bi li se ova te h n ik a m ogla u p o tre b iti za upisivanje? Ali to bi zahtevalo da za je d n o s tip o m Skladiste<?> p ro sled ite i o d re e n tip. K onverzija h v atanjem radi sam o u situ acijam a gde u n u ta r m eto d e m o ra te da rad ite s o d re e n im tip o m . Im ajte u vidu da iz f 2 ( ) n e m o ete d a v ra tite T, je r je T za f 2 ( ) n ep o zn at. K onverzija h v atan jem je zanim ljiv a, ali v e o m a o g ran ien a.

Veba 29: (5) N apravite gen erik u m e to d u koja kao arg u m en t p rim a Skladiste<List<?. U tv rd ite koje m e to d e m oete, a koje ne m oete pozivati za Skladiste i za Listu. Ponovite za a rg u m e n t L ist<Skladiste<?.

Poglavlje 15: Generiki tipovi

549

Nedostaci
U o v o m o d eljk u g o v o rim o o n ek im ned o stacim a korienja gen erik ih tip o v a u Javi.

Prosti tipovi ne mogu biti parametri tipa


K ao to je u p re th o d n o m d elu poglavlja b ilo sp o m e n u to , je d n o o d o g ran ien ja Javin ih gen erik ih tip o v a jeste to to p ro ste tipove ne m o ete u p o tre b iti k ao p a ra m e tre tip a. P rim era radi, n e m o ete n ap rav iti ArrayList<int>. Z ato tre b a u p o treb ija v ati o m o ta k e klase za p ro ste tip o v e zajed n o sa a u to m a tsk im p a k o v an jem Jave SE5. A ko n a p rav ite ArrayList<Integer> i s tim k o n te jn e ro m u p o tre b ite p ro ste cele brojeve (int), videete d a ih au to m atsk o p akovanje a u to m atsk i k o n v e rtu je u Integer i n a za d - g otovo kao da im ate ArrayList<int>:
//: genericki/Listalnt.java // Automatsko pakovanje nadoknauje nemogunost // upotrebe prostih tipova u generikom kodu. import java.util public class Listalnt { public static void main(String[] args) { List<Integer> li = new ArrayList<Integer>(); for(int i = 0 ; i < 5 ; i++) 1i .add(i); for(int i : li) System.out.print(i + " ");

}
} /* Ispis: 0 12 3 4

* ///:O b ra tite p an ju na to da a u to m atsk o pakovanje o m o g u u je ak i da foreach sin ta k so m p rav ite cele brojeve (int). Po prav ilu , ovo reenje radi d o b ro - cele brojeve (in t) m o ete da skladitite i da ih uitavate. Sve to p ra te au to m atsk e konverzije, ali k o risn ik ih ne vidi. M e u tim , ako p erfo rm an se p ro g ra m a ne zadovoljavaju, m oete u p o tre b iti specijalizovanu verziju k o n te jn e ra p rila g o en u p ro stim tip o v im a; je d n a verzija n jihovog o tv o ren o g k o d a nalazi se n a adresi

org.apache.commons.collections.primitives.
O vo je d ru g i p ristu p , koji pravi skup bajtova (Set o b jek ata tip a Byte):
//: genericki/SkupBajtova.java import java.util.*; public class SkupBajtova { Byte[] moguci = { 1,2,3,4,5,6,7,8,9 }; Set<Byte> mojSkup = new HashSet<Byte>(Arrays.asLi st(moguci));

550

Misliti na Javi

// A1i ovo ne moete: // Set<Byte> mojSkup2 = new HashSet<Byte>( // Arrays.<Byte>asList(l,2,3,4,5,6,7,8,9));

} III-Vodite ra u n a o to m e d a a u to m a tsk o p ak o v an je reava neke p ro b le m e, ali n e sve. U sledeem p rim e ru im a m o generiki in terfejs Generator koji sp e fic ira m e to d u sledeci( ) koja v raa o b jek at p a ra m e ta rsk o g tipa. Klasa PNiz sadri g eneriku m e to d u koja p o m o u g e n e ra to ra p o p u n ja v a n iz o b je k tim a (p o to je m e to d a statina, u ovom sluaju n e bi o d gov aralo d a klasu n a p ra v im o k ao g en erik u ). U poglavlju N izovi nai ete vie realizacija interfejsa Generator, a u m e to d i m a in ( ) vidite kako P N iz.popuni( ) p o p u n jav a nizove objektim a:
/ / : genericki/TestProstihGenerickih.java import net.mindview.util.*; // Popuni niz generatorom: class PNiz { public static <T> T[] popuni(T[] n, Generator<T> gen) { for(int i = 0 ; i < n.length; i++) n[i] = gen.sledeci (); return n;

} }
public class TestProstihGenerickih { public static void main(String[] args) { String[] znakovni_nizovi = PNiz.popuni( new String[7], new RandomGenerator.String(10)); for(String s : znakovni_nizovi) System.out.pri ntln(s); Integer[] celi_brojevi = PNiz.popuni( new Integer[7], new RandomGenerator.Integer()) ; for(int i: celi_brojevi) System.out.println(i); // Ovde vam automatsko pakovanje ne moe pomoi. Ovo se nee prevesti: // int[] b = // PNiz.popuni(new int[7], new RandIntGenerator());

}
) /* Ispis: YNzbrnyGcF 0WZnTcQrGs eGZMmJMRoE suEcUOneOE dLsmwHLGEa hKcxrEqUCB bklnaMesbt 7052 6665 2654

Poglavlje 15: Generiki tipovi

551

3909 5202 2209 5458

* ///:Poto Random Generator.Integer realizuje interfejs Generator<Integer>, nad a o sam se d a e a u to m a tsk o pakovanje au to m atsk i k onvertovati v red n o st rezultata m eto d e sledeci( ) iz Integer u int. M e u tim , a u to m atsk o pakovanje ne vai za nizove, p a to ne radi.

Veba 30: (2) N ap rav ite Skladiste za sve om o ta e p ro stih tipova i po kaite d a a u to m a tsk o p ak o v an je i raspak ivanje fu n k cio n ie za m eto d e s e t( ) i g e t( ) svake instance.

Realizacija parametrizovanih interfejsa


Klasa n e m o e d a realizuje dve v arijan te istog g enerikog interfejsa. (Z bo g b risan ja, obe p o sta ju isti interfejs.) To se d o g a a u sledeoj situaciji:
//: genericki/ViseVarijanatalnterfejsa.java // {CompileTimeError} (Ne moe se prevesti) interface PlacaSe<T> {} class Zaposleni implements PlacaSe<Zaposleni> {} class PoSatu extends Zaposleni implements P1acaSe<PoSatu> {} ///:-

PoSatu se nee prevesti zato to se klase PlacaSe<Zaposleni> i PlacaSe<PoSatu> b risan jem svode na istu klasu PlacaSe, pa bi g o rn ji k od znaio da isti interfejs realizujete d v a p u t. S to je jo zanim ljivije, ako iz o b e u p o tre b e interfejsa PlacaSe u k lo n ite g enerike p a ra m e tre - a p rev odilac ba to radi b risa n je m k o d e biti preveden. O vaj n e d o stata k u m e da sm eta kada radite s n ek im od o sn o vn ih Javinih interfejsa, kao to je Com parable<T>, to ete v ideti u n astavku odeljka. Veba 3 1: ( 1) U k lo n ite svu g en erin o st i/. p ro g ra m a ViseVarijanatalnterfejsa.java i prep rav ite k o d tako da se prevede.

Eksplicitna konverzija tipova i upozorenja


K onvertovanje tipova i n ared b a instanceof ne u tiu na p a ram etre g enerikog tip a. Sledei k o n te jn e r in te rn o skladiti v red n o sti kao objekte tip a Object i k on v ertu je ih nazad u T kad a ih izvadite:
/ / : generi cki/KonverzijaGeneri cki h.java class StekNepromenljiveVelicine<T> { private int indeks = 0; private Object[] skladiste; public StekNepromenljiveVelicine(int velicina) { skladiste = new 0bject[vel icina];

552

Misliti na Javi

public void stavina(T stavka) { skladiste[indeks++] = stavka; } @SuppressWarnings("unchecked") public T skinisa() { return (T)skladiste[--indeks]; }

}
public class KonverzijaGenerickih { public static final int VELICINA = 10; public static void main(String[] args) { StekNepromenljiveVelicine<String> znakovni_nizovi = new StekNepromenljiveVelicine<String>(VELICINA); for(String s : "A B C D E F G H I J , , .split(" ")) znakovni_nizovi.stavina(s); for(int i = 0; i < VELICINA; i++) { String s = znakovni_nizovi.skinisa(); System.out.print(s + 1 1 ");

} }
} /* Ispis: J I H G F E D C B A

* ///:-

Da n em a ano tac ije @SuppressWarnings, p rev o d ilac b i za m e to d u sk in isa( ) dao u p o zo ren je unch eck ed cast (n e p ro v eren a k onverzija). O n zbog b risan ja ne m oe znati d a li je konverzija b ezb ed n a, iako m eto d a sk in isa( ) zap rav o ne obavlja n ik ak v u konverziju. T se brie do svoje prve granice, to je p o d ra z u m e v a n o Object, pa sk in isa( ) sam o k o nv ertuje Object u Object. Im a sluajeva kada g eneriki k o d ne u k lan ja p o tre b u za e k sp lic itn o m k o nverzijom . Tada prevod ilac izdaje u p o zo ren je, to je n e u m esn o . N a p rim e r:
//: genericki/PotrebnaKonverzija.java import java.io.*; import java.util.*; public class PotrebnaKonverzija { @SuppressWarni ngs("unchecked") public void f(String[] args) throws Exception { ObjectlnputStream ulaz = new ObjectInputStream( new FileInputStream(args[0])); List<Spravica> oblici = (List<Spravica>)ulaz.readObject();

} } ///:Kao to ete v ideti u sledeem poglavlju, m e to d a readO bject( ) ne m o e znati ta ita, pa vraa o b jek at koji se m o ra ko n v erto vati. Ali k ad a k o m e n ta rie te a n o ta c iju @SuppressW arnings i prev ed ete p ro g ra m , d o b iete u p o zo ren je:
Note: PotrebnaKonverzija.java uses unchecked or unsafe operations. Note: Recompile with -XIint:unchecked for details.

Poglavlje 15: Generiki tipovi

553

A ako p o slu ate ta u p u tstv a i p o n o v o prev ed ete k o d uz -X lin t:u n c h e c k e d :


PotrebnaKonverzija.java:12: warning: [unchecked] unchecked cast found : java.lang.Object requi red: java.uti1.Li st<Spravica> List<Shape> oblici = (List<Spravica>)ulaz.readObject();

m o ra te da konvertu jete, a ip ak vam kau da ne b i trebalo. D a biste reili ovaj p ro b lem , m o rate u p o tre b iti no v oblik konverzije uveden u Javi SE5 - ko nverziju p rek o generike klase:
/ / : genericki/ClassKonverzija.java import java.io.*; import java.util.*; public class ClassKonverzija { @SuppressWarnings("unchecked") public void f(String[] args) throws Exception { ObjectlnputStream ulaz = new ObjectInputStream( new FileInputStream(args[0])); // Nee se prevesti: // List<Spravica> Izl =

//

Li st<Spravica>.c la s s . c a st(u la z . readObject( ) ) ;


List<Spravica> lz2 = List.class.cast(ulaz.readObject());

) III-M e u tim , n e m o ete k o n v erto v ati o b jek at u stv arn i tip (List<Spravica>). D ru g im reim a, ne m o ete rei:
List<Spravica>.class.cast(ulaz.readObject())

Pa ak i kada o a te jo je d n u konverziju:
(List<Spravica>)List.class.cast(ulaz.readObject())

ipak ete d o b iti u pozorenje.

Veba 32: (1) U verite se da StekNeproinenljiveVelicine u p ro g ra m u KonverzijaGenerickih.java g enerie izuzetke kada p o k u ate d a izaete izvan n jegovih granica. D a li to
znai da ne m o ra te pisati k o d za p ro v eru granica?

Veba 33: (3) P opravite p ro g ram KonverzijaGenerickih.java p o m o u klase ArrayList.

Preklapanje
O vo se nee prevesti, iako bi se reklo da valja p o k u ati:
//: genericki/ ListaZallpotrebu.java // {CompileTimeError} (Nee se prevesti) import j a v a . u t i l .*;

554

Misliti na Javi

public class ListaZaUpotrebu<W,T> j void f(List<T> v) {} void f(List<W> v) {}

} ///:B risanje p ro u zro k u je d a obe v arijan te p rek lo p ljen e m e to d e im aju isti p o tp is. U m esto g ornjeg p ristu p a , m eto d am a m o ra te d a ti razliita im en a kada o b risan i arg u m e n ti n e proizvo d e jed in stv en e liste arg u m en ata:
//: genericki/ListaZaUpotrebu2.java import java.util public class ListaZaUpotrebu2<W,T> { void fl(List<T> v) {} void f2(List<W> v) {}

} ///= Sreom , o vu v rstu p ro b lem a otk riv a prevodilac.

Osnovna klasa otima interfejs


P retp o stav im o da im ate klasu Ljubimac koja se m o e p o re d iti (engl. cotnpare to) sa d ru g im o b je k tim a tip a Ljubimac, zato to realizuje interfejs Comparable:
//: genericki/UporedivLjubimac.java public class UporedivLjubimac implements Comparable<UporedivLjubimac> { public int compareTo(UporedivLjubimac arg) { return 0; }

} ///:V redelo b i suziti tip s kojim se po tk lasa o d UporedivLjubimac m oe p o red iti. P rim era ra d i, Macka bi treb alo d a je u p o red iv a (engl. cottiparable) sam o s d ru g im o b jek tim a tip a

Macka:
//: genericki/OtetiInterfejs.java // {CompileTimeError} (Ne moe se prevesti) class Macka extends UporedivLjubimac implements Comparable<Macka>{ // Greka: interfejs Comparable ne moe se nasleivati // s razliitim argumentima: <Macka> i <Ljubimac> public int compareTo(Macka arg) { return 0; }

} ///:N aalost, ovo nee raditi. Kada se in terfejsu Com parable je d n o m u tv rd i a rg u m en t UporedivLjubimac, n ijed n a d ru g a klasa koja ga realizuje nee m oi da se p o red i ni sa im d ru g im sem sa o bjektim a klase UporedivLjubimac:

Poglavlje 15: Generiki tipovi

555

/ / : genericki/Ogranicenillporedi vi Ljubimci.java class Hrcak extends UporedivLjubimac implements Comparable<UporedivLjubimac> { public int compareTo(UporedivLjubimac arg) { return 0; }

}
// IIi samo: class Guster extends UporedivLjubimac { public int compareTo(UporedivLjubimac arg) { return 0; }

1 III-H rcak po k azu je da je m o g u e p o n o v o realizovati isti in terfejs koji je u klasi UporedivLjubimac, ukoliko je id en tian , u k lju u ju i tu i p a ra m e ta rsk e tipove. M e u tim , to je isto kao da su sam o prek lo p ljen e m e to d e u o sn o v n o j klasi, k ao to se vid i u klasi Guster.

Samoogranieni tipovi
U Javino m g enerik o m k o d u p e rio d i n o se p o n av lja je d a n z b u n ju ju i id io m p ro jek to van ja. Evo kako izgleda:
class SamoOgraniceni<T extends SamoOgraniceni<T { // ...

O vo izaziva vrtoglav icu kao dva ogledala u p e re n a je d n o u d ru g o - to je neka v rsta besk o n a n e refleksije. Klasa SamoOgraniceni p rim a g eneriki a rg u m e n t T, T je o g ran ien g ran ico m , a ta granica je SamoOgraniceni sa a rg u m e n to m T. O vaj p rim e r je teko analizirati kada ga p rv i p u t vidite; n jim e se istie da je rezervisana re extends, kada je u p o treb ljen a s g ran icam a, p o tp u n o d ru g aija n ego kada se k o risti za pravljen je potklasa.

Generiki kod koji se neobino ponavlja


D a biste shvatili ta znai sam o o g ran i en tip, p o n im o o d je d n o sta v n ije verzije ovog id io m a p ro jek tovanja, o n e bez sam o o g ran ien ja. G en eriki p a ra m e ta r ne m o ete naslediti d ire k tn o . M e u tim , m oete d a n asledite klasu koja taj generiki p a ra m e ta r u p o treb ljav a u so p stv en o j definiciji. D ru g im reim a, m oete rei:
//: genericki/GenerickiTipKojiSamSebePonavlja.java class GenerickiTip<T> {} public class GenerickiTipKojiSamSebePonavlja extends GenerickiTip<GenerickiTipKojiSamSebePonavlja> {} ///:-

556

Misliti na Javi

O vo b ism o m ogli nazvati generiki tip koji se neobino ponavlja ( Curiously Recurring Generics, CRG ), p o C o p lien o v o m ablonskom obrascu koji se neobinoponavlja u C + + -u . O n o n eo b i n o ponavlja o d n o si se n a in jen icu d a se klasa, p rili n o n eo b in o , pojavljuje u so pstvenoj o snov n o j klasi. D a biste shvatili ta to znai, naglas izgovorite: P ravim n o v u klasu koja nasleuje generiki tip koji im e m oje klase u zim a za svoj p a ra m e ta r. Sta generiki osnovni tip m oe da u radi s d a tim im e n o m izvedene klase? Pa, u Javi se generiki m e h a n iza m o d n o si n a arg u m en te i p o v ra tn e tipove; o n m oe d a n ap rav i o sn o v n u klasu koja za svoje arg u m en te i p o v ratn e tipove up otrebljava izvedenu klasu. Isto tako, izvedenu klasu o n m oe da upo treb i za tipove polja, iako e o n i brisan jem b iti svedeni n a Object. To izraava sledea generika klasa:
//: genericki/OsnovnoSkladi ste.java public class OsnovnoSkladiste<T> { T element; void set(T arg) { element = arg; } T get() { return element; } void f() { System.out.println(element.getClass() .getSimpleNameO);

} } ///:D obili sm o o b i an generiki tip ije m eto d e i p rim a ju i d a ju objekte istog p aram e tarskog tip a , k ao i m e to d u koja o b ra u je to u sk lad iten o polje (iako n ad njim obavlja sam o op eracije tip a Object). OsnovnoSkladiste m o em o d a u p o tre b im o u g en erik o m tip u koji se n eo b in o p o navlja (CRG): //: generi cki/CRGsaOsnovnimSkl adi stem.j ava class Podtip extends OsnovnoSkladiste<Podtip> {} public class CRGsaOsnovnimSkladistem { public static void main(String[] args) { Podtip stl = new PodtipO, st2 = new PodtipO; stl.set(st2); Podtip st3 = s t l . g e t O ; stl.f(); } } /* Ispis: Podtip * ///:V odite ra u n a o neem u van o m : n o v a klasa Podtip uzim a arg u m e n te i p o v ratn e vredn o sti tip a Podtip, a n e sam o tip a o sn o v n e klase OsnovnoSkladiste. To je su tin a CRG-a: Osnovna klasa za svojeparam etre upotrebljava izvedenu klasu. To znai da generika osnovna klasa p o staje neka vrsta ab lo n a za zajedniku fu n k c io n a ln o st svili svojih izvedenih

Poglavlje 15: Generiki tipovi

557

klasa, ali se za sve a rg u m e n te i p o v ra tn e v red n o sti te fu n k cio n aln o sti, u p o trebljava izvedeni tip. D ru g im reim a, u rezultujuoj klasi se u p o treb ljav a taan, a n e o sn o v n i tip. Z ato su u klasi Podtip i a rg u m e n t m e to d e s e t( ) i p o v ra tn i tip m eto d e g e t( ) ta n o tipa Podtip.

Samoogranienje
OsnovnoSkladiste m o e
d a upotrebi bilo koji tip k a o svoj generiki parametar, k a o ovde: //: generi cki/Neograni cena.java class Drugo {} class DrugoOsnovno extends OsnovnoSkladiste<Drugo> {} public class Neogranicena { public static void main(String[] args) { DrugoOsnovno b = new DrugoOsnovno(), b2 = new DrugoOsnovno(); b.set(new D r u g o O ) ; Drugo drugo = b .g e t();
b.f();

}
} /* Ispis: Drugo

* ///:S am o o g ran ie n je o b u h v ata i d o d a tn i k o rak kojim se namee da generiki tip b u d e u p o tre b lje n kao sop stven i a rg u m e n t o g ran ien ja. Pogledajte kako se rezu ltu ju a klasa m o e i kako se ne m o e u p o treb ljav ati:
//: genericki/SamoOgranicavajuci.java class SamoOgraniceni<T extends SamoOgraniceni<T { T element; SamoOgraniceni<T> set(T arg) { element = arg; return this;

}
T g e t () { return element; }

}
class A extends SamoOgraniceni<A> {} class B extends SamoOgraniceni<A> {} // Takoe OK class C extends SamoOgraniceni<C> { C setIGet(C arg) { set(arg); return get(); }

}
class D {} // Ovo ne moete: // class E extends SamoOgraniceni<D> {}
// Greka u vreme prevoenja: parametar tipa D nije unutar svojih granica

558

Misliti na Javi

// Ovo naalost moete da uradite, to znai da // ovaj idiom ne moete da nametnete: class F extends SamoOgraniceni {} public class SamoOgranicavajuci { public static void main(String[] args) { A a = new A(); a.set(new A()); a = a.set(new A()).get(); a = a.get(); C c = new C(); c = c.setIGet(new C());

} ) ///= S am o ogran ienje zahteva d a se klasa u p o tre b i u o v akvom o d n o su nasleivanja:


class A extends SamoOgraniceni<A> {}

T im e vas prisiljava da o sn o v n o j klasi kao p a ra m e ta r p ro sled ite klasu k o ju definiete. ta d o b ijate o d sam o o g ra n i en ja p ara m e tra ? P ara m e ta r tip a m o ra b iti isti kao klasa koja se definie. Kao to v id ite u definiciji klase B, p o d tip m o ete izvesti i o d tip a SamoOgraniceni iji je p a ra m e ta r d ru g i SamoOgraniceni, m a d a izgleda da je najea o n a u p o treb a koju vid ite za klasu A. P okuaj d efin isan ja klase E p o k azu je da ne m oete up o treb iti p a ra m e ta r tip a koji nije SamoOgraniceni. N aalost, F e se prevesti b ez u p o z o re n ja , p a se id io m sam o o g ra n i e n ja ne m oe nam e tn u ti. A ko je to zaista vano, m o ete u p o tre b iti sp o ljn u alatk u koja e o b ezb e d iti da sirovi tip ovi ne b u d u u p o tre b ljen i u m esto p a ra m e triz o v a n ih . Im ajte u v id u da o g ran ien je m o ete u k lo n iti i sve e se klase prevesti, ali isto vai i za klasu E:
/ / : generi cki/NijeSamoOgranicena.java public class NijeSamoOgranicena<T> { T element; NijeSamoOgranicena<T> set(T arg) { element = arg; return this;

}
T get() { return element; }

}
class A2 extends NijeSamo0granicena<A2> {} class B2 extends NijeSamo0granicena<A2> {} class C2 extends NijeSamo0granicena<C2> { C2 setIGet(C2 arg) { set(arg); return get(); }

Poglavlje 15: Generiki tipovi

559

class D2 {} // Ovo je sada ispravno: class E2 extends NijeSamo0granicena<D2> {} ///:-

D akle, o g ran ien je sa m o o g ra n i en jem slui sa m o da n a m e tn e o d n o s nasleivanja. A ko ste u p o treb ili sam o o g ra n i e n je, zn ate da e p a ra m e ta r tip a u p o treb ljen u klasi biti isti o sn o v n i tip k ao klasa koja taj p a ra m e ta r koristi. O n o p rim o ra v a sve koji u p o treb ljavaju tu klasu da slede taj oblik. S am o o g ran ien je se m oe u p o tre b iti i za generike m eto de:
//: genericki/SamoOgranicavajuceMetode.java public class SamoOgranicavajuceMetode { static <T extends SamoOgraniceni<T T f(T arg) { return arg.set(arg).get();

}
public static void main(String[] args) { A a = f (new A());

} } / / / =T im e spreavate da m e to d a b u d e p rim e n je n a n a ita d ru g o d o na sam o o g ran ien i arg u m e n t p rik a z a n o g oblika.

Kovarijansa argumenata
V red n o st sa m o o g ran i av aju ih tip o v a jeste to to proizv od e kovarijantne tipove argum enata - t i p o v i a rg u m e n a ta m e to d a v a rira ju k ao n jihove potklase. lako sa m o o g ran iav aju i tip o v i p ro izv o e i p o v ra tn e tipove koji su isti kao tip p o tk lase, to nije toliko van o, p o to je Java SE5 uvela kovarijantne povratne tipove.
//: genericki/KovarijantniPovratniTipovi.java class Osnovna {} class Izvedena extends Osnovna {} interface ObicnaDajOb { Osnovna dajob();

}
interface IzvedenaDajOb extends ObicnaDajOb { // Povratni tip redefinisane metode sme da se menja: Izvedena dajob();

}
public class KovarijantniPovratniTipovi { void test(IzvedenaDajOb d) { Izvedena d2 = d.dajob();

560

Misliti na Javi

M eto d a d a jo b ( ) interfejsa IzvedenaDajOb redefinie m e to d u d a jo b ( ) interfejsa ObicnaDajOb i vraa tip izveden iz tip a koji vraa O bicnaD ajO b.dajob( ). lak o je to sasvim logino - treb alo bi d a m e to d a izvedenog tip a m o e da v ra a specifiniji tip o d m eto d e o sno v nog tip a koju redefinie - n ije bilo dozv o ljen o u p re th o d n im v erzijam a Jave. S am o o g ran ien a m eto d a zaista daje ta a n izvedeni tip k ao p o v ra tn u v re d n o st, to vid ite ovde, u m eto d i d a jo b ( ):
//: genericki/GenerickeMetodelPovratniTipovi.java interface GenerickaDajOb<T extends GenerickaDajOb<T { T dajob();

}
interface DajOb extends GenerickaDajOb<DajOb> {} public class GenerickeMetodelPovratniTipovi { void test(DajOb g) { DajOb rezultat = g.dajob(); GenerickaDajOb gg = g.dajob(); // Takoe osnovni tip

} } ///:O b ra tite p a n ju na to kako se ovaj k o d n e b i preveo d a u Javu SE5 n isu bili uved en i ko v a rija n tn i p o v ra tn i tipovi. M e u tim , u n eg en erik o m k o d u , tip o v e argum enata ne m o ete n a te ra ti da se m en jaju s p o d tip o v im a:
//: genericki/ObicniArgumenti.java class ObicnaZadajOb { void set(Osnovna osnovna) { System.out.println("ObicnaZadajOb.zadajOb(Osnovna)");

} }
class IzvedenaZadajOb extends ObicnaZadajOb { void zadajOb(Izvedena izvedena) { System.out.println("IzvedenaZadajOb.zadajOb(Izvedena)");

} }
public class ObicniArgumenti { public static void main(String[] args) { Osnovna osnovna = new Osnovna(); Izvedena izvedena = new IzvedenaO; IzvedenaZadajOb ds = new IzvedenaZadajOb(); ds.zadajOb(izvedena); ds.zadajOb(osnovna); // Prevee se: preklopljena je, // nije redefinisana!

Poglavlje 15: Generiki tipovi

561

}
} /* Ispis: IzvedenaZadajOb.zadajOb(Izvedena) ObicnaZadajOb.zadajOb(Osnovna)

* ///:D ozvoljene su i zadajOb(izvedena) i zadajOb(osnovna), p a IzvedenaZadajO b.zadajO b( ) ne redefinie m e to d u O bicnaZadajO b.zadajO b( ), n ego je preklapa. Iz rezu lta ta v id ite da u klasi IzvedenaZadajOb p o sto je dve m e to d e , p a je verzija o sn o v n e klase i dalje d o stu p n a , to dok azu je d a je bila p reklo p ljen a. M e u tim , uz sam oo g ran iav aju e tipove p o sto ji sam o je d n a m e to d a u izvedenoj klasi, i o n a kao svoj a rg u m e n t u zim a izvedeni, a n e o sn o v n i tip:
//: genericki/SamoOgranicavanjelKovarijantniArgumenti.java interface SamoOgranicenaZadajOb<T extends SamoOgranicenaZadajOb<T { void zadajOb(T arg);

}
interface ZadajOb extends SamoOgranicenaZadajOb<ZadajOb> {} pubiic class SamoOgranicavanjelKovarijantniArgumenti { void testA(ZadajOb sl, ZadajOb s2, SamoOgranicenaZadajOb soz) { sl.zadaj0b(s2); // sl.zadajOb(soz); // Pogreno: // zadajOb(ZadajOb) u SamoOgranicenaZadajOb<ZadajOb> // ne moe biti primenjena na (SamoOgranicenaZadajOb)

} III-P revodilac ne p rizn aje p okuaj da se kao a rg u m e n t m e to d e zad ajO b ( ) p ro sled i o snovni tip, p o to n e postoji m e to d a s takvim p o tp iso m . U stvari, a rg u m e n t je red efin isan . Ako n em a sam o ogran iav an ja, uskae u o b iajen i m e h a n iz a m nasled iv an ja i d o b ijate prek lapan je, kao u n eg en erik o m sluaju:
/ / : genericki/ObicnoGenerickoNasledjivanje.java class GenerickaZadajOb<T> { // Nije samoograniena void zadajOb(T arg){ System.out.printl n("Generi ckaZadajOb.zadajOb(Osnovna)");

} }
class IzvedenaZO extends GenerickaZadajOb<Osnovna> { void zadajOb(Izvedena izvedena){ System.out.println("IzvedenaZO.zadajOb(Izvedena)");

562

Misliti na Javi

public class ObicnoGenerickoNasledjivanje { public static void main(String[] args) { Osnovna osnovna = new Osnovna(); Izvedena izvedena = new IzvedenaO; IzvedenaZO dgs = new IzvedenaZO(); dgs.zadajOb(izvedena); dgs.zadajOb(osnovna); // Prevee se: preklopljena je, // nije redefinisana!

}
} /* Ispis: IzvedenaZO.zadajOb(Izvedena) GenerickaZadajOb.zadajOb(Osnovna)

* ///:O vaj k o d po d raav a p ro g ra m ObicniArgumenti.java; u to m p rim e ru , IzvedenaZadajOb nasleduje klasu ObicnaZadajOb koja sadri svoju m e to d u zadajOb(Osnovna). O vde, IzvedenaZO nasleduje k lasu GenerickaZadajOb<Osnovna> koja tak o e sadri svoju m e to d u zadajOb(Osnovna) a n ju je n ap rav ila sp o m e n u ta generika klasa. Kao u p ro g ra m u ObicniArgumenti.java, iz rezu ltata v id ite da IzvedenaZO sadri dve p reklo p ljen e verzije m eto d e zad ajO b ( ). Bez sam o o g ran iav an ja, tipove a rg u m e n a ta p reklapate. A ko upotre'oite sam o o g ran iav an je, d o b ija te sam o je n u verziju m eto d e koja p rim a a rg u m e n t tan o g (o d re en o g ) tipa.

Veba 34: (4) N apravite sam o o g ra n i en gen erik i tip koji sadri a p stra k tn u m e to d u koja p rim a a rg u m e n t tip a generikog p a ra m e tra i p ro izv o d i p o v ra tn u v red n o st istog tip a generik o g p a ra m e tra . U n e a p stra k tn o j m eto d i klase pozov ite a p stra k tn u m e to d u i v ratite njen rezultat. N asledite sam o o g ran i en i tip i testirajte rezu ltu ju u klasu.

Dinamika bezbednost tipova


Poto generike kontejn ere m oete prosleivati k o d u n ap isan o m pre pojave Jave SE5, po stoji m o g u n o st da stari k o d p okvari vae kontejn ere. Java SE5 u pak etu java.util.ColIections im a skup uslun ih m eto d a za reavanje pro b lem a s pro verom tipova u toj situaciji: statin e m eto de checkedCollection( ), checkedList( ), checkedM ap( ), checkedSet( ), checkedSortedM ap( ) i checkedSortedSet( ). Svaka o d njih p rim a k o n tejn er koji hoete da d in am ik i ispitate kao svoj p rv i a rg u m e n t i tip koji hoete da n am e tn e te kao svoj d ru g i arg u m en t. Proveravani k o n te jn e r e gen erisati izuzetak ClassCastException na m estu gde p o ku ate d a um etnete neo d g o v araju i o b jek at, za razliku o d p re-generik og (sirovog) konte jn e ra koji bi vas obavestio da po stoji p ro b lem p rilik o m vaenja objekta. U to m sluaju zn ate da p o sto ji p ro b lem , ali ne znate ko ga je pro uzrok ov ao . P roveravani k o n tejn e ri o m o g u u ju da saznate ko je p o k u ao d a u m e tn e n eo d go varajui objekat. R azm o triem o p ro b lem stavljanja m ake u listu pasa koji realizuje prov eravan i ko ntejner. O vde zastareli ko d p redstavlja m etodaU Starom Stilu( ) zato to uzim a sirovu Listu, a an o tac ija @SuppressWarnings("unchecked") p o tre b n a je da bi se spreilo rezu ltu ju e upozorenje:

Poglavlje 15: Generiki tipovi

563

//: genericki/ProveravanaLista.java // Koristimo metodu Collection.checkedList(). import podaciotipu.ljubimci.*; import java.util.*; public class ProveravanaLista { @SuppressWarnings("unchecked") static void metodallStaromStilu(List verovatnoPsi) { verovatnoPsi .add(new MackaO);

}
public static void main(String[] args) { List<Pas> psil = new ArrayList<Pas>(); metodaUStaromStilu(psil); // utke prihvata Macka List<Pas> psi2 = Collections.checkedList( new ArrayList<Pas>(), Pas.class); try { metodaUStaromStilu(psi2); // Generie izuzetak } catch(Exception e) { System.out.println(e);

}
// Izvedeni tipovi dobro rade: List<Ljubimac> ljubimci = Collections.checkedList( new ArrayList<Ljubimac>(), Ljubimac.class); 1jubimci.add(new Pas()); 1jubimci ,add(new MackaO);

}
} /* Ispis: java.lang.ClassCastException: Attempt to insert class podaciotipu.ljubimci.Macka element into collection with element type class podaciotipu.ljubimci.Pas (java.1ang.C1assCastException: Pokuaj umetanja klase podaciotipu.ljubimci.Macka u kontejner iji tip odreuje klasa podaciotipu.ljubimci.Pas)

* ///:Kada p o k ren ete p ro g ra m , videete da lista p s i l utke p o d n o si u m eta n je o b je k ta tipa M acka, d o k lista p si2 o d m a h generie izuzetak kada se u m e tn e n eo d g o v araju i tip . V idite i to da se u k o n te jn e r koji proverava na o sn o v n i tip m o gu stavljati o b jek ti izv ed en o g tipa. V eba 35: (1) P repravite p ro g ra m P ro v e rav a n aL ista.jav a tako d a u p o treb ljav a klase K afa d efin isan e u ovom poglavlju.

Izuzeci
U gen erik o m kod u se, zbog brisan ja, izuzeci veom a m alo u p o treb ljav aju . B lok c a tc h ne m oe da hvata izuzetke generikog tipa, zato to taa n tip izuzetka m o ra b iti p o z n a t i u v rem e p rev o en ja i u v rem e izvravanja. Takoe, generika klasa ne m o e ni p o sre d n o n i n ep o sre d n o da nasledi klasu T h ro w a b le (i tim e o p e t spreava d a definiete generiki izuzetak koji se ne m oe uh vatiti).

564

Misliti na Javi

M e u tim , u b lo k u throvvs u n u ta r d ek laracije m e to d e m o g u se u p o treb ljav ati p a ra m e tr i tipa. To o m o g u u je d a n ap iite g en eriki k o d koji se m e n ja u zavisnosti o d tip a p ro v eravanog izuzetka:
/ / : genericki/GenerisanjeGenerickoglzuzetka.java import java.util.*; interface Preradjivac<T,E extends Exception> { void obrada(List<T> kontejnerRezultata) throws E;

}
class PokretacObrada<T,E extends Exception> extends ArrayList<Preradjivac<T,E { List<T> obradiSve() throws E { List<T> kontejnerRezultata = new ArrayList<T>(); for(Preradjivac<T,E> preradjivac : this) preradjivac.obrada(kontejnerRezultata); return kontejnerRezultata;

class Greskal extends Exception {} class Preradjivacl implements Preradjivac<String,Greskal> { static int broj = 3; public void obrada(List<String> kontejnerRezultata) throws Greskal { if(broj-- > 1) kontejnerRezultata.add("Hep!"); else kontejnerRezultata.add("Ho!"); if(broj < 0) throw new Greskal();

} }
class Greska2 extends Exception {} class Preradjivac2 implements Preradjivac<Integer,Greska2> { static int broj = 2; public void obrada(List<Integer> kontejnerRezultata) throws Greska2 { if(broj-- == 0) kontejnerRezultata.add(47); el se { kontejnerRezultata.add(ll);

}
if (broj < 0) throw new Greska2();

Poglavlje 15: Generiki tipovi

565

public class GenerisanjeGenerickoglzuzetka { public static void main(String[] args) { PokretacObrada<String,Greskal> pokretac = new PokretacObrada<String,Greskal>(); for(int i = 0 ; i < 3 ; i++) pokretac.add(new P r e radjivaclO); try { System.out.println(pokretac.obradiSve()); } catch(Greskal e) { System.out.println(e);

}
Pokretac0brada<Integer,Greska2> pokretac2 = new Pokretac0brada<Integer,Greska2>(); for(int i = 0 ; i < 3 ; i++) pokretac2.add(new Preradjivac2()); try { System.out.println(pokretac2.obradiSve()); } catch(Greska2 e) { System.out.println(e);

} } } ///:-

Preradjivac poziva m e to d u o b ra d a ( ) i m o e generisati izuzetak tip a E. R ezultat m eto d e o b ra d a ( ) sm eta se u List<T> kontejnerRezultata (to nazivam o param etar kolekcije). Klasa PokretacO brada im a m e to d u obradiSve( ) koja izvrava sve o bjek te tipa O brada koje klasa sadri i vraa kontejnerRezultata. Da ne m o ete p a ra m e triz o v a ti g en erisan e izuzetke, ne b iste m ogli napisati ovaj k o d generiki, i to zbog prov erav an ih izuzetaka. Veba 36: (2) D o d ajte d ru g i p a ra m e triz o v a n i izuzetak kJasi Preradjivac i pok aite da izuzeci m o g u da se m en jaju nezavisno.

Miksini
T erm in miksin (engl. m ixin) s v re m e n o m je d o b io razn a znaenja, ali o sn o v n o je: m eanje m o g u n o sti vie klasa da bi se d o b ila rezu ltu ju a klasa koja prestavlja sve um ean e tip o ve, tj. o n a se naziva m eavina ili m iksin. To se o b in o radi u zadnji as, to je ini p rig o d n o m alatk o m za lako sastavljanje klasa. Jedna o d p re d n o sti m ik sin a jeste to to o n i k arak teristik e i p o n aan ja d o sled n o p rim e n ju ju na razn e klase. Uz to, k ad a neto p ro m e n ite u m iksin klasi, te p ro m e n e se prenose na sve klase na koje se m ik sin p rim e n i. Z ato su m iksini zn aajan d eo aspektno orijentisanogprogram iranja (A O P), a za reavanje p ro b le m a s m eav in am a esto se p red lau razni aspekti.

566

Misliti na Javi

Miksini ujeziku C+ +
Jedan o d n ajjaih razloga za u v o e n je v iestru k o g n asle iv an ja u C + + -U jeste njegova u p o tre b a za m iksine. M e u tim , zanim ljiviji i eleg a n tn iji p ris tu p m ik sin im a k o risti p aram etrizo v an e tipove, gde je m ik sin klasa koja n asle u je svoj p a ra m e ta r tip a. U C ++-U se m ik sin i lako prave je r C + + p a m ti tip p a ra m e ta ra svojih ablona. Evo p rim e ra s dva tip a m ik sin a iz jezika C + + : je d a n slui za d o d av a n je svojstva posedov an ja v rem enske oznake (engl. tim e stam p ) a d ru g i d o d a je serijski bro j svake in stan ce objekta:
//: genericki/Miksini.cpp #include <string> #include <ctime> #include <iostream> using namespace std; template<class T> class SVremenskomOznakom : public T { long vremenskaOznaka; public: SVremenskomOznakom() { vremenskaOznaka = time(O); } long dajOznaku() { return vremenskaOznaka; }

};
template<class T> class SaSerijskimBrojem : public T { long serijskiBroj; static long brojac; public: SaSerijskimBrojem() { serijskiBroj = brojac++; } long dajSeri jskiBroj () { return seri jskiBroj ; }

};
// Definicija i inicijalizacija statine memorije: template<class T> long SaSerijskimBrojem<T>::brojac = 1; class Osnovni { string vrednost; publi c: void postavi(string vre) { vrednost = vre; } string daj() { return vrednost; }

t;
int main() { SVremenskomOznakom<SaSerijskimBrojem<Osnovni> > miksinl, miksin2; miksinl.postavi("ispitni znakovni niz 1"); miksin2.postavi("ispitni znakovni niz 2"); cout miksinl.daj() " " miksinl.dajOznaku() " " miksinl.dajSerijskiBrojO endl; cout miksin2.daj() " " miksin2.daj0znaku() " " miksin2.dajSerijskiBroj() endl;

Poglavlje 15: Generiki tipovi

567

} /* Ispis: (primer) ispitni znakovni niz 1 1129840250 1 ispitni znakovni niz 2 1129840250 2

* ///:U m eto d i m a in ( ), rez u ltu ju i tip o b jekata m iksinl i m iksin2 im a sve m eto d e u m ean ih tipova. S m atrajte m ik sin fu n k c ijo m koja preslikava p o sto jee klase na nove potklase. O b ra tite p an ju na to kako se p o m o u ove teh n ik e lako prave m iksini; u sutini, vi sam o kaete: Evo ta h o u , i to se izvri:
SVremenskomOznakom<SaSerijskimBrojem<0snovni> > miksinl, miksin2;

N aalost, Javin generiki m e h a n iz a m to ne dozvoljava. B risanje u k lan ja tip osno v n e klase, p a generika klasa ne m o e d ire k tn o d a nasled i generiki param etar.

Meanje pomou interfejsa


esto se pred lae d a se efekat m ik sin a p o stig n e p o m o u interfejsa, i to n a ovaj nain:
//: genericki/Miksini.java import java.util.*; interface SVremenskomOznakom { long daj0znaku(); } class RzcSVremenskomOznakom implements SVremenskomOznakom { private final long vremenskaOznaka; public RzcSVremenskomOznakom() { vremenskaOznaka = new Date(),getTime();

}
public long dajOznaku() { return vremenskaOznaka; }

interface SaSerijskimBrojem { long dajSerijskiBroj(); } class RzcSaSerijskimBrojem implements SaSerijskimBrojem { private static long brojac = 1; private final long serijskiBroj = brojac++; public long dajSerijskiBroj() { return serijskiBroj; }

}
interface Osnovni { public void postavi(String vre); public String d aj();

}
class RzcOsnovnog implements Osnovni { private String vrednost; public void postavi(String vre) { vrednost = vre; } public String daj() { return vrednost; }

568

Misliti na Javi

class Miksin extends RzcOsnovnog implements SVremenskomOznakom, SaSerijskimBrojem { private SVremenskomOznakom vremenskaOznaka = new RzcSVremenskomOznakom(); private SaSerijskimBrojem serijskiBroj = new RzcSaSerijskimBrojemO; public long dajOznaku() { return vremenskaOznaka.dajOznaku(); ) public long dajSeri jskiBroj () { return serijskiBroj.dajSerijskiBroj();

} }
public class Miksini { public static void main(String[] args) { Miksin miksinl = new Miksin(), miksin2 = new Miksin(); miksinl.postavi("ispitni znakovni niz 1"); miksin2.postavi("ispitni znakovni niz 2"); System.out.println(miksinl.daj() + " " + miksinl.dajOznakuO + " + miksinl.dajSeri jskiBroj ()); System.out.println(miksin2.daj() + " " + miksin2.daj0znaku() + 1 1 " + miksin2.dajSerijskiBroj());

}
} /* Ispis: (primer) ispitni znakovni niz 1 1132437151359 1 ispitni znakovni niz 2 1132437151359 2

* ///:Klasa M iksin u osnovi k o risti delegiratije, pa je za svaki u m e a n i tip n e o p h o d n o polje u o b je k tu tip a Miksin. D a b iste o d g o v araju em o b je k tu pro sled ili pozive, u klasi Miksin m o ra te d a napiete sve p o tre b n e m eto d e. U p rim e ru su u p o tre b lje n e triv ijaln e Idase, ali sa slo enijim m ik sin o m , kod veom a brzo raste.4 V eba 37: (2) U p ro g ra m M ik sin i.jav a d o d a jte n ov u m iksin klasu O b o je n , u b acite je u M ik sin i pok aite da radi.

Korienje obrasca Decorator


Kada p o gledate nain na koji se koristi, k on cep t m iksina izgleda tesno povezan sa projektnim obrascem Decorator (D e k o ra to r)/ Kada uob iajeno pravljenje potklasa daje toliko klasa da postizanje svih m oguih k om binacija postaje n ep rak tin o , esto se koriste dekoratori. O b ra z a c Decorator opisu je korienje slojevitih o b jek ata za d in a m i k o i nevidljivo d o dav an je d o g o v o rn o sti o b jek tim a p o jed in an o . Decorator specificira d a svi o b jek ti koji se o m o tav a ju oko vaeg p o e tn o g o b jek ta im aju isti o sn o v n i interfejs. O b je k at m oe im ati

4 5

Im a jte u v id u d a n e k a ra z v o jn a o k ru e n ja , k a o to su E clip se i IntelliJ Id ea, a u to m a ts k i g e n e riu k o d za d e le g ira n je . U z o rc i su te m a k n jig e Thinking in Patterns (with Java) k o ju m o e te n a i na a d re si www.MintiView.net. P o g le d a jte ik n jig u Design Patterns', a u to r i su E ric h G a m m a i d r. (A d d iso n -W e sle y , 1995).

Pogiavlje 15: Generiki tipovi

569

svojstvo m oe se d ek o risa ti ( dekorabilnost ), a slojeve fu n k c io n a ln o sti d o d ajete o m o ta van jem d ru g ih klasa oko d ek o rab iln o g objekta. Tako k o rien je d e k o ra to ra p o sta je nev id ljivo - p o sto ji sk u p zajed n ik ih p o ru k a koje m o ete slati o b jek tu , b ez o b z ira n a to d a li je o n bio d e k o risan ili nije. Klasa za d ek o risan je m o e d a d o d a je i m eto d e , ali kao to ete videti, u o g ra n i e n o m ob im u . D e k o rato ri se realizu ju p o m o u k o m p o zicije i fo rm a ln ih s tru k tu ra (h ije ra rh ije dekora b ila n /d e k o ra to r), d o k se m ik sin i realizu ju n asle iv an jem . Stoga m ik sin e, zasn o v an e n a p a ra m e triz o v a n im tip o v im a, m o ete s m a tra ti g en erik im m e h a n iz m o m d e k o ra to ra koji ne zahtevaju s tru k tu r u nasleivanja p ro je k tn o g o b ra sc a Decorator. N apisaem o p re th o d n i p rim e r p o m o u p ro je k tn o g o b ra sc a Decorator.
//: genericki/dekorator/Dekorisanje.java package genericki.dekorator; import java.util.*; class Osnovni { private String vrednost; public void set(String vre) { vrednost = vre; } public String daj() { return vrednost; }

}
class Dekorator extends Osnovni { protected Osnovni osnovni; public Dekorator(Osnovni osnovni) { this.osnovni = osnovni; } public void set(String vre) { osnovni.set(vre); } public String daj() { return osnovni.daj (); }

}
class SVremenskomOznakom extends Dekorator { private final long vremenskaOznaka; public SVremenskomOznakom(Osnovni osnovni) { super(osnovni); vremenskaOznaka = new Date(),getTime();

}
public long dajOznaku() { return vremenskaOznaka; }

}
class SaSerijskimBrojem extends Dekorator { private static long brojac = 1; private final long serijskiBroj = brojac++; public SaSerijskimBrojem(Osnovni osnovni) { super(osnovni); } public long dajSeri jskiBroj () { return seri jskiBroj; }

}
public class Dekorisanje { public static void main(String[] args) { SVremenskomOznakom t = new SVremenskomOznakom(new OsnovniO); SVremenskomOznakom t2 = new SVremenskomOznakom(

570

Misliti na Javi

new SaSerijskimBrojem(new Osnovni())); //! tZ.dajSerijskiBroj(); // Nije dostupno SaSerijskimBrojem s = new SaSerijskimBrojem(new OsnovniO); SaSerijskimBrojem s2 = new SaSerijskimBrojem( new SVremenskomOznakom(new 0snovni())); //! s2.daj0znaku(); // Nije dostupno

1 ///=Klasa koja se d o b ija k o rien jem m ik sin a sadri sve p o tre b n e m eto d e, ali je tip objekta d o b ijen o g p o m o u d e k o ra to ra je d n a k p o sled n jem tip u ko jim je objek at b io dek o risan . D ru g im reim a, iako je mogue d o d ati vie slojeva, stv arn i tip zadaje sam o p o sled n ji sloj i vidljive su sa m o njegove m e to d e, d o k je tip m ik sin a je d n a k svim u m e a n im tip o v im a. Z ato je znaajan n e d o sta ta k D e co rato ra to to o n efektivno rad i sam o s je d n im (posledn jim ) slojem deko racije, a i k o rien je m ik sin a je v ero v atn o p riro d n ije . D akle, D eco rato r te k o g ra n i en o reava p ro b le m koji p o tp u n o reavaju m iksini. V eba 38: (4) N ap rav ite jed n o sta v an sistem D eco rato ra koji p o in je o d o b i n e kafe, na ko ju m o ete p rim e n iti d e k o ra to re m leka, p en e, okolade, k aram ela i laga.

Miksini s dinamikim posrednicima


P o m o u d in am i k o g p o sred n ik a m o e se n ap ra v iti m eh a n iz a m koji boije m o d elu je m iksine o d D e c o ra to ra (o b jan jen je n a in a ra d a Javinih d in a m i k ih p o sred n ik a p ro itajte u poglavlju Podaci o tipu). Uz d in am i k i p o sred n ik , dinatniki tip rezu ltu ju e klase jed n ak je k o m b in ac iji u m e a n ih tipova. Z bog o g ran ien ja k o jim a p o d leu d in a m ik i p o sred n ic i, svaka u m ean a klasa m o ra b iti realizacija n ek o g interfejsa:
//: genericki/MiksinDi nami ckimPosredni kom.java import java.lang.reflect.*; import java.uti1 import net.mindview.util.*; import static net.mindview.uti1 .Entorka.*; class PosrednikZaMiksin implements InvocationHandler ( Map<String,0bject> delegiranoMetodom; public PosrednikZaMiksin(Dvojka<Object,Class<?... parovi) { delegiranoMetodom = new HashMap<String,Object>(); for(Dvojka<Object,Class<? par : parovi) { for(Method metoda : par.drugi.getMethods()) { String imeMetode = metoda.getName(); // Metodu realizuje prvi interfejs u mapi if (!delegi ranoMetodom.contai nsKey(imeMetode)) delegiranoMetodom.put(imeMetode, par.prvi);

} } }

Poglavjje 15: Generiki tipovi

571

public Object invoke(Object posrednik, Method metoda, Object[] args) throws Throwable { String imeMetode = metoda.getName(); Object delegiraj = delegiranoMetodom.daj(imeMetode); return metoda.invoke(delegiraj, argumenti);

}
@SuppressWarnings("unchecked") public static Object newInstance(Dvojka... parovi) { Class[] interfejsi = new Class[parovi.length]; for(int i = 0; i < parovi.1ength; i++) { interfejsi [i] = (C1 ass)parovi [i] .drugi;

}
ClassLoader cl = parovi[0].prvi.getClass().getClassLoader(); return Proxy.newProxyInstance( cl , interfejsi, new PosrednikZaMiksin(parovi));

} }
public class MiksinDinamickimPosrednikom { public static void main(String[] args) { Object miksin = PosrednikZaMiksin.newInstance( n_torka(new RzcOsnovnogO , Osnovni .class), n_torka(new RzcSVremenskomOznakom(), SVremenskomOznakom.class), n_torka(new RzcSaSerijskimBrojem(),SaSerijskimBrojem.class)); Osnovni b = (Osnovni)miksin; SVremenskomOznakom t = (SVremenskomOznakom)miksin; SaSerijskimBrojem s = (SaSerijskimBrojem)miksin; b.set("Zdravo"); System.out.println(b.daj()); System.out.pri ntln(t.dajOznaku()); System.out.println(s.dajSerijskiBroj());

}
} /* Ispis: (primer) Zdravo 1132519137015

1
* ///:Z a razlik u o d statin o g tip a, sam o d in am ik i tip sadri sve u m ean e tipove, p a ovo i dalje nije o n a k o lepo kao C + + -o v p ristu p , jer m o ra te da svedete n an ie n a o d g o v araju i tip pre n eg o to pozovete m eto d e za njega. M e u tim , z n a tn o je blie p rav o m m ik sin u . U p o d r k u m ik sin a u Javi u lo en o je p rilin o m n o g o rad a. E ksplicitno za tu sv rh u n ap rav ljen je i n ajm an je je d a n softverski d o d atak , jezik Jam.

Veba 39: (1) U p ro g ra m M iksinDinamickimPosrednikom.java d o d a jte n o v u m iksin klasu Obojen, um eajte je u m iksin i d okaite da radi.

572

Misliti na Javi

Latentni tipovi
N a p o e tk u ovog poglavlja p red stav ili sm o id eju p isan ja k o d a koji se m o e p rim e n iti u o p te n o u najveoj m o g u o j m eri. D a b ism o to p o stig li, m o ra m o u blaiti o g ran ien ja tip ov a s ko jim a na k o d rad i, a d a n e iz g u b im o p re d n o s ti stati n e p rovere (b ezb ed n o sti) tipova. Tada em o m o i d a p iem o k o d koji se bez iz m e n a m o e u p o treb ljav ati u vie situacija - tj. optiji kod. Izgleda d a Javin generiki k o d p rav i jo je d a n k o ra k u to m sm eru . Kada piete ili k o ristite generik i k o d koji sa m o uva o b jekte, o n ra d i sa svim tip o v im a (sem p ro stih , iako sm o videli da au to m a tsk o p ak o v an je to izglauje). Ili, d ru g im reim a, g eneriko skladite m oe da kae: M eni je svejedno koji si tip . K od k ojem nije vano s ko jim tip o v im a radi, zaista se m oe p rim e n iti sv u d a i stoga je p o tp u n o o p ti (g eneriki). Kao to ste tako e videli, p ro b le m n astaje k ad a h o e te d a o b ra u je te generike tipove (ukoliko se to n e m oe o baviti p o ziv an jem m eto d a klase O b je c t), je r b risan je zahteva da zadate granice generikih tip o v a koji se m o g u u p o tre b iti, d a b i bilo m o g u e b ezb ed n o p o zivati k o n k re tn e m eto d e za generike o b jek te u vaem k o d u . To je zn aajno ogranienje p o jm a optosti, p o to svoje generike tipove m o ra te o g ran iiti tako da n asle u ju o d re en e klase ili realizuju o d re en e interfejse. U n ek im sluajevim a, m o d a ete u m esto generikih klasa i interfejsa u p o tre b iti o b in e klase ili interfejse, p o to se o g ran ien i generiki tip ne m o ra razlikovati od specificiranja klase ili interfejsa. N eki p ro g ra m sk i jezici to reavaju tak o to k o riste latentne ili strukturirane tipove. M alo neobavezniji te rm in je duck typing , kao u izreci ,,lf it w alks like a d u c k a n d talks like a d u ck , you m ig h t as vvell tre a t it like a d u c k . (A ko h o d a k ao p a tk a i zvui kao patka, o n d a sm atra jte da je to p atk a.) D u ck ty p in g je p o sta o p rili n o p o p u la ra n te rm in , m o d a zato to za so b o m ne vue istorijski p rtljag kao p re th o d n a dva te rm in a. G eneriki k o d o b in o poziva tek n ekoliko m eto d a za o d re e n generiki tip, a jezik s late n tn im tip o v im a ublau je to o g ra n i e n je (i daje o ptiji k o d ) tako to zahteva realizovanje sam o nekog p o d sk u p a m e to d a , a ne o d re e n e klase ili interfejsa. L aten tn i tipovi o m o g u u ju rad s razn im h ije ra rh ija m a klasa je r se poziv aju m e to d e koje n isu deo zajedn ikog interfejsa. Stoga pare k o d a m o e zap ravo rei: ,A ko m oe g o v o r i ti ( ) i se d eti( ), m e n i je svejedno koji si tip . U koliko ne zahteva o d re e n tip , kod je optiji. L atentni tipov i su m e h a n iz a m za rasp o re iv an je i p o n o v n o korienje koda. V iekratn o u p o treb ljiv k o d je lake p isati s n jim a nego bez njih . R asporeivanje i p o n o v n o korienje koda jesu o sn o v n i p rin c ip i sveg p ro g ra m ira n ja : napii k o d je d a n p u t, koristi ga vie p u ta i uvaj n a je d n o m m estu . Poto n e m o ra m da n avedem o d re e n i interfejs koji m oj k o d o b ra u je , la te n tn i tip o v i m i o m o g u u ju d a piem m an je k oda i d a ga lake p rim en ju jem na vie m esta. O d jezika koji p o d rav aju la te n tn e tip o v e, dva su P y th o n (m o ete ga b esp latn o p re u zeti n a adresi w ww .Python.org) i C + + .6 P y th o n tipove proverava d in am ik i (gotovo sve p ro veravanje tipov a obavlja se u v rem e izvravanja), a C + + statiki (u v rem e prev o en ja). D akle, late n tn i tip ovi ne zah te v aju n i statiko ni d in am i k o p ro vcravanje tipova.
6 L atentne tipove podravaju i jezici Ruby i Sm alltalk.

Poglavlje 15: Generiki tipovi

573

A ko u z m e m o g o rn ji o p is i izrazim o ga n a P y th o n u , d o b i em o ovo:
#: genericki/PsiIRoboti.py class Pas: def govoriti (self): print Av!" def sedeti(self): print "Sedim" def reprodukovatise(self): pass class Robot: def govoriti(self): print "Klik!" def sedeti(self): print "Klank!" def promenaUlja(self): pass def obavi(bilosta): bi1osta.govori t i () bi1osta.sedeti() a = Pas() b = Robot() obavi(a) o bavi(b)
#:~

P v th o n o d re d u je ob last vaenja na o sn o v u u v u en o sti o d leve m arg in e (p a vitiaste zag rad e nisu p o tre b n e ), a d v o tak o m oznaava p o eta k nove oblasti vaenja. Z n ak o m # ozn aava se k o m e n ta r d o kraja reda, kao // u Javi. M eto d e klasa svojim p rv im a rg u m en to m ek sp licitn o zad aju ekvivalent reference this koji se p o konvenciji naziva self. Pozivi k o n stru k to ra ne zah tev aju bilo koju v rstu rezervisane rei new. U jeziku P y th o n m o g u se ko ristiti i o b in e funkcije (koje nisu lanice), kao to m o ete v id eti u funkciji ob av i( ). U obavi(bilosta), o b ra tite p an ju na to d a bilosta n em a tip i d a je sam o identifikator. Poto bilosta m o ra b iti u stan ju d a obavi op eracije koje o d njega zahteva funkcija o b a v i( ), im p lic ira n je neki interfejs. Ali taj interfejs n e m o ra te ek sp licitn o da nap iete o n je latentan. Funkciji o b a v i( ) nije v ano koji je tip n jen o g a rg u m e n ta , p a joj se m oe p ro sled iti svaki o b jek at koji p o d rav a m e to d e g o v oriti( ) i sed eti( ). Ako funkciji ob av i( ) p ro sled ite o bjek at koji ne p o d rav a te o p eracije, d o b iete izuzetak u v rem e izvravanja. Isti u in a k m o em o d a p o stig n e m o na C + + -u :
//: genericki/PsiIRoboti.cpp class Pas { publi c: void govoriti() {}

574

Misliti na Javi

void sedeti() {} void reprodukovatiseO

{}

};
class Robot { public: void govoriti() {} void sedeti () {} void promenalllja() {

};
template<c1ass T> void obavi(T bilosta) { bilosta.govoriti(); bilosta.sedeti();

}
int main() { Pas d; Robot r; obavi(d); obavi(r);

} III-I u P y th o n u i u C + + -u , P as i R o b o t n em aju n ita zajedniko, sem to im aju dve m eto d e sa id e n ti n im p o tp isim a . Sa sta n o v ita tipova, to su sasvim razliiti tipovi. M e u tim , funkciji o b a v i ( ) nije vano koji je tip n jen o g a rg u m en ta, a la ten tn o st tip o v a jo j o m o guuje da p rih v ati objekte o b a tip a. C + + p ro verava m o e li zaista d a poalje te p oruk e. A ko p o ku ate d a prosled ite p o grean tip , p rev o dilac e v am ispisati p o ru k u o greci (te p o ru k e o grekam a su o d uv ek bile uasne i o p irn e, i o sn o v n i su razlog to C + + -o v i ab lo ni im aju iou rep u taciju ). Iako to rad e u razliita v re m en a - C + + u v rem e p revo en ja, a P y th o n u vrem e izvravanja o b a jezika p roveravaju u p o tre b u tip o v a, pa ih sm a tra ju strogo tipiziratiim jezicim a.7 Late n tn i tip o v i ne u groavaju stro g u tip iziran o st. Poto su g eneriki tip o v i u Javu d o d a ti n ak n a d n o , nije bilo anse da se realizuje bilo koja v rsta la te n tn ih tipova, pa Java n e m a tu m o g u n o st. Z ato isprva izglea kao da je Javin generiki m eh a n iza m m an je generik i" o d jezika koji p o drav aju la te n tn e tip ov e.8 N a p rim er, ako g o rn ji p rim e r p o k u a m o da realizujem o u Javi, m o raem o da u p o tre b im o klasu ili interfejs koje e m o sp ecificirati u izrazu za granice:
//: genericki/Obavlja.java public interface Obavlja { void govorit i (); void sedeti();

} III-P o to je u n je m u o z v o lje n a e k sp lic itn a k o n v e rz ija tip o v a koia u s u tin i o n e m o g u u je siste m tip o v a , n e k i tv r e d a je C + + sla b o tip iz ira n jezik, ali su to e k s tre m n a sta n o v ita . V ero v a tn o je ta n ije rei da ie C + + stro g o tip iz ira n je z ik sa s k riv e n im v ratim a". la v in u re a liz ac iju g e n e ri k ih tip o v a b ris a n ie m , k a tk a d a n a ziv aiu tlni^ini.'tfthii ^ e n e ri k i tip o v i.

Poglavlje 15: Generiki tipovi

575

//: genericki/PsiIRoboti.java // U Javi nema latentnih tipova import podaciotipu.ljubimci.*; import static net.mindview.util.Print.*; class CirkuskiPas extends Pas implements Obavlja { public void govoriti() { print("Vau!"); } public void sedeti() { print("Sedim"); } public void reprodukovatise() {}

}
class Robot implements Obavlja { public void govoriti() { print ("Kl i k!"); } public void sedeti() { print("Klank!"); } public void promenalllja() {}

}
class Komuniciraj { public static <T extends Obavlja> void obavi(T izvodjac) { izvodjac.govoriti(); izvodjac.sedeti();

} }
public class PsiIRoboti { public static void main(String[] args) { CirkuskiPas d = new CirkuskiPas(); Robot r = new Robot(); Komuni ciraj.obavi(d); Komunici raj.obavi(r);

}
} /* Ispis: Vau! Sedim Klik! Klank!

* ///:M e u tim , im ajte u vid u da m e to d i o b a v i( ) g eneriki tipo vi nisu n e o p h o d n i. M o em o je d n o sta v n o specificirati da o n a prihvata objek at koji realizuje interfejs O bav lja:
//: genericki/ProstiPsiIRoboti.java // Uklanjamo generiki kod; prograin i dalje radi. class KomunicirajJednostavno { static void obavi(Obavlja izvodjac) { izvodjac.govorit i (); izvodjac.sedeti();

576

Misliti na Javi

) }
public class ProstiPsilRoboti { public static void main(String[] args) { KomunicirajJednostavno.obavi (new C i r k u s k i P a s O ) ; KomunicirajJednostavno.obavi(new Robot());

}
} /* Ispis: Vau! Sedim Klik! Klank!

* ///:U ovo m sluaju, generiki k o d nije b io n e o p h o d a n , p o to su klase io n ak o m o ra le da realizuju interfejs O bav lja.

Kompenzacija za nepostojanje latentnih tipova


Iako Java ne p od rav a la te n tn e tipove, ispostavlja se d a to n e zn ai d a se generik i k o d sa o g ran ien jim a n e m oe u p o tre b ljav ati u ra z n im h ije ra rh ija m a tip o v a. D ru g im reim a, ip ak je m ogue pisati pravi generiki k o d , ali je to n eto tee.

Refleksija
Jedan p ristu p je u p o treb a refleksije. Evo m e to d e o b a v i ( ) koja u p o treb ljav a laten tn e tipove:
//: genericki/LatentnaRefleksija.java // Pravljenje latentnih tipova pomou refleksije. import java.lang.reflect.*; import static net.mindview.util.Print.*; // Ne realizuje interfejs Obavlja: class Mimika { public void hodajNasuprotVetru() {} public void sedeti () { print("Pravim se da sedim'1); } public void gurajNevidljiveZidove() {} public String toStringO { return "Mimika"; }

}
// Ne realizuje interfejs Obavlja: class PametanPas { public void govoriti() { print("Vau!"); } public void sedeti() { print("Sedim"); } public void reprodukovatise() {}

Poglavlje 15. Generiki tipovi

577

class KomunicirajRefleksivno public static void obavi(Object zvucnik) { Class<?> zvck = zvucnik.getClass(); try { try { Method govoriti = zvck.getMethod("govoriti"); govoriti.invoke(zvucnik); } catch(NoSuchMethodException e) { print(zvucnik + " ne moe govoriti");

}
try { Method sedeti = zvck.getMethod(sedeti"); sedeti.invoke(zvucnik); } catch(NoSuchMethodException e) { print(zvucnik + 1 1 ne moe sedeti");

}
} catch(Exception e) { throw new RuntimeException(zvucnik.toString(), e ) ;

} } }
public class LatentnaRefleksija { public static void main(String[] args) { Komunici rajRef1eksivno.obavi(new PametanPas()); KomunicirajRefleksivno.obavi(new Robot()); KomunicirajRefleksivno.obavi (new M i m i k a O ) ;

}
} /* Ispis: Vau! Sedim Klik! Klank! Mimika ne moe govoriti Pravim se da sedim

* ///:O vde su klase sasvim razliite i n em aju zajed n ik ih o sn o v n ih klasa (sem klase O b je c t) niti interfejsa. P om o u refleksije, m e to d a K o m u n ic ira jR e fle k siv n o .o b a v i( ) m oe dinam iki da u tv rd i da li su eljene m eto d e d o stu p n e i d a ih pozove. M oe da se izbori ak i sa in jenicom da M im ik a im a sam o je d n u o d p o tre b n ih m e to d a i da svoj cilj ispunjava delim ino.

Primena metode na sekvencu


Refleksija p ru a zanim ljive m o g u n o sti, ali svu p ro v eru tipova odlae za tre n u ta k izvravanja i zato je nepoeljna u m n o g im situ acijam a. P rovera tip o v a u v rem e prevoenja o b in o je m n o g o poeljnija. Ali, da li je m o g u e im ati p ro v e ru tip o v a u v rem e prevoenja i laten tn e tipove?

578

Misliti na Javi

P ogledajm o p rim e r koji istrau je taj p ro b lem . P retp o sta v im o d a h o e te da n ap rav ite m e to d u p r i m e n i ( ) koja bilo koju m e to d u p rim e n ju je n a sve o b jek te u nekoj sekvenci. U ovoj situaciji interfejsi kao da ne o d g o v araju . H o ete d a p rim e n ite b ilo k o ju m e to d u na kolekciju objekata, a interfejsi n a m e u o g ran ien je koje ne dozvoljava d a o piete bilo koju m e to d u . Kako da to u rad ite u Javi? P ro b lem m o e m o n ajp re da reim o refleksijom , i to isp ad a p rili n o eleg a n tn o zbog arg u m e n a ta prom enljive d u in e koji se k o riste o d Jave SE5:
//: genericki/Primeni.java // (main: TestZaPrimeni} import java.lang.reflect.*; import java.util.*; import static net.mindview.util.Print.*; public class Primeni { public static <T, S extends Iterable<? extends T void primeni(S sekv, Method f, Object... argumenti) { try { for(T t: sekv) f.invoke(t, argumenti); } catch(Exception e) { // Greke su programerski propusti throw new RuntimeException(e);

} } }
class Oblik { public void rotirati() { print(this + " rotirati"); } public void promvelicine(int novaVelicina) { print(this + " promvelicine " + novaVelicina);

} }
class Kvadrat extends Oblik {} class PopunjenaLista<T> extends ArrayList<T> { public PopunjenaLista(C1ass<? extends T> tip, int velicina) { try { for(int i = 0; i < velicina; i++) // Pretpostavlja da postoji podrazumevani konstruktor: add(tip.newlnstance()); } catch(Exception e) { throw new RuntimeException(e);

} } }

Poglav[je 15: Generiki tipovi

579

class TestZaPrimeni { public static void main(String[] args) throws Exception { List<Oblik> oblici = new ArrayList<0blik>(); for(int i = 0; i < 10; i++) obl ici.add(new Oblik()) ; Primeni.primeni(oblici, Obl ik.class.getMethod("rotirati)); Primeni .primeni (obl ici, Oblik.class.getMethodC'promvelicine", int.class), 5); List<Kvadrat> kvadrati = new ArrayList<Kvadrat>(); for(int i = 0; i < 10; i++) kvadrati.add(new Kvadrat()); Primeni.primeni(kvadrati, Oblik.class.getMethod("rotirati)); Primeni.primeni(kvadrati, Oblik.class.getMethod("promvelicine", int.class), 5); Primeni.primeni(new PopunjenaLista<0blik>(0blik.class, 10), Oblik.class.getMethod("roti rati")); Primeni .primeni (new PopunjenaListaObl ik>(Kvadrat.class, 10), Obli k .class.getMethod(roti rati)); JednostavanRedZaCekanje<Obli k> obli kR = new JednostavanRedZaCekanje<Oblik>(); for(int i = 0; i < 5; i++) { obl ikR.add(new OblikO); oblikR.add(new Kvadrat());

}
Primeni.primeni(oblikR, Oblik.class.getMethod("rotirati"));

}
} /* (Pokrenite da biste videli rezultate) * / / / : -

U p ro g ra m u Prim eni im am o sree, zato to je u lavu u g ra e n interfejs Iterable koji se upo treb ljav a u Javinoj b iblioteci k o n tejn e ra. Z ato m e to d a p rim e n i( ) m oe d a p rih v ati sve to realizuje interfejs Iterable, a to su sve p o tld ase o d Collection kao to je List. Ali o n a m oe da p rih v ati i sve d ru g o u em u ste vi realizovali Iterable - na p rim er, ovde definisan u klasu JednostavanRedZaCekanje koja se g o re u p o treb ljav a u m eto d i m a in ( ):
//: genericki/JednostavanRedZaCekanje.java // Drugaija vrsta kontejnera koji je iterabilan import java.uti1 .*; public class JednostavanRedZaCekanje<T> implements Iterable<T> { private LinkedList<T> skladiste = new LinkedList<T>(); public void add(T t) { skladiste.offer(t); } public T get() { return skladiste.poll(); } public Iterator<T> iterator() { return skladiste.iterator();

} ///-

580

Misliti na Javi

U p ro g ra m u Primeni.java, izuzeci su p retv o ren i u izuzetke RuntimeException zato to n e m a pravog n a in a d a se o d n jih o p o ra v im o - u o vo m sluaju o n i zaista predstavljaju p ro g ram e rsk e p ro p u ste . V odite ra u n a o to m e d a sam m o ra o da stav im g ran ice i dokerske a rg u m e n te d a bi klase Prim eni i PopunjenaLista m o g le d a se k o riste u svim eljenim situacijam a. A ko ih izvadite, videete d a Prim eni i PopunjenaLista nee ra d iti u n ek im p rim e n am a . PopunjenaLista nas stavlja u p o m a lo n ezg o d an poloaj. D a bi se u njoj n eki tip m ogao u p o tre b iti, o n m o ra im a ti p o d ra z u m e v a n i k o n s tru k to r (bez arg u m e n ata ). Java to ne m o e d a n a m e tn e u v rem e p rev o en ja, p a se cela p ri a p reb acu je n a v rem e izvravanja. O b i n o se p red lae d a se u sp e n a p ro v era u v rem e p rev o en ja p o stig n e d efin isan jem p ro izv o d n o g interfejsa koji im a m e to d u za g en erisan ie objek ata; o n d a bi PopunjenaLista p rih v atala taj interfejs, a n e sirovog proizv o aa" leksem e tip a (engl. type token ). Ali o n d a b i sve klase u p o tre b lje n e u o b je k tu tip a PopunjenaLista m o rale realizovati taj p ro iz v od ni interfejs. N aalost, veina n a p isan ih ldasa ne zn a za va interfejs, te ga i n e realizuje. K asnije u v a m p o k az ati je d n o reen je ovog p ro b le m a p o m o u ad ap te ra. N o, m od a je p rik azan i p ristu p korienja leksem e tip a razb o rit k o m p ro m is (b arem kao p rv o reenje). Uz takav p ristu p , korienje neega kao to je objek at tip a PopunjenaLista dov o ljn o je lako da o n biva korien, a ne zaobien. N aravno, greke se p rijavljuju tek u v rem e izvravanja, p a se m o ra te n a d a ti da e o n e izro n iti ra n o u procesu razvoja koda. Z n ajte d a se teh n ik a leksem e tip a p re p o ru u je u lite ra tu ri o Javi, npr. u lan k u Generiki tipovi u program skom jeziku Java,9 gde auto r, G ilad B racha, kae: Taj id io m se m n o go k o risti u n o v im in terfe jsim a za p ro g ra m ira n je aplikacija, recim o za o b ra d u anotacija". M e u tim , n isu ba svi sreni zb o g toga; im a lju d i koji m n o g o rad ije koriste pro izv o d n i p ristu p , predstav ljen u p re th o d n o m d elu poglavlja. Sem toga, koliko god da je Javino reenje ispalo eleg an tn o , m o ra m o p rim e titi da zbog u p o tre b e refleksije o n o m oe b iti sp o rije (iako je to u n o v ijim verzijam a Jave z n a tn o p ob o lja n o ) o d realizacije bez refleksije, p o to se u v rem e izvravanja deava m n o g o toga. To ne bi treb alo da vas sprei da k o ristite ovo reenje, b are m za p o eta k (ukoliko ne p a d n e te u iskuenje da p re ra n o o p tim iz u je te k o ), ali svakako im ajte na u m u tu o sn o v n u razliku izm e u n avedena dva p ristu p a.

Veba 40: (3) Svim lju b im c im a u p ro g ra m u podaciotipu.ljubim ci d o d ajte m eto d u govo riti( ). P rera d ite p ro g ra m Prim eni.java tako da poziva m eto d u govoriti( ) za razn o ro d n e kolekcije objek ata tip a Ljubimac.

Kada sluajno nemate odgovarajui interfejs


Im ali sm o sree u g o rn jem p rim e ru .z a to to je interfejs Ite ra b le ve u g ra en , a radi tano o n o to n a m treb a. A ta da ra d im o u o p te m sluaju, kada n em a ve u g ra en o g interfejsa koji slu ajn o ba zadovoljava nae potreb e? P rim e ra rad i, h ajd e da u o p tim o ideju klase PopunjenaLista i n ap ra v im o p a ra m e trizovanu m eto d u p o p u n i( ) koja p rim a n ek u sekvencu i p o p u n ja v a je p o m o u nekog Generatora. Po kuajte da nap iete to u Javi i n aleteete na p ro b lem , je r n em a p o d esn o g
P o g le a jte p o s le d n ji o d e lja k u o v o m p o g la v lju .

Poglavlje 15: Generiki tipovi

581

interfejsa ImaAdd, p o p u t iu terfejsa Iterable n a en o g u p re th o d n o m p rim e ru . Stoga u m e s to d a kaete: Sve za ta se m o e pozvati m e to d a a d d ( ), m o ra te re i p o d tip o d Collection". R ezu ltu jui k o d nije n a ro ito opti, p o to m o ra biti o g ran ien na ra d sam o s realizacijam a klase Collection. A ko p o k u am da u p o tre b im klasu koja ne realizuje Collection, m oj gen erik i k o d nee rad iti. Evo k ako to izgleda:
//: genericki/Popuni .java // Uoptavanje ideje programa PopunjenaLista // {main: PopuniTest} import java.util.*; // // // // Ne radi sa "svime to ima metodu add()." Ne postoji interfejs "ImaAdd", pa moramo da upotrebimo neki kontejner (Collection). U ovom sluaju, ne moemo da uoptimo pomou generikog koda.

public class Popuni { public static <T> void popuni(Collection<T>kolekcija, Class<? extends T> classLeksema, int velicina) { for(int i = 0; i < velicina; i++) // Pretpostavlja da postoji podrazumevani konstruktor: try { kolekcija.add(classLeksema.newInstance()); } catch(Exception e) { throw new RuntimeException(e);

} }

class Ugovor { private static long brojac = 0; private final long id = brojac++; public String toStringO { return getClass().getName() + " " + id;

} }
class PrenosVlasnistva extends Ugovor {} class PopuniTest { public static void main(String[] args) { List<Ugovor> ugovori = new ArrayList<Ugovor>(); Popuni.popuni(ugovori, Ugovor.class, 3); Popuni.popuni(ugovori, PrenosVlasnistva.class, 2); for(Ugovor c: ugovori) System.out.println(c); JednostavanRedZaCekanje<Ugovor> redUgovora = new JednostavanRedZaCekanje<Ugovor>(); // Nee raditi. popuni() nije dovoljno opta: // Popuni.popuni(redUgovora, Ugovor.class, 3);

582

Misliti na Javi

} /* Ispis: Ugovor 0 Ugovor 1 Ugovor 2 PrenosVlasnistva 3 PrenosVlasnistva 4

* ///:O vde b i m eh an izam p a ra m e triz o v a n ih tip o v a s la te n tn im tip o v im a d o b ro posluio, p o to n e biste bili n a m ilo sti p ro jek tan tsk ih o d lu k a tv orca neke biblioteke i ne biste m o rali d a prera u jete svoj k o d k ad g o d n ai ete na n ek u b ib lio tek u koja nije previdela vau situaciju (p a b i va ko d za is ta b io o p ti). U g o rn jem slu a ju ,p o to p ro je k ta n ti Jave (razu m ljivo) n isu videli p o tre b u za in terfejso m ImaAdd, og ran ien i sm o n a h ijerarh iju klase CoUection, p a JednostavanRedZaCekanje ne rad i m ad a im a m e to d u add(). Poto je o granien na ra d sam o s k o n te jn e rim a klase Collection, p re th o d n i p ro g ra m nije naroito opti. To ne bi bilo tako d a im a m o la te n tn e tipove.

Simuliranje latentnih tipova pomou adaptera


Dakle, Javin generiki k o d n e m a la te n tn e tipove, a on i n a m treb aju da b ism o m ogli da p iem o p ro g ram e koji se m o g u p rim e n iti na najrazliitije klase (tj. opte (generike) p ro gram e). M oem o li nekako da z a o b id e m o to ogranienje? ta bism o ovde postigli p o m o u la te n tn ih tipova? M ogli b ism o da n ap iem o kod koji kae: Svejedno m i je koji tip ovde k o ristim , sam o ako im a ove m etode". U stvari, la ten tn i tipo vi prave im plicitan mterfejs koji sadri eljene m etod e. Iz toga sledi da e p ro b lem biti reen ako sam i n ap iem o p o tre b a n interfejs (p o to Java to nije u rad ila u m esto nas). Pisanje koda koji o d interfejsa koji im a m o pravi eljeni interfejs, p rim e r je p ro jek tn o g ob rasca A dapter (A dap ter). A d ap tere m o e m o u p o tre b iti za p rilag o av an je p o stojeih klasa tako da n aprave eljeni interfejs, uz relativn o m alu koliinu koda. Reenje u kojem sm o u p o treb ili p re th o d n o d efin isan u h ije ra rh iju klasa Kafa, p o k azu je razliite naine pisanja adaptera:
//: genericki/Popuni2.java // Upotreba adaptera za simuliranje latentnih tipova. // (main: Popuni2Test} import genericki.kafa.*; import java.util.*; import net.mindview.util.*; import static net.mindview.uti1.Print.*; interface ImaAdd<T> { void add(T t); } public class Popuni2 { // Verzija s Class leksemom: public static <T> void popuni(ImaAdd<T> imajuciadd, Class<? extends T> classLeksema, int velicina) { for(int i = 0; i < velicina; i++) try {

Poglavlje 15: Generiki tipovi

583

imaadd.add(classLeksema.newInstance()); ) catch(Exception e) { throw new RuntimeException(e);

} }
// Generatorska verzija: public static <T> void popuni(ImaAdd<T> imaadd, Generator<T> generator, int velicina) { for(int i = 0; i < velicina; i++) imaadd.add(generator.sledeci());

// Za prilagoavanje osnovnog tipa morate upotrebiti kompoziciju. // Pomou kompozicije, napraviemo da svi podtipovi // klase Collection imaju metodu add(): class ImaAddCollectionAdapter<T> implements ImaAdd<T> { private Collection<T> c; public ImaAddCollectionAdapter(Collection<T> c) { this.c = c;

}
public void add(T stavka) { c.add(stavka); }

}
// Pomaga za automatsko hvatanje tipa: class Adapter { public static <T> ImaAdd<T> adapterKolekcije(Collection<T> c) { return new ImaAddCol1ectionAdapter<T>(c);

// Za pri1agoavanje odreenog tipa moete upotrebiti nasleivanje. // Pomou nasleivanja, napraviemo da // JednostavanRedZaCekanje ima metodu add(): class ImaAddJednostavanRedZaCekanje<T> extends JednostavanRedZaCekanje<T> implements ImaAdd<T> { public void add(T stavka) { super.add(stavka); }

}
class Popuni2Test { public static void main(String[] args) { // Prilagodi kontejner (objekat tipa Collection): List<Kafa> nosilac = new ArrayList<Kafa>(); Popuni2.popuni( new ImaAddCol1ectionAdapter<Kafa>(nosi 1ac), Kafa.class, 3); // Pomagaka metoda tip hvata: Popuni2.popuni(Adapter.adapterKolekcije(nosilac), SMlekom.class, 2);

584

Misliti na Javi

for(Kafa c: nosilac) print(c); print("................. ...... "); // Upotreba prilagoene klase: ImaAddJednostavanRedZaCekanje<Kafa> redKafa = new ImaAddJednostavanRedZaCekanje<Kafa>(); Popuni2.popuni(redKafa, Moka.class, 4); Popuni2.popuni(redKafa, SMlekom.class, 1); for(Kafa c: redKafa) print(c);

}
} /* Ispis: Kafa 0 Kafa 1 Kafa 2 SMlekom 3 SMlekom 4 Moka 5 Moka 6 Moka 7 Moka 8 SMlekom 9

* ///:-

Popuni2 ne zahteva k o n te jn e r tip a Collection, kao to je zahtev ao p ro g ra m Popuni. U m esto toga, o n zahteva n eto to realizuje in terfejs ImaAdd, a ImaAdd je nap isan u p ra vo za p ro g ram Popuni - o n je pojava la te n tn o g tip a k o ju sam h te o da prev o d ilac nap rav i um esto m ene. U ovoj verziji, d o d ao sam i p rek lo p ljen u m e to d u p o p u n i( ) koja u m e sto leksem e tipa p rim a Generator. T ip Generatora se p roverava u v rem e prev o d en ja: prev o d ilac vas nee p u stiti da mu prosled ite n eisp rav an tip G eneratora, pa u v rem e izvravanja nee biti izuzetaka. Prvi adapte r, ImaAddCollectionAdapter, rad i sa o sn o v n im tip o m Collection, to znai da se m oe u p o tre b iti svaka realizacija klase Collection. O va verzija je d n o sta v n o skladiti referencu o b jek ta tip a Collection i k o risti je za realizaciju m e to d e a d d ( ). Ako im ate o d re e n i tip , a n e o sn o v n u klasu h ijerarh ije, prav ljen je ad a p te ra nasleivan jem zahtevae neto m a n je k o d a, kao to m o ete v ideti u ImaAddJednostavanRedZaCekanje.
U m eto d i Popuni2T est.m ain( ), m o ete v ideti razne vrste a d a p te ra na delu. Prvo, neki p o d tip klase Collection biva p rilag o d e n p o m o u klase ImaAddCoIlectionAdapter. D ru ga verzija a d a p te ra k oristi g en erik u p o m a g a k u m e to d u , i m o ete videti kako ta gencrika m eto d a hvata tip, tak o da o n ne m o ra biti izriito n ap isan - to je p o d esa n trik koji daje elegantniji kod. Z atim se upotrebljava p re th o d n o p rilag o en a klasa ImaAddJednostavanRedZaCekanje. Vodite rau n a o to m e da u oba sluaja ad ap teri o m o g u u ju da se klase, koje p reth o d no nisu realizovale interfejs ImaAdd m o g u p o p u n jav ati m e to d o m Popuni2.popuni().

Poglavlje 15: Generiki tipovi

585

Izgleda kao d a ovakvo k o rien je a d a p te ra k o m p en zu je n ep o sto jan je la te n tn ih tip o v a i tim e o m o g u u je p isan je zaista o p teg koda. M e u tim , to je d o d a tn i k o ra k koji m o ra ju ra z u m e ti i tv o ra c i k o risn ik b iblioteke, a m a n je isk u sn i p ro g ra m e ri m o d a nee tak o b rzo shvatiti njegov k on cept. L aten tn i tip o v i u k la n ja ju taj d o d a tn i korak, im e olakavaju korienje generik og koda, i u to m e je n jih o v a v red n o st.

Veba 41: (1) P rep ravite p ro g ra m Popuni2.java tak o d a u m esto klasa Kafa u p o treb ljav a klase iz podaciotipu.ljubim ci.

Upotreba funkcijskih objekata kao strategija


U ov om zav rn o m p rim e ru n a p ra v ie m o p rav i g eneriki k o d p o m o u ad ap te ra , kao to je op isa n o u p re th o d n o m o d eljk u . N ajp re je u p rim e ru treb alo d a se n ap rav i zb ir sekvence elem en ata (b ilo kojeg tip a koji se m o e sab ira ti), ali se razvio d o izvoenja o p tih o p e ra cija ko rien jem funkcionalnog stila p ro g ra m ira n ja . Ako sam o p o g le d ate pro ces p o k u av an ja d a se sa b eru o bjekti, videete d a u to m sluaju im a m o zajednike o p eracije ra z n ih klasa, ali te o p eracije n isu pred stav ljen e u nekoj o sn o v n o j klasi koju b ism o m o g li d a sp ecificiram o - p o n e k a d ak m o ete d a u p o tre b ite o p e ra to r + , a u d ru g im sluajevim a m o e p o sto jati neka v rsta m eto d e add. To je u o b iajen a situ acija koja se sree kada p o k u av ate d a piete generiki k o d , zato to h o ete da k o d b u d e p rim e n ljiv na razliite klase - n aro ito , kao u o v o m sluaju, n a vie posto jeih klasa koje ne m oete da p rep rav ite. ak i k a d a b iste suzili ovaj izb o r na po tk lase o d N u m b e r, ta natklasa n ita n e kae o nekoj sabirljivosti. Reenje je p rim e n iti p ro je k tn i o b razac Stratcgy (S trategija) koji p ro izv o d i eleg an tn iji kod tako to ,,ono to se m en ja p o tp u n o izoluje u n u ta r o d re e n o g funkcijskog objekta.'0 Funkcijski objek at je onaj koji se na neki n ain p o n a a kao fu n k cija - o b i n o k ad a je u p ita n ju je d n a m eto d a (u jezicim a koji p o d rav aju p rek lap an je o p e ra to ra , m o ete n ap rav iti da poziv te m e to d e izglea kao o b ian poziv m eto d e). V re n o st funkcijskih o b jek ata je to to, za razliku od o b i n ih m e to d a, njih m o ete p rosleivati, a o n e tak o e m o g u im ati i stan je koje se ne gubi izm e u poziva. N aravno, neto slino m o ete postii s bilo ko jo m m e to d o m o d re e n e klase, ali (kao to je sluaj sa svim p ro je k tn im o b rascim a) funkcijski objekat se p rv en stv en o o d lik u je svojom sv rh o m . O vde je svrha n ap rav iti n eto to se p o naa kao m eto d a koja se m oe p rosleivati; stoga je funkcijski o b jek at tesno povezan sa p ro je k tn im o b rascem Stratcgy (a p o n e k a d je isto to i o n ). Kao to m i se desilo s vie p ro je k tn ih o b razaca, ovde m i rei p o staju nekako nejasne: p ra v im o funkcijske ob jek te koji obavljaju prilag o av an je, a njih p ro sled u jem o m eto d a m a koje ih u p o tre b lja v a ju kao strategije. Sleei taj p ristu p , d o d a o sam razn e vrste g en erik ih m e to d a koje sam p rv o b itn o nam erav ao da n a p ra v im , i jo neke. R ezultat je ovo:
//: genericki/Funkcional .java import java.math.*; import java.uti1.concurrent.atomic.*; " Za njih se upotreb ljava i im e funktori la u koristiti te rm in funkcijski objekat, p oto funktor" ima specifino i d ru g o znaenje u inatem atici.

586

Misliti na Javi

import java.util.*; import static net.mindview.util .Print.*; // Razni tipovi funkcijskih objekata: interface Kombinator<T> { T objedini(T x, T y); } interface UnarnaFunkcija<R,T> { R funkcija(T x ) ; } interface Kolektor<T> extends UnarnaFunkcija<T,T> { T rezultat(); // Izdvoji rezultat parametra kolekcije

}
interface UnarniPredikat<T> { boolean test(T x); } public class Funkcional { // Poziva objekat Kombinator za svaki element da bi ga objedinio // s tekuim rezultatom, koji zatim vraa: public static <T> T redukuj(Iterable<T> sekv, Kombinator<T> kombinator) { Iterator<T> it = sekv.iterator(); if(it.hasNext()) { T rezultat = it.sledeci(); while(it.hasNext()) rezultat = kombinator.objedini(rezultat, it.sledeci()); return rezultat;

}
// Ako je sekv prazna lista: return null; // 11i generii izuzetak

}
// Uzmi funkcijski objekat i pozovi ga za svaki objekat u // listi; povratnu vrednost zanemari. Funkcijski objekat // moe delovati kao parametar kolekcije, pa se // na kraju on vraa kao rezultat. public static <T> Kolektor<T> forEach(Iterable<T> sekv, Ko1ektor<T> funk) { for(T t : sekv) funk.funkcija(t); return funk;

}
// Pravi listu rezultata pozivanjem funkcijskog // objekta za svaki objekat u listi: public static <R,T> List<R> transformisi (Iterable<T> sekv, UnarnaFunkcija<R,T> funk) { List<R> rezultat = new ArrayList<R>(); for(T t : sekv) rezultat.add(funk.funkcija(t)); return rezultat;

}
// Primenjuje unarni predikat na svaku stavku sekvence, // i vraa listu stavki koje su dale "true": public static <T> List<T> fi1tar(Iterable<T> sekv, UnarniPredikat<T> pred) { List<T> rezultat = new ArrayList<T>();

Poglavlje 15: Generiki tipovi

587

for(T t : sekv) if(pred.test(t)) rezultat.add(t); return rezultat;

}
// Da bismo mogli da koristimo gornje generike metode, // moramo napraviti funkcijske objekte za prilagoavanje metoda // naim specifinim potrebama: static class SabiracCelih implements Kombinator<Integer> { public Integer objedini(Integer x, Integer y) { return x + y;

} }
static class OduzimacCelih implements Kombinator<Integer> { public Integer objedini(Integer x, Integer y) { return x - y;

} }
static class SabiracVelikihDecimalnih implements Kombinator<BigDecimal> { public BigDecimal objedini(BigDecimal x, BigDecimal y) { return x.add(y);

} }
static class SabiracVelikihCelih implements Kombinator<BigInteger> { public Biglnteger objedini(Biglnteger x, Biglnteger y) { return x.add(y);

} }
static class SabiracAtomicLong implements Kombinator<AtomicLong> { public AtomicLong objedini(AtomicLong x, AtomicLong y) { // Nisam siguran da ovo ima smisla: return new AtomicLong(x.addAndGet(y.get()));

} }
// Moemo napraviti ak i UnarnuFunkciju s metodom "ulp // (Units in the last place, jedinica na poslednjem mestu): static class VelikiDecimalniUlp implements UnarnaFunkcija<BigDecimal,BigDecimal> { public BigDecimal funkcija(BigDecimal x) { return x.ulp();

} }
static class VeceOd<T extends Comparable<T implements UnarniPredikat<T> { private T granica; public VeceOd(T granica) { this.granica = granica; }

588

Misliti na Javi

public boolean test(T x) { return x.compareTo(granica) > 0;

} }
static class KolektorMnoziCele implements Kolektor<Integer> { private Integer vre = 1; public Integer funkcija(Integer x) { vre *= x; return vre;

}
public Integer rezultat() { return vre; }

}
public static void main(String[] args) { // Generiki tipovi, argumenti promenljive duine // i pakovanje u zajednikom radu: List<Integer> li = Arrays.asList(l, 2, 3, 4, 5, 6, 7); Integer rezultat = redukuj(li, new SabiracCel ih()); print(rezultat); rezultat = redukuj(li, new OduzimacCelih()); print(rezultat); print(filtar(li, new Vece0d<Integer>(4))); print(forEach(li, new KolektorMnoziCele()).rezultat()); print(forEach(fi1tar(li, new Vece0d<Integer>(4)), new KolektorMnoziCele()) .rezultat()); MathContext mk = new MathContext(7); List<BigDecimal> lvd = Arrays.asList( new BigDecimal(1.1, mk), new BigDecimal(2.2, m k) , new BigDecimal(3.3, m k), new BigDecimal(4.4, mk)); BigDecimal rvd = redukuj(lvd, new SabiracVelikihDecimalnih()); print(rvd); print(filtar(lvd, new VeceOd<BigDecimal>(new BigDecimal(3)))); // Koristi ugraeno generisanje prostih faktora tipa Biglnteger: List<BigInteger> Ibi = new ArrayList<BigInteger>(); Biglnteger bi = Biglnteger.valueOf(11); for(int i = 0; i < 11; i++) { 1b i .add(bi) ; bi = bi,nextProbablePrime();

}
pri nt (1 bi);

Poglavlje 15: Generiki tipovi

589

Biglnteger rbi = redukujObi, new Sabi racVel i kihCel ih()); print(rbi); // Zbir stavki sa ove liste prostih faktora i sam je prost broj: print(rbi,isProbablePrime(5)); List<AtomicLong> lal = Arrays.asList( new AtomicLong(ll), new AtomicLong(47), new AtomicLong(74), new AtomicLong(133)); AtomicLong ral = redukuj (1 al, new SabiracAtomicLongO); print(ral); print(transformisi (lvd,new VelikiDecimalnilllpO));

}
} /* Ispis: 28 -26 [5, 6, 7] 5040

210
11.000000 [3.300000, 4.400000] [11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47] 311 true 265

[0 . 000001 , 0 . 000001 , 0 . 000001 , 0 .000001] * ///:P o injem defin isanjem interfejsa za razliite tipove fu nkcijskih objek ata. N jih sam prav io p o p o treb i, kako sam razvijao razliite m eto d e i u v i ao p o tre b u za svakim . Klasu K om binator je p red lo io a n o n im n i k o m e n ta to r jedn og o d lanaka ob jav ljenih n a m ojoj W eb lokaciji. Kombinator a p stra h u je k o n k retn e detalje sa b iran ja dva o b je k ta i k azuje sam o da o n i bivaju nekako o b jed injen i. Kao rezultat, m oete videti da SabiracCelih i OduzimacCelih m o g u biti tipovi n ad tip a Kombinator. UnarnaFunkcija p rim a sam o jedan a rg u m e n t i daje rezultat; a rg u m e n t i rez u lta t ne m o ra ju biti istog tipa. Kolektor se upotreb ljav a kao p a ra m e ta r kolekcije", a rezu ltat m oete da izdvojite kada zavrite. U narniPredikat daje rezu ltat tip a boolean. M o g u se d efinisati funkcijski o bjekti i d ru g ih tipova, ali ovo je bilo d o v o ljn o za p o u k u . Klasa Funkcional sadri vie g enerikih m eto d a koje funkcijske o b jekte p rim e n ju ju na sekvence. red u k u j( ) p rim e n ju je funkciju u o b je k tu tipa Kom binator n a svaki elem en t sekvence da bi proizvela sam o jed a n rezultat. fo rE ach ( ) p rim a Kolektor i p rim en ju je svoju fun kciju na svaki elem en t, z a n e m a ru ju i re z u lta t svakog poziva funkcije. N ju m oete p ozivati sam o zbog sp o re d n o g efekta (to ne bi bio fu n k cio n a ln i stil p ro g ram iran ja, ali ipak m oe da po slui), ili Kolektor m oe da o d rava in te rn o stan je d a bi p o stao p a ra m e ta r kolekcije, kao to je sluaj u ov o m p rim e ru . tran sfo rm isi( ) pravi listu pozivanjem funkcijskog o bjekta U narnaFunkcija za svaki o b jek at u sekvenci i h v atan jem rezultata.

590

Misliti naJavi

N ajza, filta r( ) p rim e n ju je funkcijski objek at U narniPredikat na svaki o bjekat u sekvenci i o n e koji v raaju tru e sm eta u Listu koju vraa. M oete definisati i sopstvene generike funkcije. P rim e ra radi, C + + -o v a s ta n d a rd n a b ib lio tek a a b lo n a STL im a ih n a gom ile. O vaj p ro b le m je b io reen i u n ekim b iblio tek a m a o tv o re n o g izvornog koda; je d n a o d n jih je JGA (G eneric A lg o rith m s for Java, G en erik i alg o ritm i za Javu). C + + -o v i ia te n tn i tipovi se staraju za pronalaenje odgo varaju ih op eracija p rilik o m p o ziva funkcija, ali u Javi m o ra m o d a p iem o funkcijske objekte za prilago av anje generikih m eto d a naim p o treb am a. Z ato sledei deo klase sadri razliite realizacije funkcijskih objekata. O b ra tite pan ju n a to, recim o, da SabiracCelih i SabiracVelikihDecimalnih reavaju isti p ro b lem - sab iran je dva objekta - pozivanjem o dg ov araju ih operacija za svoj tip. D akle, to je kom b in acija obrazaca A dapter i Strategy. U m e to d i m a in ( ), v id ite d a se u svakom pozivu m eto d e uz od go v araju i funkcijski o b jek at p ro sle u je sekvenca. T akoe, vie izraza je p rili n o sloeno, kao:
forEach(filtar(li, new Vece0d(4)), new KolektorMnoziCele()).rezultat()

P re th o d n i izraz pravi listu b ira n je m svih elem en ata u li veih o d 4, p rim e n ju je n a n ju K olektorM noziCele( ) i izdvaja re z u lta t( ). D etalje ostatk a p ro g ra m a n eu da o bjan jav am - v e ro v a tn o ete ih i sam i shvatiti k ad a ih p ro u ite.

Veba42: (5) N ap rav ite dve zasebne klase koje n em aju nita zajedniko. Svaka klasa treb a
d a sadri n ek u v re d n o st i da im a b arem o n e m eto d e koje proizvo de tu v re d n o st i m enjaju

je. Izm en ite Funkcional.java tako da obavlja fu n k cio n aln e operacije na kolekcijam a vaih klasa (te o p eracije ne m o ra ju b iti aritm etik e kao to su u p ro g ra m u Funkcional.java).

Saetak: da li je eksplicitna konverzija tipova zaista tako loa?


Poto C + + a b lo n e objan jav am o d njih o v o g n astan k a, sleee ra z m a tra n je sam n av od io vero v atn o ee o d ikoga. Tek n edav n o sam p restao d a se p ita m koliko esto je takvo razm a tra n je u m e s n o koliko p u ta se p ro b lem koji u opisati zaista p rik ra d e i pom oli? R az m atran je ide ovako. K ontejnerske klase List, Set, Map itd. sp ad aju m e u n a jp rik lad n ija m esta za u p o tre b u m e h an izm a generikih tipova. U poznali ste ih u poglavlju uvanje objekata i jo ete itati o n jim a u poglavlju Detaljno razm atranje kontejnera. Pre Jave SE5, tip o b jek ta stavljenog u k o n tejn er bio je sveden navie na Object, pa se gubila inform acija o p ra v o m tip u . K ada je treb alo neto u ra d iti sa o b jek to m i izvaditi ga iz kon tejn era, m o ra o se eksp licitn o k o n v erto v ati nazad u pravi tip. M oj p rim e r bila je lista o b jek ata tip a Macka (n a p o etk u poglavlja uvatije objekata d ata je v arijan ta s jab u k am a i n a ra n d a m a ). D ok nije bilo g enerike verzije k o n tejn era koju je d o n ela Java SE5, u n u tra sm o stavljali Objecte, iz k o n te jn era sm o vadili Objecte, pa je bilo veom a lako staviti obje k a t tip a Pas u listu objek ata tipa Macka.

Poglavlje 15: Generiki tipovi

591

M e u tim , p red g en erik a Java n e bi d o p u stila d a zloupotrebite objekte stavljene u k o n tejner. D a ste stavili objekat tip a Pas u k o n tejn e r Macka i p o k u ali d a sve u k o n te jn e ru tretirate k ao d a je tip a Macka, d o b ili biste RuntimeException - d a ste izvadili referen cu o bjekta tip a Pas iz k o n tejn era Macka i p o k u ali d a ga ek p lic itn o ko n v ertu jete u tip Macka, d o b ili biste izuzetak. P roblem nije ostajao sakriven, ali ste ga otkriv ali u v rem e izvravanja, a ne u v rem e prev o en ja. U p re th o d n im izd an jim a ove knjige, dalje sam pisao: Ovo je vie od neprijatnosti. Zbog toga mogu nastati greke k o jeje teko otkriti. Ako deo (ili vie delova) program a umee objekte u kontejner, a vi tek preko izuzetka u nekom zasebnotn delu program a otkrijete da je u kontejner stavljen lo objekat, onda m orate da traite gde je lo objekat umetnut. N akon daljnjeg razm iljan ja o ovom e, p oeo sam d a su m n ja m . P rvo, koliko esto se to dogaa? Ne seam se d a m i se ikada desilo n eto takvo, a i n a k o n feren cijam a n isa m uo d a se to n ek o m e dog o d ilo . U je d n o j knjizi n av o d io se p rim e r liste n azv an e datoteke koja sadri String objekte - u to m p rim e ru izgledalo je sasvim p riro d n o d o d a ti o b jek at tip a D atoteka u datoteke, pa je objek at treb alo n azvati recim o im enaDatoteka. K oliko g o d da Java p ro v erava tipove, i dalje je m o g u e pisati n ejasn e p ro g ra m e , a loe n a p isa n p ro g ra m koji se ipak prevede i dalje ostaje lo. M oda veina p ro g ra m e ra k o n te jn e rim a daje p rik lad n a im ena, recim o macke kao vizuelno u p o zo ren je da se u n u tr a n e d o d a ju o b jek ti koji n isu tip a Macka. A d a se to i desi, koliko d u g o bi ostalo n eo tk riv en o ? in i m i se d a bi se pojavio izuzetak im bi poelo testiran je s p rav im p o d acim a. Jedan a u to r je ak tv rd io d a bi takva greka m o g la o sta ti sakrivena g o d in a m a . Ali neto ne p a m tim p oplav u izvetaja o lju d im a koji su v eo m a teko p ro n alazili greke tip a ,,pas u listi m aaka, pa ak se ne seam ni da su ih esto pravili. S d ru g e stran e, u poglavlju Paralelno izvravanjc videete d a se s n itim a v eo m a lako i esto p rave greke koje se p o javljuju izuze tno retko, a daju tek n e o d re e n u p red stav u o to m e ta n e valja. D akle, da li je greka ,,pas u listi m aaka pravi razlog to je ova veo m a zn aajn a i p rilin o slo en a m o g u n o st d o d a ta Javi? S m a tra m da je svrha jezike m o g u n o sti o p te n am en e n azv an e generiki tip o v i (n e n u n o i njene k o n k re tn e realizacije u Javi) izraajnost, a ne sa m o pravljenje k o n te jn e ra iji se tip o v i p roveravaju. K o n tejneri b ezb e d n ih tipova su sp o re d a n efekat sp o so b n o sti p ravljenja koda optije nam en e. Z ato, iako se greka tip a pas u listi m aaka esto navodi kao o p ra v d an je za uvo en je generik ih tipova, p itan je je da li to stoji. I kao to sam tv rd io na p o etk u ovog poglavlja, ne verujem da je to zapravo svrha g enerikih tipova. Kao to im i im e govori, generiki tip ov i o m o g u u ju pisan je optijeg k o d a koji m an je og ran iav a tipove s k ojim a m oe da radi, pa se isto pare koda m oe p rim e n iti na vie tipova. U o v o m poglavlju videli ste da je p rilin o lako nap isati zaista opte klase ,,skladite (a Javini k o n te jn e ri to jesu ), ali napisati gen eriki kod koji o b ra u je svoje generike tipove zahteva d o a tn i tr u d tv o rca klase ; n jen o g korisn ika, koji m o ra da shvati k o n cep t i reaiizaciju p ro je k tn o g o b rasca Adapter. Taj d o d a tn i tru d oteava korienje gen erik ih tip o v a i ini ih m an je p rim e n ljivim u sluajevim a gde bi inae d o b ro posluili.

592

Misliti na Javi

T akoe, im ajte u v id u sledee: zbog to g a to su generiki tip o v i bili n a k n a d n o ub aen i u Javu, u m esto d a su od p o etk a p ro je k to v an i kao n je n sastavni deo, neki k o n te jn e ri n isu toliko ro b u sn i koliko bi treb alo da b u d u . N a p rim er, p o gledajte M ap, k o n k re tn o m eto d e containsKey(Object klju) i get(Object klju). D a su te klase p ro jek to v an e s p o sto jeim gen erik im tip o v im a , te m eto d e bi u p o treb ljav ale p a ram e trizo v a n e tipove, a ne Object, i tak o obezbed ile p ro v eru tipova u v rem e p rev o en ja k o ju bi g eneriki tip o v i treb alo d a o b ezb e u ju . P rim era rad i, u C + + m a p a m a tip kljua se uvek p roverava u v rem e prev o en ja. Jedno je sasvim jasno: uvo en je bilo koje v rste generikog m e h a n iz m a u kasnoj verziji jezika, n ak o n to je o n uao u o p tu u p o tre b u , predstavlja veom a, v eo m a zapetljan p o d u hvat koji se ne m oe ostvariti bez rtava. U C + + su abloni uvedeni u p o etn o j ISO verziji jezika (iako je i to p ro uzro k o v alo potekoe, p o to se p re pojave prv o g sta n d a rd n o g C + + a koristila p re th o d n a verzija bez ab lo n a), p a su abloni u stvari oduvek bili deo tog jezika. U Javu su generiki tipovi uvedeni tek 10 g o d in a n ak o n njenog izlaska u svet, pa su problem i s p relaskom na generike tipove bili veo m a veliki i z n a tn o su uticali n a njihov dizajn. Posledica toga je d a vi, p ro g ram er, trp ite zato to p ro je k tan ti Jave nisu im ali viziju kada su pisali verziju 1.0. Kada je Java p ravljena, n jen i p ro jek tan ti su n arav n o znali za C + + -o v e ab lon e, i ak su razm atrali da li da ih ukljue u jezik, ali su iz nekog razloga (izgleda da su urili) odluili da ih izostave. Z ato trp e i jezik i p ro g ra m e ri koji ga koriste. Sam o v rem e e pokazati sve posledice koje e Javina realizacija g enerikih tipova im ati n a taj jezik. N eki jezici, m e u k ojim a su Nice (videti http://nice.sourceforge.net ; ovaj jezik generie Javin b ajtkod i radi s p o sto jeim Javinim b ib lio tek am a) i N extG en (videti http://japan.cs.rice.edu/nextgeri) im aju istiji i laki p ristu p p a ra m etriz o v an im tip o v im a. Nije nem o gue da neki takav jezik p o stan e Javin naslednik, p o to o n i rad e ta n o o n o to su a u to ri C + + -a u rad ili s C -o m : uzeli postojee i to poboljali.

Proitajte i ovo
U vodni d o k u m e n t za g enerike tip o v e je Gcnerics in thc java Progratnming Langttage , iji je a u to r G ilad B racha, a nalazi se na lokaciji h ttp://iava.fuii.coni/j2se/l.5/pdf/gcucrics-ttitorial.pdf Jara Gerterics FAQs A ngelike L anger veo m a je k o ristan resurs, na lokaciji www.langer.camelot.dc/GenericsFAQ/JavaGencricsFAQ.htnil. Vie o d o k ersk im a rg u m e n tim a saznaete iz teksta A dding VVildcards to thc Java Program tning Language, iji su a u to ri T orgerson, E rnst, H ansen, Von d e r A he, B racha i Gafter. lan ak je d o stu p a n na lokaciji w ww.iot.lin/issucs/isstie_2004_12/articlc5. Reenja odabranih vebi data su u elektronskom dokum entu l'lic Thiitking in lciva Annotatctl Soltttion Guidekoji se moe kupiti na lokaciji wmv.MiiidVicw.coiu.

Nizovi
N a kraju poglavlja Inicijalizacija i ienje objanjeno j e kako :,c definie i in icija lizu je niz.
PO JED N O ST A V L JE N O V I EN JE N IZA JESTE: N APRAVITE GA I PO P^M TTE, BIRATE ELEM EN TE IZ

njega p o m o u celo b ro jn o g indeksa, i o n ne m en ja svoju veh. To je u g lav n o m sve to m o ra te d a zn ate, ali p o n e k a d s nizo v im a m o ra te da obavite so ' iran ije o p eracije, a k atk ad a i da p ro c en ite da li je bolje u p o tre b iti niz ili neki flek. laln iji k o n tejn er. U ovom p oglavlju bolje ete u p o z n ati nizove.

ta to nizove ini posebnim


Im a vie d ru g ih n ain a za uvanje o bjekata, pa ta to niz in i po sebn im ? N izovi se o d d ru g ih v rsta k o n te jn e ra razliku ju po trim a o so b in a m a : efikasnosti, tip u i sp o so b n o sti uvanja p ro stih tipova. N iz je Javin najefikasm ii n a in za u v an je i n a su m ian p ris tu p g ru p i referenci na objekte. N iz je jed n o stav n a n e a rn a sekvenca ijim elem e n tim a se p ristu p a brzo, ali se ta b rz in a plaa: kada n a p rr . : niz, njegova veliina je ta n o o d re e n a i ne m oe se m en jati to k o m celog njegovog . o tn o g veka. Kao reenje ovog p ro b le m a m oe vam pasti na p am et klasa A rray L ist (k 4 e u p o zn ali u poglavlju uvanje objckata): ako p o n esta n e p ro sto ra u nizu, o n a au to ; v. .ski pravi nov niz i p reme.ta sve reference iz starog niza u novi. Iako bi klasi A rra y L i :o p ravilu treb a lo da d ajete p re d n o s t nad n izo m , o n a je zbog svoje prilagodljive veliine o setn o m an je efikasna o d o b in o g niza. I nizovi i k o n te jn e ri jem e da ih ne m o ete zlo u p o tre b iti. Bt / o b zira na to da li k o ristite niz ili k o n tejn er, pojavie se izuzetak tip a R u n tim e E x c e p tio n ako im p rek o raite granice, to ukazuje na greku p ro g ram era. Pre generik ih tipova, d ru g e k ontejnerske klase su radile sa o b jek tim a kao da n isu o d re en o g tipa, tj. o d nosile su se p rem a njim a kao da su tip a O bjec O b je c t je k orenska klasa svih Javinih klasa.) Z ato je niz su p eriorn iji o d pred generik i k o ntejnera: kada p ravite niz, zn ate da e o n sadrati o d re en i tip. To znai da e se proven \ tip a to k o m p rev o en ja o n e m o g u iti stavljanje pog ren o g tipa u niz, ili itanje pogre. g tipa iz niza. N aravno, Java e spreiti i slanje n eo dgovarajue p o ru k e o bjek tu , i to u v p 'm e prev o en ja ili izvravanja. D akle, nijedan o d dva naina nije riziniji. Lepe je da prevodilac u p o z o ri na greku u p ro g ra m u , pa da krajnji korisnik ne b u d e iznenaen ig nekog izuzetka. N iz m oe da uva pro ste tip o v e ,d o k predg en eriki ko n tejn eri to nisu m ogli. M e u tim , generiki k o n tejn eri mogu da zaaju i proveravaju tip objekata koje sadre, a sa a u to m a tskim pak o v an jem d elu ju kao da m ogu da p rim a ju i p roste tipove, p o to je konverzija au to m atsk a. Evo p rim e ra u kojem se p ored e nizovi i generiki ko n tejn eri:
/ / : nizovi/PoredjenjeSKontejnerima.java import java.uti1 . import static n e t ,mindview.uti1 .Print.*;

594

Misliti na Javi

class SferaOdBeri1ijuma { private static long brojac; private final long id = brojac++; public String toStringO { return "Sfera " + id; }

}
public class PoredjenjeSKontejnerima { public static void main(String[] args) { SferaOdBeri 1 ijuma[] sfere = new SferaOdBeril ijuma[10] ; for(int i = 0; i < 5; i++) sfere[i] = new Sfera0dBerilijuma(); pri nt(Arrays.toString(sfere)); print(sfere[4]); List<SferaOdBerilijuma> listaSfera = new ArrayList<SferaOdBerilijuma>(); for(int i = 0 ; i < 5 ; i++) listaSfera.add(new SferaOdBerilijumaO); print(li staSfera); print(listaSfera.get(4)); int[] celiBrojevi = { 0, 1, 2, 3, 4, 5 }; print(Arrays.toString(celiBrojevi)); print(cel iBrojevi [4]);
List<Integer> 1 istaCelihBrojeva = new ArrayList<Integer>( Arrays.asList(0, 1, 2, 3, 4, 5)); 1i staCeli hB r o j e v a .a d d (97); p r i n t (1i staCelih B r o j e v a ) ; pri n t (1i staCeli hB ro j e v a . g e t (4));

}
} /* Ispis: [Sfera 0, Sfera 1, Sfera 2, Sfera 3, Sfera 4, null, n u l l , nul 1 , null, nul 1] Sfera 4 [Sfera 5, Sfera 6, Sfera 7, Sfera 8, Sfera 9] Sfera 9 [0, 1, 2, 3, 4, 5] 4 [0, 1, 2, 3, 4, 5, 97] 4

* ///:U Javi se granice p roveravaju bez o b zira na to da li ko ristite niz ili k o n tejn er; p riv id n o je jed in a razlika m e u n jim a to to nizovi im aju o p e ra to r [ ] za p ristu p a n je e lem en tim a, a liste im aju m eto de kao to su a d d () i g et(). S linost i/m e d u nizova i klase A rra y L ist nam ern a je, da bi bilo lako k o ristiti i je d n e i d ru g e. Ali kao to ste videii u pogiavlju uvanje objekata , k o n te jn e ri su m n o g o fu n k cio n aln iji o d nizova.

Poglavlje 16: Nizovi

595

O d u v o enja au to m atsk o g p ako vanja, p ro ste tipo ve je g otovo je d n a k o lako uvati u k o n te jn e rim a kao u nizovim a. Jedina p reo stala p re d n o s t nizova je efikasnost. M ed u tim , k ad a reavate o p tiji p ro b lem , n izovi m o g u d a n a m e tn u p restro g a o g ran ien ja, i u tim sluajevim a u p o treb ljav ajte k o n tejn ere.

IMizovi su prvorazredni objekti


Bez o bzira n a to s kojom v rsto m niza rad ite, id e n tifik a to r niza je referenca na stv arn i o b jek at koji je nap rav ljen u d in a m i k o j m e m o riji. To je o b jek at koji sad ri reference na d ru g e objekte, a m o e b iti n ap ra v ljen bilo im p licitn o , kao deo sin tak se za inicijalizaciju niza, bilo eksplicitno, o p e ra to ro m new . D eo o b jek ta - niza (zapravo, je d in o polje k o m e m oete d a p ristu p ite ) jeste lan le n g th koji se m o e sam o itati, a ozn aav a bro j elem en a ta niza. Pored toga, sin ta k sn a o z n a k a [j je d in i je n a in za p ristu p o b je k tu - nizu. Sledei p rim e r p rik azu je razliite n a in e za inicijalizaciju niza i dodeljiv an je referenci o b je k tim a u n izu . P rim er p o k azu je i da se nizovi o b jek ata i nizovi p ro s tih tip o v a k oriste g otovo istovetno. Jeina razlika je to to nizovi o b jek ata sadre reference, d o k nizovi p ro stih tipo v a sadre v red n o sti p ro stih tipova.
//: nizovi/MogucnostiNiz a .java // Inicijalizacija i ponovna dodela vrednosti nizu. import java.util import static net.mindview.util.Print.*; public class MogucnostiNiza { public static void main(String[] args) { // Nizovi objekata: SferaOdBeri1ijuma[] a; // Neinicijalizovana lokalna promenljiva SferaOdBerilijuma[] b = new SferaOdBeril ijuma[5]; // Reference unutar niza se // automatski inicijalizuju na null: print("b: " + Arrays.toString(b)); SferaOdBeri 1 ijuma[] c = new SferaOdBeril ijuma[4]; for(int i = 0; i < c.length; i++) if(c[i] == null) // Mogue testiranje na null referencu c[i] = new SferaOdBeri 1 ijuma(); // Agregatna inicijalizacija: SferaOdBeri1ijuma[] d = { new SferaOdBeri1ijuma(), new SferaOdBeri1ijuma(), new SferaOdBeri1ijuma()

};
// Dinamika agregatna inicijalizacija: a = new SferaOdBeri 1 ijuma[] { new SferaOdBerilijuma(), new SferaOdBeri1ijuma(),

};
// (Zarez na poslednjem mestu je neobavezan u oba sluaja) print("a.length = " + a.length); pri nt ("b.1ength = 1 1 + b.length);

596

Misliti na Javi

print("c.length = " + c.length); print("d.length = " + d.length); a = d; print("a.length = " + a.length); // Nizovi prostih tipova: int[] e; // Null referenca int[] f = new int [5]; // Prosti tipovi unutar niza se // automatski inicijalizuju na nulu: print(f: " + Arrays.toString(f)); int[] g = new int [4]; for(int i = 0; i < g.length; i++) g[i] = i*i; int[] h = { 11, 47, 93 }; // Greka u prevoenju: promenljiva e nije inicijalizovana: //!print("e.length = " + e.length); print("f.length = " + f.length); print("g.length = print("h.length = e = h; print("e.length = e = new int[] { 1, print("e.length = " + g.length); + h.length); " + e.length); 2 }; " + e.length);

}
} /* Ispis: b: [null, null, nul1 , nul1, null] a.length = 2 b.length = 5 c.length = 4 d.length = 3 a.length = 3 f : [ 0 , 0, 0 , 0 , 0] f.length = 5 g.length = 4 h.length = 3 e.length = 3 e.length = 2

* ///:Niz a je neinicijalizovana lokalna p rom enljiva, a prevodilac spreava da se s to m referenco m u rad i bilo ta sve do k se ona p rav iln o ne inicijalizuje. Niz b je in icijalizovan tako da ukazuje na niz referenci tipa S fe ra O d B e riliju m a , ali se u taj niz nikada ne sm etaju objekti klase S fe ra O d B e riliju m a . M e u tim , uvek postoji m o g u n o st da p ro itate d u in u niza, p o to b ukazuje na postojei objekat. O vde se pojavljuje mali n ed o statak : ne m oete da sazn ate koliko se stv arn o elem en ata nalazi u nizu, p o to polje le n g th saoptava sam o koliko elem enata moc da stanc u niz, tj. kolika je d u in a niza, a ne koliko elem en ata sadri. M e u tim , kada se n aprav i niz, njegove reference se a u to m atsk i inicijalizuju vred n o u n u ll,

Poglavlje 16: Nizovi

597

pa m oete da u tv rd ite da li o d red en i eiem en t niza sadri o b jek at tako to p ro v erite da li je njegova v red n o st null. Slino to m e, niz p ro stih tipova se a u to m atsk i inicijalizuje vredn o u n u la za n u m erik e tipove, (char)O za tip char, o d n o sn o false za tip boolean. N iz c pokazuje kako se pravi objekat niza, n ak on ega sledi dod ela o bjekata klase SferaOdBerilijuina svim njegovim elem entim a. Niz d p o k azu je sintaksu agregatne inicijalizacije koja om ogu u je pravljenje objekta niza (im plicitno , p o m o u o p erato ra new, kao za niz c) i njegovo inicijalizovanje o b jektim a klase SferaOdBerilijuma, sve u je d n o j naredbi. Inicijalizacija sledeeg niza se m oe o zn aiti kao d in am i k a ag reg atn a inicijalizacija". A gregatna inicijalizacija koja je u p o tre b lje n a za n iz d m o ra se k o ristiti p rilik o m definicije niza, ali p o m o u d ru g e vrste zapisa m o g u e je n ap rav iti i inicijalizovati niz b ilo gde. Na p rim er, p re tp o sta v im o da m eto d a sakrij() k o risti niz o b jek ata klase SferaOdBerilijuma. M ogli biste je pozvati na sledei nain:
sakrij(d);

ali m o ete i d in am ik i da n a p ra v ite niz koji elite d a p ro sled ite k ao a rg u m e n t:


sakrij(new SferaOdBerilijuma[] new SferaOdBerilijuma() }); { new SferaOdBerilijuma(),

U m n o g im situ acijam a ovakav n ain pisan ja je p o g o d n iji. Izraz:


a=d;

p ok azu je kako se referenca koja je povezana s je d n im n izo m m oe d o d eliti d ru g o m nizu, ba kao to se m oe u ra d iti s bilo kojim d ru g im tip o m reference na o b jek at. Sada i a i d u k azu ju na isti niz. D rug i deo p ro g ram a M o g u n o stiN iz a .ja v a pok azuje da se nizovi p ro s tih tip o v a p onaaju isto kao i nizovi o b jekata, osim to nizovi p ro stih tip o v a d ire k tn o sad re v red n o sti p ro stih tipova. V eba 1: (2) N apravite m e to d u koja niz S fe ra O B e riliju m a p rim a kao arg u m e n t. Pozovite tu m e to d u tako to ete njen a rg u m e n t n ap rav iti d in am ik i. Pokaite da u to m sluaju o b in a ag reg atna inicijalizacija nizova ne radi. P ro n a ite jed in e situacije u kojim a o bina agreg atn a inicijalizacija nizova radi, i o n e gde je re d u n d a n tn a .

Vraanje niza vrednosti


P retp o sta v im o da piete m eto d u koja ne treb a da v rati je d n u v red n o st, ve vie njih. U jezieim a kao to su C i C + + to nije lako, zato to ne m o ete da v ra tite ceo niz ve sam o p o kaziva na niz. To stvara p ro b lem e jer po staje teko k o n tro lisa ti iv o tn i vek niza, to p ro u z ro k u je neefikasno korienje m em o rije. U Javi m oete da v ratite niz i nik ada ne m o rate da b rin e te o n jem u , je r e o n posto jati sve d o k v am je p o treb a n , a kada zavrite, o b risae ga saku plja sm ea.

598

Misliti na Javi

Kao p rim er, po g led ajm o kako se v raa niz e lem en ata tip a S trin g :
//: nizovi/Sladoled.java // Vraanje nizova iz metoda. import java.uti1 public class Sladoled { private static Random slucajan = new Random(47); static String[] UKUSI = { okolada", "jagoda", "vanila", "mentol", "moka", rum", "praline", "vona pita"

};
public static String[] skupUkusa(int n) { if (n > UKUSI.length); throw new IIlegalArgumentException("Zadat prevelik"); String[] rezultati = new String[n]; boolean[] izabran = new boolean[UKUSI.length]; for (int i = 0; i < n; i++) { int t; do t = slucajan.nextInt(UKUSI.length); while (izabran [t]); rezultati[i] = UKUSI[t]; izabran[t] = true;

}
return rezultati;

}
public static void main(String[] args) { for(int i = 0; i < 7; i++) { System.out.pri ntl n(Arrays.toString(skupUkusa(3)));

}
} /* Ispis: [rum, mentol, moka] [okolada, jagoda, moka] [jagoda, mentol, moka] [rum, vanila, vona pita] [vanila, okolada, moka] [praline, jagoda, moka] [moka, jagoda, mentol]

* ///:M etoda skupUkusa() pravi niz elem en a ta tipa String nazvan rezultati. D uin a niza je od re en a a rg u m e n to m koji se p ro sle u je m eto d i. N akon toga, n asu m in o se biraju ukusi iz niza UKUSI i stavljaju u niz rezultati koji se vraa kao rezultat m eto d e. V raanje niza je isto kao vraanje bilo kog o b jek ta - rad i se o referenci. Nije v ano da li je niz napravljen u n u ta r m eto d e skupUkusa() ili na bilo k o m d ru g o m m estu. S kuplja sm ea se b rin e o b risan ju niza kada se zavri ra d s n jim , tj. niz p osto ji sve d o k vam je p o treb an .

Poglavlje 16: Nizovi

599

Uzgred, dok skupUkusa() nasum ino bira ukuse, obezbeuje da isti elem ent ne bude izabran dvaput. To se radi u petlji do koja bira nasum ino sve d ok ne pronae neku vrednost to se jo ne nalazi u nizu izabran. (Naravno, moglo je da se obavi i poreenje vrednosti objekata klase String da bi se ustanovilo da li se izabrani elem ent ve nalazi u nizu rezultati.) Ako pronae odgovarajui element, dodaje nov ukus i trai sledei (i se poveava za jedan). Iz rezultata vidite da skupUkusa() svaki p u t bira ukuse sluajnim redosledom.

Veba 2: (1) Napiite m etodu koja prim a int argum ent i vraa niz te veliine, popunjen objektim a SferaOdBerilijuma.

Viedimenzionalni nizovi
Lako je praviti viedim enzionalne nizove. Za viedim enzionalni niz prostih tipova, svaki vektor niza piete u n u tar vitiastih zagrada:
//: ni zovi/Vi sedimenzionalni Ni zovi Prosti h .java // Pravljenje viedimenzionalnih nizova. import java.util public class VisedimenzionalniNizoviProstih { public static void main(String[] args) { int[] [] a = {
{ 1, 2, 3, },
{ 4, 5, 6, },

};
System.out.pri ntln(Arrays.deepToStri ng(a));

}
} /* Ispis:
[[1, 2, 3], [4, 5, 6]]

* ///:Sa svakim ugnedenim skupom vitiastih zagrada prelazite na sledei nivo niza. U ovom prim eru upotrebljena je m etoda A rrays.deepT oString() Jave SE5, koja viedim enzionalne nizove pretvara u znakovne nizove (objekte tipa String), kao to vidite iz rezultata. Niz moete napraviti i pom ou rezervisane rei nevv. Evo jednog trodim enzionalnog niza napravljenog u izrazu new:
//: nizovi/TriDPomocuNew.java import java.util.*; public class TriDPomocuNew { public static void main(String[] args) { // 3-D niz zadate duine: int[] [] [] a = new i nt[2] [2] [4]; System.out.pri ntln(Arrays.deepToStri ng(a));

}
} /* Ispis:

60 0

Misliti na Javi

[[[0, 0, 0, 0],

[0, 0, 0, 0]],

[[0, 0, 0, 0],

[0, 0, 0, 0]]]

* ///:-

Vidite da se nizovi prostih tipova inicijalizuju autom atski, ukoliko im inicijalizacijsku vrednost ne zadate eksplicitno. Nizovi objekata se inicijalizuju na null. Svaki vektor nizova koji sainjavaju m atricu m oe biti proizvoljne duine (naziva se: nepravilan, neregularan ili n ep otpu n niz):
//: nizovi/NepravilanNiz.java import j a v a . u t i l .*; public class NepravilanNiz { public static void main(String[] args) { Random slucajan = new Random(47); // 3-D niz s vektorima razliite duine: int[][][] a = new int[slucajan.nextlnt(7)] [] []; for(int i = 0; i < a.length; i++) { a[i] = new int[slucajan.nextlnt(5)] []; for(int j = 0; j < a [ i ] .1e n g t h ; j++) a[i][j] = new in t[ sl uc ajan.nextlnt(5)];

}
Sy st em .o u t .pri nt ln ( A r r a y s .deepToStri n g (a));

}
} /* Ispis: [[]. [[0], [0], [0, 0, 0, 0]], [[0, 0, 0], [[], [0, 0], [0], [0, 0]], []], [[0], [[0, 0, 0], [], [0]]] [0], [0, 0, 0, 0]], [0, 0, 0],

* ///:Prvi new pravi niz iji je prvi elem ent nasum ino odabrane duine, a ostali elem enti neodreene duine. Drugi new unutranje petlje for popunjava elemente, ali trei indeks ostavlja kao neodreen do treeg new. Na isti nain m oete praviti nizove neprostih objekata. Ovde ete videti kako sm o vie new izraza okupili u n u tar vitiastih zagrada:
//: nizovi/ V i sedimenzionalniNizoviObjekata.java import j a v a . u t i l .*; public class Vi sedimenzionalniNizoviObjekata { public static void main(String[] SferaOdBeri 1 ijuma[] [] sfere = { { new Sfer aO dB er i1 i j u m a ( ) , new SferaOdBerilijuma() { new Sfer aO dB er i1ij u m a ( ) , new Sf eraOdBeri1 ij um a( ), new Sfer aO dB er i1 ij u m a ( ) , new Sf er aO dB er il ij um a() }, { new Sfer aO dB er i1 ij u m a ( ) , new SferaOdB er il ij um a( ), new Sf er aO dB er il ij um a( ), new Sf er aOdBeri1i j um a( ), new SferaOdBerilijuma(), new SferaOdB er il ij um af ), new Sfer aO dB er i1 i j u m a ( ) , new SferaOdBerilijuma() }, }, args) {

};
Sy st em .o ut .p ri nt ln (Ar ra ys .d ee pT oS tr in g( sfe re ));

Poglavlje 16: Nizovi

601

} /* Ispis: [[Sfera 0, Sfera 1], [Sfera 2, Sfera 3, Sfera 4, Sfera 5], [Sfera 6, Sfera 7, Sfera 8, Sfera 9, Sfera 10, Sfera 11, Sfera 12, Sfera 13]]

* ///:Vidite da su i sfere nepravilan niz, u kojem su duine svih lista objekata razliite. Autom atsko pakovanje radi i sa inicijalizatorim a nizova:
//: ni zo vi/AutomatskoPakovanjeNizova.java import ja v a . u t i l .*; public class Au tomatskoPakovanjeNizova { public static void main(String[] args) { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }, { 21, 22, 23, 24, 25, 26, 27, 28, 29, 30 }, { 51, 52, 53, 54, 55, 56, 57, 58, 59, 60 }, { 71, 72, 73, 74, 75, 76, 77, 78, 79, 80 }, { Integer[] [] a = { // Automatsko pakovanje:

};
Sy st em .o ut .p ri nt ln (Ar ra ys .d ee pT oS tr in g( a));

}
} /* Ispis: [[1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 27, 28, 29, 30], [21, 22, 23, 24, 25, , '' G , [51, 52, 53, 54, 55, 56, 57, 58, 59, 60],

[71, 72, 73, 74, 75, 76, 77, 78, 79, 80]]

* ///:Evo kako se pare po pare pravi niz neprostih objekata:


//: nizovi/SastavljanjeVisedimenzionalnihNizova.java // Pravljenje viedimenzionalnih nizova. import java.uti1 .*; public class SastavljanjeVisedimenzionalnihNizova { public static void main(String[] args) { Integer[] [] a; a = new Integer[3] []; for(int i = 0; i < a.length; i++) { a[i] = new Integer[3]; for(int j = 0; j < a[i].1ength; j++) a [i] [J] = i * j; // Automatsko pakovanje

}
System.out.pri ntln(Arrays.deepToStri ng(a));

}
} /* Ispis:
[[0, 0, 0], [0, 1, 2], [0, 2, 4]]

* ///:)n o i*j slui samo za to da se neto zanimljivo upie u taj Integer.

602

Misliti na Javi

M etoda A rrays.deepT oString() radi i sa nizovima prostih i sa nizovim a objekata:


//: nizovi/VisedimNizOmotaca.java // Viedimenzionalni nizovi import j a v a . u t i l .*; public class VisedimNizOmotaca { public static void main(String[] args) { 1, 2, 3, }, { 4, 5 , 6, }, { Integer[][] al = { // Automa ts ko pakovanje "omotakih" objekata.

};
Double[][][] a2 = { // Automatsko pakovanje { { 1.1, 2.2 }, { 3.3, 4.4 } }, { { 5.5, 6.6 }, { 7.7, 8.8 } }, { { 9.9, 1.2 }, { 2.3, 3.4 } },

};
String[] [] a3 = { { "The", "Quick", " S l y \ "Over" }, "Dog", "and", "friend" }, "Fox" }, { "Jumped",

{ "The", "Lazy , "Brown",

};
System.out.println("al: System.out.println("a2: System.out.println("a3: " + Arrays.dee pT oS tr in g(a l)); " + A r r a y s .deepToString(a2)); " + A r r a y s .deepToString(a3));

}
} /* Ispis: al: [[1, 2, 3], [4, 5, 6]] [3.3, 4 . 4 ] ] , [[5.5, 6.6], [7.7, 8.8]], [[9.9, 1.2], a 2 : [[[1.1, 2.2], [2.3, 3.4]]] a3: [[The, Quick, Sly, F o x ] , [Jumped, O v e r ] , [The, Lazy, Brown, Dog, and, friend]]

* ///:-

I opet, u Integer i Double nizovima, autom atsko pakovanje Jave SE5 pravi om otake objekte um esto nas. Veba 3: (4) Napiite m etodu koja pravi i inicijalizuje dvodim enzionalni niz tipa double.
Veliinu niza ocireduju argum enti m etode, a inicijalizaone vrednosti su date opsegom o dredenim poetnom i zavrnom vrednou koje su i argum enti te metode. Napravite drugu m etodu koja ispisuje niz generisan prvom m etodom . U m etodi main() testirajte m etode pravljenjem i ispisivanjem nekoliko nizova razliitih veliina.

Veba 4: (2) Ponovite p rethodnu vebu za trodim enzionalni niz. Veba 5: (1) Pokaite da se viedim enzionalni nizovi neprostih objekata autom atski inicijalizuju na null. Veba 6: (1) Napiite m etodu koja prim a dva int argum enta koji pokazuju odgovarajue dim enzije 2-D niza. M etoda treba da napravi i popuni 2-D niz SferaOdBerilijuma u skladu sa argum entim a dimenzija. Veba 7: (1) Ponovite p rethodnu vebu za 3-D niz.

Poglavlje 16: Nizovi

603

Nizovi i generiki tipovi


Po pravilu, nizovi i generiki tipovi ne idu zajeno. Nije m ogue napraviti instancu niza param etrizovanih tipova:
1justi<Banana>[] ljusti = new 1ju st i< Ba na na >[ 10 ]; // Nedozvoljeno

Brisanjem se uklanja podatak o param etru tipa, a niz m o ra da zna taan tip koji sadri, da bi rad s tipovim a proveravanjem uinio bezbednim . M eutim , m oete param etrizovati tip sam og niza:
//: nizovi/ParametrizovanTipNiza.java class ClassParametar<T> { public T[] f ( T [] arg) { return arg; }

}
class ParametarMetode { public static <T> T[] f(T[] arg) { return arg; }

}
public class ParametrizovanTipNiza { public static void main(String[] Integer[] Integer[] Double[] args) { celib = { 1, 2, 3, 4, 5 }; celib2 = bdouble2 =

Double[] bdouble = { 1.1, 2.2, 3.3, 4.4, 5.5 }; new Cl as sP ar am et er<Integer>().f(celib); new C1 assParameter<Double > ( ) .f (bdouble); celib2 = MethodParameter.f(celib); bdouble2 = Me th odParameter.f (bdouble);

} } ///:Vodite rauna o pogodnosti upotrebe param etrizovane m etode um esto param etrizovane klase: ne m orate da pravite instancu klase s param etrom za sve razliite tipove na koje ete je prim eniti, i moete da je napravite statinom . N aravno, ne m oete uvek da upotrebite param etrizovanu m etodu um esto param etrizovane klase, ali to bi m oglo da bude bolje reenje. Nije najtanije rei da je nem ogue napraviti niz generikog tipa. Istina je da prevodilac nee dozvoliti pravljenje instance niza generikog tipa. M eutim , dopustie da napravite referencu na taj niz. Na primer:
List<String>[] ls;

604

Misliti na Javi

Ovo e proi kroz prevodilac bez problem a. I m ada ne moete napraviti objekat niza koji sadri generiki tip, moete napraviti niz negenerikog tipa i eksplicitno ga konvertovati:
//: ni zovi/Ni zGeneri cki hTi p o v a .java // Mogue je napraviti niz generikog tipa. import java.util public class NizGenerickihTipova { @SuppressWarnings("unchecked") public static void main(String[] List<String>[] List[] ls; la = new Li st [10]; args) {

Is = (List<String>[])la; // Up ozorenje "Unchecked" 1 s [0] = new Ar ra yL is t < S t r i n g > ( ) ; // Provera u vreme prevoenja daje greku: //! 1 s [1] = new A r r a y L i s t < I n t e g e r > ( ) ; // Problem: List<String> je podtip klase Object Object[] objekti = Is; // Dakle, do deljivanje je OK // Prevodi se i izvrava bez pritube: o b j e k t i [1] = new Ar ra yL i s t < I n t e g e r > ( ) ; // Meutim, za jednostavne potrebe mogue je // napraviti niz generikih tipova, iako // e se javiti upozorenje "unchecked": List<SferaOdBerilijuma>[] sfere = (L ist<SferaOdBerilijuma>[])new List for(int i = 0; i < sfere.length; i++) sfere[i] = new ArrayList<SferaOdBerilijuma>();

[10] ;

} } ///: im im ate referencu na L ist< S tring > [], neke provere e se obaviti u vreme prevoenja. Problem je to to su nizovi kovarijantni, pa je niz L ist< S tring> [] istovrem eno i niz O bject]]. To moete upotrebiti za dodeljivanje liste A rrayL ist< Integer> svom nizu, a da ne izazovete greku ni u vreme prevoenja ni u vrem e izvravanja. Ukoliko znate da neete svoditi navie i potrebe su vam relativno jednostavne, lako ete napraviti niz generikih tipova koji e biti elem entarno proveravan u vreme prevoenja. M eutim gotovo uvek je bolje izabrati generiki kontejner nego niz generikih tipova. Po pravilu, videete da su generiki tipovi delotvorni na granicam a klase ili metode. U njenoj unutranjosti, brisanje ini generiki tip neupotrebljivim . Stoga ne moete, na prim er, napraviti niz generikog tipa:
/ / : nizovi/NizGenerickogTipa.java // Java nee da prevede nizove generikih tipova. public class NizGenerickogTipa<T> { T [] niz; // 0K @SuppressWarni ngs("unchecked") public Array0fGenericType(int velicina) {

Pogiavlje 16: Nizovi

605

//! niz = new T[velicina]; // Nedozvoljeno niz = (T[])new O b j e c t [ v e l i c i n a ] ; // Upozorenje "unchecked"

}
// Nedozvoljeno: //! public <U> U[] napraviNiz() { return new U [10]; }

} lll-~ Brisanje ponovo sm eta - u ovom prim eru pokuali smo da napravim o nizove tipova koji su bili obrisani i stoga su nepoznati. Im ajte u vidu da niz tipa Object m oete napraviti i eksplicitno konvertovati, ali bez anotacije @SuppressWarnings dobili biste upozorenje ,,unchecked u vreme prevoenja, zato to niz niti sadri tip T niti se dinam iki proverava na tip T. D rugim reim a,ako napravite String[], Java e se i u vrem eprevoenja i u vreme izvravanja postarati da u taj niz m oete sm etati sam o objekte tipa String. M eutim , ukoliko napravite niz Object[], u njega ete m oi da sm etate sve sem prostih tipova.

Veba 8: (1) Dokaite tvrdnje iz preth od no g pasusa. Veba 9: (3) N apravite klase po trebn e za prim er ljusti<Banana> i pokaite da ih prevodilac ne prihvata. Reite problem tako to ete napraviti o b ji' at tipa ArrayList. Veba 10: (2) Izm enite NizGenerickihTipova.java tako da se us. . sto nizova koriste kontejneri. Pokaite da se tako gube i upozorenja u vrem e prevodenja.

Pravljenje podataka za testiranje


Prilikom eksperim entisanja s nizovim a i program im a uopte, odesno je imati mogunost lakog generisanja nizova po punjenih podacim a za testirasr>e. Alatke opisane u ovom odeljku popunjavaju niz vrednostim a ili objektim a.

Arrays.fill()
Standardna klasa A rrays u Javi im a svoju m etodu fill(), ali je ona prilino prosta - samo kopira jednu vrednost u svaki elem ent niza, a ako se radi o objektim a, kopira istu referencu u svaku lokaciju. Evo prim era:
//: nizovi/PopunjavanjeNizova.java // Upotreba metode Arrays.fill() import ja v a . u t i 1 import static n e t . m i n d v i e w . u t i l .Print.*; public class PopunjavanjeNizova { public static void main(String[] int velicina = 6; booleanf] byte[] al = new bo ole a n [velicin a ] ; a2 = new byte[vel i c i n a ] ; args) {

char[] a3 = new char[vel i c i n a ] ; short[] a4 = new short [vel i c i n a ] ; int[] a5 = new int[vel i c i n a ] ; 1ong [] a6 = new 1 ong [vel i ci n a ] ;

606

Misliti na Javi

float[] a7 = new float[vel i c in a]; double[] a8 = new double[vel i c in a]; String[] a9 = new Stri ng [v el ic in a]; Arra ys .f i1 1 (al, true); printO'al = " + Ar ra ys .t oS tr in g( al )); Arrays.fil1(a2, Arrays.fill(a3, Arrays.fill(a4, (byte)ll); x'); (short)17); print("a2 = " + Ar ra ys .t oS tr in g( a2 )); print("a3 = 1 1 + Ar ra ys .t oS tr in g( a3 )); print("a4 = + A r ra ys .t oS tr in g( a4 )); Arrays.fill(a5, 19); print("a5 = " + A r r a y s .t oS tr in g( a5 )); Arrays.fill(a6, 23); print("a6 = " + A r ra ys .t oS tr in g( a6 )); Arrays.fill(a7, 29); print("a7 = " + A r r a y s .t oS tr in g( a7 )); A r r a ys .f i1 1 (a8, 47); print("a8 = 1 1 + A r ra ys .t oS tr in g( a8 )); Arra ys .f i1 1 (a9, "Zdravo"); print("a9 = 1 1 + A r r a y s .t oS tr in g( a9 )); // Manipulisanje opsezima: Arrays.fill(a9, 3, 5, "svima"); print("a9 = " + A r r a y s .toStri ng (a 9));

}
} /* Ispis: al = [true, true, true, true, true, true] a2 = [11, 11, 11, 11, 11, 11] a3 = [x, x, x, x, x, x] a4 = [17, 17, 17, 17, 17, 17] a5 = [19, 19, 19, 19, 19, 19] a6 = [23, 23, 23, 23, 23, 23] a7 = [29.0, 29.0, 29.0, 29.0, 29.0, 29.0] a8 = [47.0, 47.0, 47.0, 47.0, 47.0, 47.0] a9 = [Zdravo, Zdravo, Zdravo, Zdravo, Zdravo, Zdravo] a9 = [Zdravo, Zdravo, Zdravo, svima, svima, Zdravo]

* ///:Moete da popunite ceo niz ili, kao to pokazuju poslednje naredbe, nekoliko elemenata niza. M eutim , poto m etodom Arrays.fill() m oete da popunite niz samo jednom vrednou, rezultati nisu naroito upotrebljivi.

Generatori podataka
Za pravljenje zanimljivijih nizova podataka, ali na fleksibilan nain, upotrebiem o koncept G en erato ra predstavljen u poglavlju Generiki tipovi. Ako neka alatka upotrebljava G enerator, napravljeni podaci se menjaju u zavisnosti od izabranog G en erato ra (to je

Poglavjje 16: Nizovi

607

prim er projektnog obrasca Strategy (Strategija) - razliiti G eneratori predstavljaju razliite strategije1)U ovom odeljku napraviem o nekoliko Generatora; kao to ste ve videli, lako ete definisati sopstvene. Prvo, napraviem o osnovni skup generatora za brojanje svih om otaa prostih tipova i objekata tipa String. Klase generatora su ugneene u klasi GeneratorDateKoIicine da bi mogle biti nazvane im enom tipa objekata koje generiu; na prim er, generator koji pravi objekte tipa Integer pravim o izrazom new GeneratorDateKoIicine.Integer():
/ / : net/mi nd view/uti1/ G e n er at or Da te Ko licine.java // Jednostavne realizacije generatora. package net.mindview.util; public class GeneratorDateKolicine { public static class Boolean implements Ge ne ra to r< java.lang.Boolean> { private boolean vrednost = false; public java.lang.Boolean next() return vrednost; { vrednost = Ivrednost; // Napred - nazad

) }
public static class Byte implements Ge ne ra to r< ja va .1a n g .Byte> { private byte vrednost = 0; public java.lang.Byte next() { return vrednost++; }

}
static char[] znakovi = ("abcdefghijklmnopqrstuvwxyz" + "A BC DE FG HI JK LM NO PQ RST UV WX YZ ") .t oC ha rA rr ay( ); public static class Character implements Ge ne ra to r< ja va .l an g.C ha ra ct er > { int indeks = -1; public java.lang.Character next() return znakovi [indeks]; { indeks = (indeks + 1) % znakovi.length;

} }
public static class String implements Generator<ja va .l an g.S tr in g> { private int duzina = 7; Ge nerator<java.lang.Character> cg = new Character(); public String() {} { this.duzina = duzina; { } public String(int duzina) char[]

public java.lang.String next() for(int i = 0; i < duzina;

baf = new ch a r [ d u z i n a ] ; i++)

lako to nije ba sasvim jasno. M oglo bi sc tvrditi i da G e n e ra to r predstavlja obrazac C otntnand (Ko m anda). M edu tim , ja sm atram da je zadatak p o p u n iti niz i da G e n e ra to r obavlja deo tog zadatka, pa irii to vie lii na strategiju nego na kom andu.

608

Misliti na Javi

baf[i] = cg.next(); return new java .l an g. St ri ng (b af);

} }
public static class Short implements Generator<java.lang.Short> { private short vrednost = 0; public java.lang.Short next() { return vrednost++; }

}
public static class Integer implements Ge ne rator<java.1ang.Integer> { private int vrednost = 0; public java.lang.Integer next() { return vrednost++; }

}
public static class Long implements Generator<java.lang.Long> { private long vrednost = 0; public java.lang.Long next() { return vrednost++; }

}
public static class Float implements Generator<java.lang.Float> { private float vrednost = 0; public java.lang.Float next() float rezultat = vrednost; vrednost += 1.0; return rezultat; {

} }
public static class Double implements Genera to r< ja va .1ang.Double> { private double vrednost = 0.0; public java.lang.Double next() double rezultat = vrednost; vrednost += 1.0; return rezultat; {

} } } ///:Svaka klasa realizuje neko znaenje pojm a koliina. U sluaju klase GeneratorDateKoIicine.Character, radi se o velikim i m alim slovima koja se ponavljaju. Klasa GeneratorD ateK olicine.String upotrebljava GeneratorDateKolicine.Character za popunjavanje niza znakova, koje zatim pretvara u String. Veliinu niza odreuje argum ent konstruktora. O bratite panju na to da GeneratorDateKolicine.String upotrebljava elem entarni Generator<java.lang.Character> um esto specifine reference na GeneratorDateKolicine.Character. Kasnije taj generator moe zam eniti RandoinGenerator.String iz paketa RandomGenerator.java.

Poglav[je 16: Nizovi

609

Evo jedne alatke za testiranje koja upotrebljava refleksiju su agneenim Generatoro m , p a se m oe p rim eniti za testiranje proizvoljnog skupa Geireratora ovog oblika:
//: nizovi/TestiranjeGeneratora.java import net.mindview.util.*; public class TestiranjeGeneratora { public static int velicina = 10; public static void test(Class<?> okolnaKlasa) for(Class<?> tip : o k o l n a K l a s a . g e t C l a s s e s O ) try { Generator<?> g = (Generator<?>)tip.newInstance(); for(int i = 0; i < velicina; i++) System.out.printf (g.next() + 1 1 ); System .o ut .p ri nt ln (); } catch(Exception e) { throw new Ru nt im eE xc ep ti on (e ); { {

System.out.print(tip.getSimpleName() + ": ");

} } }
public static void main(String[] args) { te st (G en er at or Da te Kol ic in e. cl as s);

}
} /* Ispis: Double: 0.0 1.0 2.0 3.0 4.0 5.0 6.0 7.0 8.0 9.0 Float: 0.0 1.0 2.0 3.0 4.0 5.0 6.0 7.0 8.0 9.0 Long: 0 1 2 3 4 5 6 7 8 9 Integer: 0 1 2 3 4 5 6 7 8 9 Short: 0 1 2 3 4 5 6 7 8 9 String: abcdefg hijklmn opqrstu vwxyzAB CDEFGHI JKLMN0P QRSTUVW XVZabcd efghijk lmnopqr Character: a b c d e f g h i j Byte: 0 1 2 3 4 5 6 7 8 9 Boolean: true false true false true false true false true false

* ///:Ovde se pretpostavlja da klasa koja se ispituje sadri skup ugneenih objekata tipa Generator, od kojih svaki ima podrazumevani konstruktor (onaj bez argum enata). Sve ugneene klase proizvodi reflektivna metoda getClasses(). Zatim metoda test() pravi instancu svakog od tih generatora i ispisuje rezultat koji se dobija s deset poziva m etode next(). Evo skupa G eneratora koji upotrebljavaju generator sluajnih brojeva. Poto se konstru k to r klase R andom inicijalizuje konstantom , rezultat se ponavlja svaki p u t kada pom ou jednog od ovih G eneratora pozovete program :
//: net/mindview/util/GeneratorSlucajnih.java // Generatori koji daju sluajne vr e d n o s t i . package net.mindview.util; import j a v a . u t i l .*;

610

Misliti na Javi

public class GeneratorSlucajnih { private static Random r = new Random(47); public static class Boolean implements Ge ne ra to r< ja va .l an g.B oo 1e an > { public java.lang.Boolean next() return r. ne xt Bo ol ea n( ); {

1
}
public static class Byte implements Ge ne ra to r< ja va .l an g.B yt e> { public java.lang.Byte next() return (byte)r.nextlnt(); {

} }
public static class Character implements Ge ne ra to r< java.lang.Character> { public java.lang.Character next() { return G e n e ra to rD at eK ol ic ine .z na ko vi[ r.nextInt(Generato rDa te Ko li ci ne .z na ko vi .du zi na )];

} }
public static class String extends GeneratorDateKolicine.String // Ukljuiemo generator sluajnih znakova: { cg = new Character(); public S t r i n g O {} { su pe r ( d u z i n a ) ; } } // Inicijalizator instance

public String(int duzina)

}
public static class Short implements Ge ne ra to r<java.lang.Short> { public java.lang.Short next() return (s ho rt)r.nextInt(); {

} }
public static class Integer implements G e n e r a to r< ja va .1a n g .Integer> { private int mod = 10000; public Integer() {} { mod = modulo; { } public Integer(int modulo) return r. ne xt In t( mo d);

public ja va.lang.Integer next()

} }
public static class Long implements Ge ne ra to r< ja va .l an g.L on g> { private int mod = 10000; public Long() {} { mod = modulo; { } public Long(int modulo)

public java.lang.Long next()

Poglavlje 1 6: Nizovi

611

return new ja va .1 an g. Lo ng (r .n ext In t( mo d) );

} }
public static class Float implements Generator<java.lang.Float> { public java.lang.Float next() { / / Odseci sva decimalna mesta posle drugog: int odseceno = Math.round(r.nextFloat() * 100); return ((float)odseceno) / 100;

} }
public static class Double implements Ge nerator<java.lang.Double> { public ja va .lang.Double next() { long odseceno = Math.round(r.nextDouble() * 100); return ( (double)odseceno) / 100;

} } } ///= Vidite da GeneratorSlucajnih.String nasleduje GeneratorDateKolicine.String i jednostavno ukljuuje Character generator. Za generisanje brojeva koji nisu preveliki, G eneratorSlucajnih.Integer koristi standardn u vrednost m odula 10.000, ali prekjopljeni konstruktor om oguuje i izbor m anje vrednosti. Isti pristup je upotrebljen i za GeneratorSIucajnih.Long. Za Generatore Float i Double, vrednosti iza decim alne take se odsecaju. Za testiranje klase GeneratorSIucajnih ponovo emo upotrebiti TestiranjeGeneratora:
//: ni zo vi /TestiranjeGeneratoraSlucajnih.java import net.mindview.util.*; public class TestiranjeGeneratoraSlucajnih { public static void main(String[] args) {

Testi ra nj eG en er at or a.test(GeneratorSlucajnih.class);

}
} /* Ispis: Double: 0.73 0.53 0.16 0.19 0.52 0.27 0.26 0.05 0.8 0.76 Float: 0.53 0.16 0.53 0.4 0.49 0.25 0.8 0.11 0.02 0.8 L o n g : 7674 8804 8950 7826 4322 896 8033 2984 2344 5810 Integer: 8303 3141 7138 6012 9966 8689 7185 6992 5746 3976 S h o r t : 3358 20592 284 26791 12834 -8092 13656 29324 -1423 5327 String: bklnaMe sbtWHkj UrlikZPg wsqPzDy CyRFJQA HxxHvHq XumcXZJ oogoYWM NvqeuTp nXsgqia Character: x x E A J J m z M s Byte: -60 -17 55 -14 -5 115 39 -37 79 115 Boolean: false true false false true true true true true true

* ///:-

612

Misliti na Javi

Broj ispitnih vrednosti odreuje javno polje TestiranjeGeneratora.velicina koje m oete menjati.

Pravljenje nizova od Generatora


Za pravljenje niza od Generatora po treb ne su nam dve alatke za konverziju. Prva pom ou bilo kojeg G eneratora pravi niz podtipova klase Object. Da bism o reili problem prostih tipova, druga alatka prim a proizvoljan niz om otaa prostih tipova i proizvodi odgovarajui niz prostih tipova. Prva alatka im a dve opcije koje predstavlja preklopljena statina m etoda array(). Prva verzija m etode prim a postojei niz i popunjava ga G eneratorom , a druga uzim a jedan objekat tipa Class, jedan G enerator i eljeni broj elem enata, i pravi nov niz koji se opet popunjava Generatorom. Vodite rauna o tom e da ova alatka proizvodi sam o nizove podtipova klase Object i da ne m oe da pravi nizove prostih tipova:
//: net/mindview/util/Generisani.java package net.mindview.util; import java.util public class Generisani { a, Ge nerator<T> gen) {

// Popunjavanje postojeeg niza: public static <T> T[] niz(T[] return new Coll ec ti on Da ta <T >( gen, a. du zi n a ) , t o A r r a y ( a ) ;

)
// Napravi nov niz: @SuppressWarnings("unchecked") public static <T> T[] niz(Class<T> tip, Generator<T> gen, int velicina) { T[] a = (T[])ja va .l an g. re fl ec t.A r r a y .n e w l ns ta nc e( tip, v e l i c i n a ) ; return new C o l1e c t i on Da ta <T >( ge n, v e l i c i n a ) .t o A r r a y ( a ) ;

} } ///:D efinija klase C ollectionD ata navedena je u poglavlju D etaljno razm atranje kontejnera. O na pravi Collection objekat popunjen elem entim a koje je napravio G en erato r gen. Broj tih elemenata odreuje drugi argum ent konstruktora. Svi podtipovi klase Collection (kontejneri) imaju m etodu toA rray() koja elem entim a kontejnera popunjava niz zaat argum entom . Druga m etoda upotrebljava refleksiju za dinam iko pravljenje novog niza odgovarajueg tipa i veliine. On se zatim popunjava isto kao 11 prvoj m etodi. Klasu G enerisani m oemo ispitati nekom od klasa G en eratorD ateK olicine definisanih u prethodnom odeljku:
//: ni zo vi /T estGenerisanih.java import j a v a . u t i l .*; import net.mindview.util.*;

Poglavlje 16: Nizovi

613

public class TestGenerisanih { public static void main(String[] Integer[] a = { 9, 8, 7, 6 }; System .o ut .p ri nt ln (Ar ra ys .t oS tr in g( a) ); a = Ge nerisani.array(a,new GeneratorDateKolicine.Integer()); System .o ut .p ri nt ln (Ar ra ys .t oS tr in g( a) ); Integer[] b = Ge nerisani.array(Integer.class, new GeneratorDateKolicine.Integer(), System.out.print ln (Ar ra ys .t oS tr in g( b)); 15); args) {

}
} /* Ispis: [9, 8, 7, 6] [0, 1, 2, 3] [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]

* ///:Iako je niz a inicijalizovan, njegove vrednosti nestaju kada se propusti kroz m etodu G enerisani.array () koja ih zam enjuje (ali ne m enja prvobitno m esto niza u m em oriji). Inicijalizacija niza b pokazuje kako se popunjen niz pravi od nule. G eneriki tipovi ne rade s prostim tipovim a, a m i generatore hoem o da upotrebim o za popunjavanje nizova prostih tipova. Problem sm o reili tak to sm o napravili konvertor koji p rim a proizvoljan niz om otakih objekata i konvertuje ga u niz odgovarajuih prostih tipova. Da nem a te alatke, m orali bism o da pravim o zaseban generator za svaki prost tip.
/ / : net/mi ndview /u ti1/K onvertujU.java package net.mindview.util; public class KonvertujU { public static boolean[] boolean[] p r o s t ( B o o l e a n [] ulaz) i++) { rezultat = new bo ol ea n [ u l a z . d u z i n a ] ;

for(int i = 0; i < ulaz.duzina; return rezultat;

rezultat[i] = ulaz[i]; // Automatsko raspakivanje

}
public static char[] prost(Character[] char[] for(int i = 0; i < ulaz.duzina; rezultat[i] = u l a z [ i ] ; return rezultat; 1++) ulaz) { rezultat = new ch ar [ u l a z . d u z i n a ] ;

}
public static byte[] prost(Byte[] ulaz) byte[] for(int i = 0; i < ulaz.duzina; i++) rezultat[i] = u l a z [ i ] ; return rezultat; { rezultat = new by te [ u l a z . d u z i n a ] ;

}
public static short[] prost(Short[] ulaz) {

61 4

Misliti na Javi

short[]

rezultat = new sh or t[ ul az .d uz in a];

for(int i = 0; i < ulaz.duzina; i++) rezultat[i] = ul a z [ i ] ; return rezultat;

}
public static int[] prost(Integer[] ulaz) int[] rezultat = new in t[ ul az .d uz in a]; for(int i = 0; i < ulaz.duzina; i++) rezultat[i] = u l az [i ]; return rezultat; {

}
public static long[] prost(Long[] ulaz) { long[] rezultat = new long[ula z. du zi na ]; for(int i = 0; i < ulaz.duzina; i++) rezultat[i] = u l az [i ]; return rezultat;

}
public static float[] prost(Float[] ulaz) float[] for(int i = 0; i < ulaz.duzina; i++) rezultat[i] = u l a z [i]; return rezultat; { rezultat = new floa t[ ul az .d uz in a];

}
public static double[] double[] prost(Double[] i++) ulaz) { rezultat = new d o u b le [u la z. du zi na ];

for(int i = 0; i < ulaz.duzina; rezul tat[i] = ul a z [ i ] ; return rezultat;

}
} lll-~

Svaka verzija m etode prost() pravi odgovarajui niz prostih tipova tane duine i /a tim u njega kopira elemente niza om otakih tipova ulaz. O bratite panju na to da se autom atsko raspakivanje deava u izrazu:
rezultat[i] = ul a z [ i ] ;

Evo prim era iz kojega moete videti kako se klasa K onvertujU upotrebljava sa obe verzije m etode G enerisani.array():
//: nizovi/PrimerKonverzijePr os tih.java import java.util .*; import net.mindview.util.*; public class PrimerKonverzijeProstih { public static void main(String[] args) { Integer[] a = Generisani.niz(Integer.class, new Ge ne ra to rD ateKolicine.Integer(), 15); int[] b = Ko nvertujU.prost(a);

Poglav[je 16: Nizovi

615

S y st em .o ut .println(Arrays.toString(b)); boolean[] c = KonvertujU.prost( Generisani.niz(Boolean.class, new GeneratorDateK ol ic ine .B oo le an (), 7)); S y st em .o ut.pri ntln (A rr ay s.toStri n g (c ));

}
} /* Ispis:

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]


[true, false, true, false, true, false, true]

* ///:Najzad, ovo je program za testiranje alatki za generisanje nizova p om ou klasa G eneratorSlucajnili:


//: nizovi/TestGenerisanjaNizova.java // Testiranje alatki import ja v a . u t i l .*; import ne t. mi nd vi ew .u ti l.*; import static ne t. mi nd vi ew .u ti l.Print.*; public class TestGenerisanjaNizova { public static void main(String[] args) int velicina = 6; boolean[] al = KonvertujU.prost(Generisani,niz( Boolean.class, new G e ne ra to rS lu ca jn ih .Bo ol ea n( ), velicina)); print("al = " + Arrays .t oS tr in g( al )); byte[] a2 = KonvertujU.prost(Generisani.niz( Byte.class, new Genera to rS lu ca jn ih .By te (), velicina)); print("a2 = " + Arrays.toString(a2)); char[] a3 = KonvertujU.prost(Generisani.niz( Character.class, new Ge ne ra to rS lu ca jn ih .Ch ar ac te r( ), velicina)); print("a3 = " + Arrays.toString(a3)); short[] a4 = Ko nv er tu jU .p ro st (G ene ri sa ni.n i z ( Short.class, new Gene ra to rS lu ca jn ih .Sh or t( ), velicina)); p r in t(a4 = " + A r ra ys .t oS tr in g( a4 )); int[] a5 = KonvertujU.prost(Generisani.niz( Integer.class, new G e ne ra to rS lu ca jn ih .In te ge r( ), velicina)); print("a5 = " + A r ra ys .t oS tr in g( a5 )); long[] a6 = KonvertujU.prost(Generisani,niz( Long.class, new G e ne ra to rS lu ca jn ih .Lo ng(), velicina)); print("a6 = " + Arrays .t oS tr in g( a6 )); float[] a7 = KonvertujU.prost(Generisani,niz( Float.class, new G e n e ra to rS lu ca jn ih .F1 oa t(), velicina)); print("a7 = " + Arrays .t oS tr in g( a7 )); double[] a8 = Ko nv er tu jU .p ro st (G ene ri sa ni.n i z ( Do ub le .c la ss, new G e ne ra to rS lu ca jn ih.D o u b l e ( ) , velicina)); print("a8 = 1 1 + Arrays .t oS tr in g( a8 )); { koje nizove popunjavaju generatorima.

616

Misliti na Javi

} /* Ispis: al = [true, false, true, false, false, true] a2 = [104, -79, -76, 126, 33, -64] a3 = [Z, n, T , c, Q, r] a4 = [-13408, 22612, 15401, 15161, -28466, -12603] a5 = [7704, 7383, 7706, 575, 8410, 6342] a6 = [7674, 8804, 8950, 7826, 4322, 896] a7 = [0.01, 0.2, 0.4, 0.79, 0.27, 0.45] a8 = [0.16, 0.87, 0.7, 0.66, 0.87, 0.59]

* ///:T im e se obezbeuje i da sve verzije m etode KonvertujU.prost() ispravno rade.

Veba 11: (2) Pokaite da autom atsko raspakivanje ne radi s nizovima. Veba 12: (1) Napravite inicijalizovan niz tipa double pom ou klase GeneratorDateKolicine. Ispiite rezultate. Veba 13: (2) Popunite objekat tipa String pom ou klase GeneratorDateKolicine.Character. Veba 14: (6) N apravite po jedan niz svakog od prostih tipova, zatim ih popunite pom ou klase GeneratorDateKoIicine. Ispiite svaki niz. Veba 15: (2) Izm enite PoredjenjeSKontejnerima.java tako to ete napraviti Generator za objekte tipa SferaOdBerilijuma, i prepravite m etodu main() tako da taj Generator upotrebi za Generisani.array(). Veba 16: (3) Na osnovu program a GeneratorDateKolicine.java, napravite klasu PreskociGenerator koja nove vrednosti proizvodi poveavanjem za 1, u skladu s argum entom konstruktora. Prepravite program TestGenerisanjaNizova.java tako da pokae
da vaa nova klasa radi ispravno.

Veba 17: (5) N apravite i ispitajte Generator za tip BigDecimal, i obezbedite da radi s m etodam a klase Generisani.

Metode klase A rrays


U paketu java.util pronai ete klasu Arrays koja sadri skup statikih uslunih funkcija za nizove. Postoji est osnovnih funkcija: equals(), za poredenje jednakosti dva niza (i deepEquals() za viedim enzionalne nizove), fill() za popunjavanje niza odreenom vrednou (ve ste je upoznali u p retho d nom delu poglavlja), sort() za uredivanje elemenata niza, binarySearch() za pronalaenje elem enta u ureenom nizu, toStringO za pravljenje String objekta koji predstavlja niz, i hashCode(), za transform isanje kljua niza (saznaete ta to znai u poglavlju D etaljno ra zm a tm n je kontejnera). Sve ove m etode postoje i za nizove prostih tipova i za nizove tipa objekata. Osim toga, postoji i jedinstvena m etoda asList() koja proizvoljan niz pretvara u kontejner tipa List, o em u ste saznali u poglavlju
uvanje objckata.

Pre nego to razm otrim o m etode klase Arrays, pogleaem o jednu korisnu m etodu koja nije njen deo.

Poglavlje 16: Nizovi

617

Kopiranje niza
S tandarna biblioteka u favi obezbeuje statiku m etodu System.arraycopy() pom ou koje se niz m nogo bre kopira nego kada se koristi petlja for za runo kopiranje. System.arraycopy() je preklopljena i radi s nizovima svih tipova podataka. Evo prim era kako ova m etoda radi s nizovim a tipa int:
//: ni zovi/KopiranjeNizova.java // Upotreba metode System.arraycopy() import java.util import static ne t. mi nd vi ew .u ti l.Print.*; public class KopiranjeNizova { public static void main(String[] int[] i = new i nt [7]; int[] j = new i n t[ 10 ]; Ar r a y s . f i 1 1 (i, 47); Arrays.fi 11 (j, 99); print("i = " + Ar ra ys .t o S t r i n g ( i ) ) ; print("j = " + Ar ra ys .t o S t r i n g ( j ) ) ; Sy st em . a r r a y c o p y ( i , 0, j, 0, i.length); print("j = " + Ar ra ys . t o S t r i n g ( j ) ) ; int[] k = new i nt [5]; A r r a y s . f i 1 1 (k, 103); Sy st em .a r r a y c o p y ( i , 0, k, 0, k.length); print("k = " + Ar ra ys . t o S t r i n g ( k ) ) ; Arrays.fill(k, 103); System.arraycopy(k, 0, i, 0, k.length); print("i = " + A r r a y s .t o S t r i n g (i)); // O b j e k t i : Integer[] Integer[] u = new Integer[10]; v = new Integer[5]; args) {

A r r a y s .f i1 1 (u, new In te ger(47)); A r r a y s . f i l l (v, new In te ger(99)); print("u = " + Arrays.toString(u)); print("v = " + Ar ra ys .t oS tr ing (v )); System.arraycopy(v, 0, u, u.length/2, v.length); print("u = " + Arrays.toString(u));

}
} /* Ispis: i = [47, 47, 47, 47, 47, 47, 47] j = [99, 99, 99, 99, 99, 99, 99, 99, 99, 99] j = [47, 47, 47, 47, 47, 47, 47, 99, 99, 99] k = [47, 47, 47, 47, 47] i = [103, 103, 103, 103, 103, 47, 47] u = [47, 47, 47, 47, 47, 47, 47, 47, 47, 47] v = [99, 99, 99, 99, 99] u = [47, 47, 47, 47, 47, 99, 99, 99, 99, 99]

* ///:-

618

Misliti na Javi

A rgum enti m etode arraycopy() su izvorni niz, pom eraj u izvornom nizu od koga treba zapoeti kopiranje, odredini niz, pom eraj u odredinom nizu od koga treba da pone kopiranje i broj elem enata za kopiranje. P rirodno, svako prekoraenje granica niza prouzrokovae izuzetak. Prim er pokazuje da se m ogu kopirati i nizovi prostih tipova i nizovi objekata. M eutim , ako kopirate nizove objekata, kopiraju se sam o reference, ali ne i sami objekti. To se zove povrno kopiranje (engl. shallow copy, videti dodatke knjige na Webu). M etoda System.arraycopy() ne obavlja autom atsko pakovanje niti autom atsko raspakivanje - oba niza m oraju biti tano istog tipa.

Veba 18: (3) N apravite i popunite niz objekata tipa SferaOdBerilijuma. Kopirajte taj niz u drugi, nov niz i pokaite da je to povrna kopija.

Poreenje nizova
Klasa Arrays nudi preklopljene verzije m etode equals() za poreenje jednakosti dva niza. I ova m etoda postoji za nizove svih prostih tipova i tipa Object. Da bi bili jednaki, nizovi m oraju da im aju isti broj elem enata, i svaki elem ent m ora da bude jednak odgovarajuem elem entu drugog niza, pri em u se m etoda equals () poziva za svaki element. (Za proste tipove se koristi m etoda equals() om otake klase, npr. Integer.equals() za tip int.) Evo prim era:
//: nizovi/PoredjenjeNizova.java // Korienje metode Arrays.equals() import java.util import static ne t. m i n d v i e w . u t i l .Print.*; public class PoredjenjeNizova { public static void main(String[] int[] al = new int [ 10]; int[] a2 = new i n t [10]; Arrays.fill(al, 47); A r r a y s .f i1 1 (a2, 47); pr i n t ( A r r a y s .eq ua ls (al, a2 ) ); args) {

a2[3] = 11;
print(Arrays.equals(al, a 2 ) ); String[] String[] sl = new String[4]; "Cao"); new StringC'Cao") s2 ) ); }; s2 = {new S t r i n g C C a o " ) , new St ri ng C' Ca o" ), Arrays.fill(sl,

new S t r i n g ( " C a o " ) , print(Arrays.equals(sl,

}
} /* Ispis: true false true

* ///:-

Poglavlje 16: Nizovi

619

U poetku, nizovi a l i a2 su jednaki pa je povratna vrednost true, ali se zatim jedan elem ent izm eni pa je drugi red rezultata false. U poslednjem sluaju, svi elem enti s l ukazuju na isti objekat, dok s2 im a pet jedinstvenih objekata. M edutim , jednakost nizova se odreuje prem a sadraju (m etodom Object.equals()), pa je rezultat true.

Veba 19: (2) N apravite klasu sa int poljem koje se inicijalizuje pom ou argum enta konstruktora. N apravite dva niza tih objekata koristei identine inicijalizacione vrednosti za oba niza i pokaite da m etoda Arrays.equals() kae da oni nisu jednaki. Reite problem tako to ete dodati m etodu equals() u svoju klasu. Veba 20: (4) Pokaite rad m etode deepEquals() za viedim enzionalne nizove.

Poreenje elemenata niza


Ureivanje se m ora izvoditi na osnovu stvarnog tipa objekta. Naravno, jedan p ristu p bio bi da se pie posebna m etoda ureivanja za svaki tip podataka, ali verovatno ve shvatate da se tako ne dobija kod koji se m oe lako koristiti s razliitim tipovima. Najvaniji cilj program iranja jeste da se razdvoji ono to se m enja od onoga to ostaje isto, a ovde je kod koji se ne m enja opti algoritam ureivanja, dok je ono to se m enja od jedne do druge prim ene zapravo nain poreenja objekata. Dakle, um esto ugraivanja koda za poreenje u razliite potprogram e za ureivanje, koristi se projektni obrazac Strategv.1 Strategija znai da se deo koda koji se razlikuje od sluaja do sluaja kapsulira u n u tar posebne klase (objekta tipa Strategija). O bjekat tipa Strategija predajete delu koda koji je uvek isti, a on pom ou te Strategije izvrava svoj algoritam. Na taj nain se mogu napraviti raziiiti objekti koji e izraavati razliite naine poreenja i dostavljae ih istom kociu za ureivanje. U Javi postoje dva naina za ureivanje. Prva je m etoda prirodnog" poreenja koja se ugrauje u klasu tako to se realizuje interfejs java.lang.Comparable. To je veoma jednostavan interfejs s jednom m etodom , compareTo(). Ova m etoda prim a kao argum ent drugi objekat istog tipa i vraa negativnu vrednost ako je tekui objekat manji od argum enta, nulu ako je tekui objekat jednak argum entu, odnosno pozitivnu vrednost ako je tekui objekat vei od argum enta. Evo klase koja realizuje interfejs C o m p arab le i ilustruje poreenje nizova pom ou m etode Javine standardne biblioteke A rrays.sort():
//: nizovi/UporedivTip.java // Primena interfejsa Comparable. import java.util import net.mindview.util.*; import static n e t. mi nd vi ew .u ti l.Print.*; public class UporedivTip implements Comparable { i nt i ; int j ;

Desijtt Patteriis, E ri i G am m a i dr. (A ison- Wesley, 1995). Videti Thittking in Patterns (w ith Java) na lokaciji w w w .M indV iew .net.

620

Misliti na Javi

private static int broj = 1; public UporedivTip(int nl, int n2) i = nl; j = n2; {

}
public String toString() if(broj++ % 3 == 0) rezultat += "\n"; return rezultat; { String rezultat = " [i = " + i + ", j = " + j + "]";

} }
public int compareTo(UporedivTip rv) { return (i < rv.i ? -1 : (i == rv.i ? 0 : 1));

}
private static Random r = new Random(47); public static Ge nerator<UporedivTip> generator() return new Generator<UporedivTip>() public UporedivTip next() { { {

return new U p or ed iv Ti p( r. ne xt Int (1 00 ), r . n e x t ln t( 10 0) );

} }; }
public static void main(String[] UporedivTip[] a = "); args) {

Ge ne ri san.niz(new Up or ed i v T i p [ 1 2 ] , g e ne ra to r( )); print("pre ureivanja: Arrays.sort(a); print("nakon ureivanja: "); p r in t( Ar ra ys .t oS tr ing (a )); p r i n t ( Ar ra ys .t oS tr ing (a ));

}
} /* Ispis: pre ureivanja: [[i = 58, j = 55], , [i = 68, j = 0], , [i = 51, j = 89], , [i = 20, j = 58], [i = 93, j = 61], [i = 22, j = 7], [i = 9, j = 78], [i = 61, j = 29] [i = 88, j = 28] [i = 98, j = 61]

[i = 16, j = 40] , [i = 11, j = 22]

]
nakon ureivanja: [[i = 9, j = 78], [i = 11, j = 22], [i = 22, j = 7], [i = 61, j = 29], [i = 93, j = 61], [i = 16, j = 40] [i = 51, j = 89] [i = 68, j = 0] [i = 98, j = 61] , [i = 20, j = 58], , [i = 58, j = 55], , [i = 88, j = 28],

] * ///:Kaa definiete funkciju poreenja, sam i definiete ta znai uporediti jedan objekat vae klase s drugim . Ovde se za poredenje koriste sam o vrednosti polja i, a polje j se ne uzima u obzir.

Poglavjje 16: Nizovi

621

M etoda generator() vraa objekat koji realizuje interfejt. Generator, tako to pravi ano nim nu unutranju klasu. Na taj nain, inicijalizacijom sluajnim vrednostim a, prave se objekti klase UporedivTip. U m etodi m ain(), za popunjavanje niza elem enata tipa UporedivTip koristi se generator, a niz se p o to m ureuje. D a interfejs Com parable nije bio realizovan, pojavila bi se greka CIassCastException u vrem e prevoenja prilikom pokuaja pozivanja m etode sort(). To bi se desilo zato to m etoda sort() tip svog argum enta konvertuje u Comparable. Pretpostavim o da radite s klasom koja ne realizuje interfejs Comparable, ili da klasa realizuje taj interfejs, ali vam se ne svia kako on a radi i za odreeni tip podataka radije biste koristili neku drugu funkciju za poreenje. D a biste to postigli, m orate da prim enite drugi pristup za poreenje objekata. Treba da napravite posebnu klasu koja realizuje interfejs C om parator (ukratko prestavljen u poglavlju uvanje objekata). Ovo je p rim er projektnog obrasca Strategy. C om parator im a dve m etode, compare() i equals(). M eutim , equals() se prim enjuje (realizuje) sam o kada su po treb n e specijalne perform anse, jer se pri svakom pravljenju klase ona autom atski nasleuje iz klase Object koja im a svoju m etodu equals(). Znai, m oete da koristite stand ardn u m eto d u equals() klase Object i da zadovoljite uslove koje nam ee interfejs. Klasa Collections (njom e em o se pozabaviti u n arednom poglavlju) sadri m etodu reverseOreder(); m etoda pravi C om parator koji obre p riro d an redosled ureivanja. To se jednostavno prim enjuje na nau klasu UporedivTip:
//: nizovi/ObrnutiRedosled.java // Prikazuje Co l 1ec tio n s .r e v e rs eO rd er (). import ja v a . u t i 1.*; import net.mindview.util.*; import static n e t . mi nd vi ew .u ti l.Print.*; public class ObrnutiRedosled { public static void main(String[] args) UporedivTip[] a = Generisan.niz( new U p or ed iv Ti p[ 12 ], U p o r e d i v T i p. ge ne ra tor () ); print("pre uredivanja: "); pr in t( Ar ra ys .t oS tr ing (a )); Arrays.sort(a, Col 1 e c t i o n s .re ve rs eO rd er ()); print("nakon ureivanja: "); pri n t ( A rr ay s.toStri ng (a )); {

)
} /* Ispis: pre ureivanja: [[i = 58, j = 55], , [i = 68, j = 0], , [i = 51, j = 89], , [i = 20, j = 58], [i = 93, j = 61], [i = 9, j = 78], [i = 16, j = 40], [i = 61, j = 29] [i = 98, j = 61] [i = 11, j = 22] [i = 22, j = 7 ] , [i = 88, j = 28]

]
nakon ureivanja: [ [i = 98, j = 61], , [i = 68, j = 0], , [i = 51, j = 89], , [i = 16, j = 40], [i = 93, j = 61], [i = 61, j = 29], [i = 22, j = 7], [i = 11, j = 22], [i = 88, j = 28] [i = 58, j = 55] [i = 20, j = 58] [i = 9, j = 78]

] * ///-

622

Misliti na Javi

Moete napisati i sopstveni Com parator. Sledei Com parator poredi objekte tipa UporedivTip po vrednosti njihovih polja j, a ne po vrednosti polja i:
//: nizovi/PrimerZaComparator.java // Primena interfejsa Comparator za neku klasu. import j a v a . u t i l .*; import net.mindview.util.*; import static n e t . mi nd vi ew .u ti l.Print.*; class ComparatorZaUporedivTip implements Comparator<UporedivTip> { public int compare(UporedivTip ol, UporedivTip o2) { return (ol.j < o2.j ? -1 : (ol.j == o2.j ? 0 : 1));

} }
public class PrimerZaComparator { public static void main(String[] args) { UporedivTip[] a = Generisan.niz( new U p or ed iv Ti p[ 12 ], UporedivTip.generator()); print("pre ureivanja: "); prin t( Ar ra ys .t oS tr ing (a )); Arrays.sort(a, new Co mp Ty pe Co mp ar at or ()); print("nakon ureivanja: "); print(Arrays.toStri n g ( a ) );

}
} /* Ispis: pre uredivanja: [[i = 58. j = 55], , [i = 68, j = 0], , [i = 51, j = 89], , [i = 20, j = 58], [i = 93, j = 61], [i = 22, j = 7], [i = 9, j = 78], [i = 16, j = 40], [i = 61, j = 29] [i = 88, j = 28] [i = 98, j = 61] [i = 11, j = 22]

]
nakon ureivanja: [[i = 68, j = 0], [i = 22, j = 7], [i = 11, j = 22] [i = 16, j = 40] [i = 9 3 , j = 61] [i = 51, j = 89] , [i = 88, j = 28], , [i = 58, j = 55], , [i = 98, j = 61], [i = 61, j = 29], [i = 20, j = 58], [i = 9, j = 78],

] * ///:V eba21: (3) P okuajteda uredite nizobjekata iz vebe 18. Prim enite C o m p arab le da biste reili problem . Potom napravite C o m p arato r za sortiranje objekata obrn u tim redosledom.

Poglavlje 16: Nizovi

623

Ureivanje niza
Pom ou ugraenih m etoda moete da uredite proizvoljan niz elem enata prostog tipa, odnosno bilo koji niz objekata koji realizuju C o m parab le ili im aju pridrueni C o m p arato r.3 Evo prim era kojim se generiu, a potom i sortiraju, sluajni objekti tipa String:
//: nizovi/UredjivanjeTipaString.java // Uredjivanje niza elemenata tipa String. import j a v a . u t i l .*; import net.mindview.util.*; import static net.mind vi ew .u ti l.Print.*; public class UredjivanjeTipaString{ public static void main(String[] args) { S t r i n g [] sa = Generisani .niz(new String[20], new GeneratorSlu ca jn ih .St ri ng (5 )); print("Pre ureivanja: ", A r r a y s .t oS tr in g( sa )); A r r a y s .s or t( sa ); Arrays2.print("Nakon ureivanja: print("Ureivanje obrnutim redom: ", A r ra ys .t oS tr in g( sa )); " + Arra ys .t oS tr in g( sa )); Arrays.sort(sa, C o l l e c t i o n s . r e v e r s e O r d e r O ) ; Arrays.sort(sa, St ri ng .C AS E_ IN SE NS ITI VE _O RD ER ); print("Uredivanje bez obzira na veliinu slova: " + Arrays .t oS tr in g( sa ));

}
} /* Ispis: Pre ureivanja: RFJQA, HxxHv] Nakon ureivanja: [EqUCB, HLGEa, HxxHv, JMRoE, Mesbt, 0WZnT, OneOE, RFJQA, WHkjU, YNzbr, bklna, cQrGs, dLsmw, eGZMm, gwsqP, hKcxr, nyGcF, rUkZP, suEcU, zDyCy] Ureivanje obrnutim redom: JMRoE, HxxHv, HLGEa, EqUCB] Ureivanje bez obzira na veliinu slova: RFJQA, rUkZP, suEcU, WHkjU, YNzbr, zDyCy] [bklna, cQrGs, dLsmw, eGZMm, EqUCB, gwsqP, hKcxr, HLGEa, HxxHv, JMRoE, Mesbt, nyGcF, OneOE, 0WZnT, [zDyCy, suEcU, rUkZP, nyGcF, hKcxr, gwsqP, RFJQA, OneOE, 0WZnT, Mesbt, eGZMm, dLsmw, cQrGs, bklna, YNzbr, WHkjU, [YNzbr, nyGcF, 0WZnT, cQrGs, eGZMm, JMRoE, suEcU, OneOE, dLsmw, HLGEa, hKcxr, EqUCB, bklna, Mesbt, WHkjU, rUkZP, gwsqP, zDyCy,

* ///:Verovatno ete prim etiti da je rezultat ovog algoritm a za ureivanje niza elem enata tipa S trin g leksikografski, odnosno na poetak postavlja rei koje poinju velikim slovom, a potom slede rei koje poinju malim slovom. (Ovako se obino ureuju stavke u telefonskim im enicim a.) Ako hoete da grupiete rei abecedno, tj. bez obzira na to da ii poinju velikim ili malim slovom, upotrebite String.CASE_INSENSITIVE_ORDER kao u poslednjem pozivu m etode so rt() u gornjem prim eru.

Z auo, u lavi 1.0 i 1.1 nije postojala podrka za ureivanje objekata tip a S trin g .

62 4

Misliti na Javi

Algoritam ureivanja koji se koristi u Javinoj standardnoj biblioteci optim alan je za odredeni tip koji se ureduje: Q uicksort za proste tipove, o d nosno stabilan MergeSort za objekte. Dakle, ne m orate da brinete o uinku, osim ako vam alatka za m erenje perform ansi ne ukae na to da je postup ak ureivanja usko grlo.

Pretraivanje ureenog niza


Kada se niz uredi, odreeni elem ent se m oe brzo potraiti pom ou m etode Arrays.binarySearch(). M eutim , veom a je vano da ne pokuate da prim enite m etodu binarySearch() na niz koji nije ureen, jer se rezultati toga ne m ogu predvideti. U sledeem prim eru, klasa GeneratorSlucajnih se koristi prvo za popunjavanje niza, a zatim i za dobijanje vrednosti koja e se traiti:
//: nizovi/PretrazivanjeNiza.java // Koriscenje Ar ra y s . b i n a r y S e a r c h ( ) . import java.util import net.mindview.util.*; import static riet.mindview.util .Print.*; public class PretrazivanjeNiza { public static void main(String[] args) Generator<Integer> gen = new G e n e r a t o r S lu ca jn ih .In te ge r( lO OO ); int[] a = KonvertujU.prost( Generisani .niz(new Integer[25], gen)); Ar ra ys .s or t( a); print("Ureen niz: while(true) { r); " + A r r a y s .t o S t r i n g ( a ) ) ; {

int r = ge n. n e x t ( ) ; int pozicija = Ar rays.binarySearch(a, if(pozicija >= 0) {

print("Pozicija od " + r + " jeste " + pozicija + ", a[" + pozicija + "] = " + a [ po zi ci ja ]) ; break; // Izlazak iz petlje while

} } }
} /* Ispis: Ureden niz: [128, 140, 200, 207, 258, 258, 278, 288, 322, 429, 511, 520, 522, 551, 555, 589, 693, 704, 809, 861, 861, 868, 916, 961, 998] Pozicija od 322 jeste 8, a[8] = 322

* ///:U petlji vvhile generiu se sluajne vrednosti za elem ente koji se trae, sve dok se jedna vrednost ne pronae. M etoda A rrays.binaryS earch() vraa vrednost koja je vea od nule ili jednaka nuli ako se pronae traeni element. U sup ro tn om , vraa negativnu vrednost koja predstavlja mesto na koje bi taj elem ent trebalo da se u m etne kada bi se redosled niza runo odravao. Vraena vrednost je:

Poglavlje 16: Nizovi

625

-(taka umetanja) - 1

Taka um etanja je indeks prvog elem enta koji je vei od traenog, odnosno a.size() ako su svi elem enti niza m anji od traenog. Ako niz sadri vie elem enata iste vrednosti, ne zna se koji e od njih biti pronaen. To znai da algoritam ne podrava duplikate elem enata, ve ih tolerie. Ako vam je potrebna ureena lista bez ponovljenih elem enata, upotrebite klasu TreeSet (koja odrava ureeni redosled) ili LinkedHashSet (koja odrava redosled um etanja). Ove klase se um esto vas autom atski staraju o svim pojedinostim a. Samo u sluajevima kada vam je neophodno poboljanje perform ansi trebalo bi zam eniti, jednu od tih klasa nizom iji se redosled odrava runo. Ako ste za ureivanje niza objekata koristili kom parator (nizovi prostih tipova se ne m ogu ureivati p om ou kom paratora), m orate da upotrebite isti Com parator kada pretraujete m etodom binarySearch() (pom ou preklopljene verzije ove funkcije). Na primer, program UrejivanjeTipaString.java m oe se prilagoditi tako da pretrauje niz:
//: nizovi/AbecednaPretraga.java // Pretraivanje pomou komparatora. import java.util.*; import net.mindview.util.*; public class AbecednaPretraga ( public static void main(String[] String[] args) { sa = G e n e r i s a n i .niz(new String[30],

new Ge ne ra to rS lu ca jn ih .St ri ng (5 )); Arrays.sort(sa, S t ri ng .C AS E_ IN SE NS ITI VE _O RD ER ); System .o ut ,p ri nt ln (Ar ra ys .t oS tr in g( sa )); int indeks = Arrays.binarySearch(sa, sa[10], Stri n g .C A SE _IN S E N S ITIV E O R D E R ); S y s t e m . o u t .p r in tln ("In de ks: "+ indeks + "\n"+ sa[indeks]);

I
} /* Ispis: [bklna, cQrGs, cXZJo, dLsmw, eGZMm, EqUCB, gwsqP, hKcxr, HLGEa, HqXum, HxxHv, JMRoE, JmzMs, Mesbt, MNvqe, nyGcF, ogoYW, OneOE, 0WZnT, RFJQA, rUkZP, sgqia, sljrL, suEcU, uTpnX, vpfFv, WHkjU, xxEAJ, YNzbr, zDyCy] Indeks: HxxHv 10

* ///:C o m p a ra to r se m ora proslediti preklopljenoj m etodi binaryS earch() kao trei argum ent. U gornjem prim eru e elem ent koji se trai sigurno biti pronaen jer je dobijeu iz sam og niza. Veba 22: (2) Pokaite da su rezultati prim ene m etode binaryS earch() na neureen niz nepredvidljivi. Veba 23: (2) N apravite niz objekata tipa Integer, popunite ga nasum inim in t vrednostim a (prim enite autom atsko pakovanje) i uredite ga obrn u tim redosledom pom ou C om paratora. Veba 24: (3) Pokaite da se klasa iz vebe 19 moe pretraivati.

626

Misliti na Javi

Saetak
U ovom poglavlju, videli ste da Java prua pristojnu podrku za nizove neprom enljive veliine i niskog nivoa. Takvi nizovi im aju dobre perform anse, ali ne i fleksibilnost kao u jezicima C i C ++. U prvoj verziji Jave, nizovi neprom enljive veliine i niskog nivoa bili su apsolutno neophodni, ne sam o zato to su projektanti Jave (i zbog perform ansi) odluili da Java im a i proste tipove, nego i zato to je podrka za kontejnere u toj verziji bila minim alna. Dakle, u ranim verzijama Jave, uvek je bilo preporuljivo koristiti nizove. U kasnijim verzijama Jave, znatno se poboljala podrka za kontejnere, pa o ni sada zasenjuju nizove po svemu sem po perform ansam a, a i perform anse kontejnera su u m euvrem enu znatno poboljane. Ve sm o na vie m esta rekli da problem e s perform ansam a obino ne prouzrokuje ono na ta program er sum nja. Nakon uvoenja autom atskog pakovanja i generikih tipova, dranje prostih tipova u kontejnerim a postalo je lako, pa je i to razlog da nizove zam enite kontejnerim a. Nizovi vie nem aju ni tu iskljuivu prednost da om oguuju bezbedan rad s tipovim a, poto i generiki tipovi daju kontejnere iji se tipovi autom atski proveravaju. Rekosmo u ovom poglavlju da su generiki tipovi prilino nepodesni za rad s nizovima, a i sami ete se uveriti kada pokuate da ih upotrebite. esto se deava da u vreme prevoenja dobijate upozorenja unchecked ak i kada postignete da generiki tipovi i nizovi na neki nain sarauju (kao to ete videti u n arednom poglavlju). Kada se raspravljalo o konkretnim prim erim a, projektanti jezika Java u vie navrata su m i govorili da bi umesto nizova trebalo da koristim kontejnere (pom ou nizova sam pokazivao specifine tehnike, pa tu m ogunost nisam im ao). Sve navedeno pokazuje da bi kontejnerim a trebalo da date prednost nad nizovim a kada program irate u novijim verzijama Jave. Tek kada se dokae da su perform anse problem (i da e ih prelazak na nizove znatno popraviti), trebalo bi da ponovo podelite program na proste faktore i preete na nizove. Ovo je prilino hrabra tvrdnja, ali neki jezici uopte nem aju nizove niskog nivoa i nepromenljive veliine. Imaju samo kontejnere prom enljive veliine koji su m nogo funkcionalniji od Javinih nizova. Prim era radi, P ython4 im a tip list ija sintaksa lii na onu elem entarnih nizova, ali mnogo funk on alniji - m oe se ak i naslediti:
#: nizovi/PythonoveListe.py aLista = [1, 2, 3, 4, 5] print type(aLista) # <type print a L i s t a [4] # 5 'lista'> print aLista # [1, 2, 3, 4, 5] Indeksiranje liste aLista.append(6) # Veliina lista je promenljiva aLista += [7, 8] # Dodavanje jedne liste drugoj print aLista # [1, 2, 3, 4, 5, 6, 7, 8] deoAListe = aLista[2:4] print deoAListe # [3, 4] class Mojali st a( li st ): # Nasleivanje liste

Videti www.Python.org.

Poglavlje 16: Nizovi

627

# Definicija metode, pokaziva def d a jO br nu tu (s el f):

'this' se pie eksplicitno:

obrnuta = s e l f [:] # Kopiranje liste pomou delova obrnuta.reverse() # Ugraena metoda klase list return obrnuta 1 ista2 = MojaLista(aLista) # Za pravljenje objekta nije potrebno 'nevv' print type(lista2) # <klasa '__ main__ .MojaLista'> print Iista2.daj0brnutu() # [8, 7, 6, 5, 4, 3, 2, 1]

# :Osnove Pythonove sintakse date su u p reth o d n o m poglavlju. Ovde sm o napravili listu navodei u n u tar uglastih zagrada niz objekata radvojenih zarezima. Rezultat je objekat tipa list u vrem e izvravanja (izlaz naredbe p rin t prikazan je u istom redu, u obliku kom entara). Ispisivanje liste daje isti rezultat kao m etoda Arrays.toString() u Javi. Pravljenje podniza liste postiem o deljenjem", tako to operator : sm estim o u n u tar operacije indeksiranja. Tip list im a jo m nogo ugraenih operacija. MojaLista je definicija klase; osnovne klase se piu u zagradam a. U nutar klase, naredbe def proizvode m etode, a prvi argum ent m etode autom atski postaje ekvivalentan rezervisanoj rei this u Javi, sam o to u P ythonu on m ora biti eksplicitno naveden, a identifikator self se koristi po konvenciji (nije rezervisana re). O bratite panju na autom atsko nasieivanje konstruktora. Iako u P ythonu zapravo postoje sam o objekti (m eu kojim a i tipovi za cele brojeve i brojeve s pokretnim zarezom ), izlaz u nudi za optim izovanje perform ansi kritinih delova program a predstavlja pisanje dodataka na jezicim a C, C + + ili pom ou Pyrexa, specijalne alatke koja olakava ubrzavanje koda. Na taj nain moete ostvariti i istou objekata i poboljane perform anse. Jezik PH P5 ide jo dalje u tom sm eru, jer im a sam o jedan tip niza koji radi i kao niz iji su indeksi celi brojevi i kao asocijativan niz (m apa). Zanimljivo je nagadati sada, posle toliko godina evolucije Jave, da li bi projektanti opet stavili u jezik proste tipove i nizove niskog nivoa da sve poinju iz poetka. Da je to bilo izostavljeno, m ogao se napraviti zaista ist objektno orijentisan jezik (uprkos takvim tvrdnjam a, Java to nije, upravo zbog elem enata niskog nivoa). Zahtev da se efikasno radi uvek izgleda nezaobilazan, ali s godinam a sm o videli evoluciju od te ideje ka upotrebi kom ponenata vieg nivoa kao to su kontejneri. D odajte tom e sledeu injenicu: ukoliko su kontejneri ugraeni u osnovu jezika (kao to jesu u nekim jezicima), o nda prevodilac ima m nogo bolju priliku da optim izuje. Bilo kako bilo, od nizova ne m oem o pobei; nailaziete na njih u kodu koji itate. M eutim , kontejneri su gotovo uvek bolje reenje. Veba25: (3) Napiite P ythonoveL iste.py u Javi.
R eenja o d a b r a n ih vebi d a ta su u e le k tro n sk o m d o k u m e n tu The Thinking in java Annotated Solu-

tion Cuide , koji se m o e k u p iti n a lo k aciji www.MindView.com.

Videti www.plip.net.

Detaljno razmatranje kontejnera


U poglavlju uvanje objekata predstavili sm o Javinu biblioteku kontejnera i n jen u osnovnu fun kcio n aln o st, to je dovoljno za poetak rada s njim a. U ovom poglavlju iserpnije emo istraiti ovu va n u biblioteku.

D a b is t e b i b l i o t e k u S KONTEJNERIMA MOGLI DA KORISTITE U POTPUNOSTI, MORATE ZNATI vie nego to sadri poglavlje uvanje objekata. Poto je ovo poglavlje zasnovano na naprednijem gradivu (kao to su generiki tipovi), oloili sm o ga do ovog m esta u knjizi. N akon to dam o potpuniji pregled kontejnera, saznaete kako radi transform isanje kljua (engl. hashing) i kako treba pisati m etode h a s h C o d e () i e q u a ls () da bi radile s kontejnerim a transform isanih kljueva. Nauiete zato postoje razliite verzije nekih kontejnera i kada koji treba upotrebiti. Poglavlje emo zavriti razm atranjem uslunih m etoda opte nam ene i specijalnih klasa.

Potpuna taksonomija kontejnera


U ,,Saetku poglavlja uvanje objekata prikazan je pojednostavljen dijagram Javine biblioteke kontejnera. Ovo je njen potpuniji dijagram koji obuhvata apstraktne klase i stare kom ponente (sem realizacija interfejsa Queue):

P n t m i n n tak<ntm m iia k n n t p i n p r n

Poglavlje 17: Detaljno razmatranje kontejnera

629

U Javu SE5 je dodato sledee: Interfejs Queue (klasa LinkedList je izm enjena tako da ga realizuje, kao to ste videli u poglavlju iivanje objekata) i njegove realizacije PriorityQ ueue i razni podtipovi klase BlockingQueue koje em o razm otriti u poglavlju Paralelno izvravatije. Interfejs ConcurrentM ap i njegova realizacija ConcurrentHashM ap, takoe za u potrebu u vienitnom radu i to je prikazano u poglavlju Paralelno izvravanje.

CopyOnWriteArrayList i CopyOnWriteArraySet, takoe za paralelno izvravanje. EnumSet i EnumMap, specijalne realizacije klasa Set odnosno Map za u p o treb u s nabrojanim tipovim a (enum); razm otrene su u poglavlju N abrojani tipovi. Vie uslunih m etoda u klasi Collections.
Pravougaonici oivieni isprekidano (ali ne najkraim crticam a) predstavljaju apstraktne klase. Vidi se da im ena vie klasa poinju sa Abstract. Isprva to moe da zbuni, ali radi se o alatkam a koje delimino realizuju odreeni interfejs. D a pravite sopstveni skup, na prim er, ne biste poeli od interfejsa Set i realizovali sve njegove metode; valjda biste nasledili klasu AbstractSet i napisali m inim um onoga to je potrebno za pravljenje nove klase. M eutim , biblioteka kontejnera sadri gotovo svu funkcionalnost koja vam ikada m oe zatrebati, pa najee moete zanem ariti sve klase ija im ena poinju sa Abstract.

Popunjavanje kontejnera
M ada je problem sa tam panjem sadraja kontejnera reen, popunjavanje kontejnera ima isti neostatak kao java.util.Arrays. Kao u paketu Arrays, postoji pratea klasa nazvana Collections koja sadri statine uslune m etode, m eu kojim a i jednu nazvanu fill ( ). Kao u verziji paketa Arrays, ta m etoda fill ( ) po celom kontejneru sam o duplira jednu referencu na objekat. Sem toga, ona radi sam o za List objekte, ali se proizvedena lista moe proslediti k onstruktoru ili nekoj metodi addA ll( ):
//: kontejneri/PopunjavanjeLista.java

// Metode C o l1e c t i o n s .fi11() i Collecti on s. nC op ie s(). import j a v a . u t i 1 class AdresaStringa { private String s; public Ad re sa St ri ng a( St ring s) { this.s = s; } public String t o S t r i n g O { return s u p e r . t o S t r i n g O + " " + s;

} }
public class PopunjavanjeLista { public static void main(String[] Collections.nCopies(4, S y s t e m . o u t .pri ntl n (1 is t ) ; Col 1 e c t i o n s .fi 11 (1 i st, new Adre sa St ri ng aC 's vi ma!")); args) { List<AdresaStringa> 1 ist= new ArrayList<AdresaStringa>( new AdresaStri ng a( "Z dr avo ")));

630

Misliti na Javi

System.o ut .p ri nt ln (li st );

}
} /* Ispis: (primer) [AdresaStringa@82ba41 Zdravo, AdresaStringa@82ba41 Zdravo, AdresaStringa@82ba41 Zdravo, AdresaStringa@82ba41 Zdravo] [AdresaStringa@923e30 svima!, AdresaStringa@923e30 svima!, AdresaStringa@923e30 svima!, AdresaStringa@923e30 svima!]

* ///:U p rim eru su prikazana dva naina popunjavanja kontejnera (objekta tipa Collection) referencama na jedan objekat. Prvo, m etoda CoUections.nCopies( ) pravi listu koja se prosleuje konstruktoru; on popunjava ArrayList. M etoda to S trin g ( ) klase A dresaStringa poziva m etodu O bject.toString( ) koja daje im e klase i heksadecim alno prikazan klju za heiranje (engl. hash code) tog objekta - generisan m etodom h ashC ode( ). Iz rezultata vidite da sve reference upuuju na isti objekat, a isto vai i nakon poziva druge m etode, Collections.fill( ). M etodu fill( ) jo m anje korisnom ini to to moe da zam eni sam o elem ente koji su ve u listi - ona ne moe da dodaje nove elemente.

Generatorsko reenje
Gotovo svi podtipovi interfejsa Collection imaju konstruktor koji prim a drugi objekat tipa Collection, iz kojega moe da popuni nov kontejner. Dakle, da bism o sainili podatke za testiranje, sam o treba da napravim o klasu koja kao argum ente prim a konstruktore nekog G eneratora (oni su definisani u poglavlju Generiki tipovi i poblie objaenjeni u poglavlju N izo vi) i neki broj kolicina:
//: ne t/ mi nd vi ew /u ti1/PodaciKontejnera.java // Kontejner koji podacima popunjava generator. package net.mindview.util; import j a v a . u t i l .*; public class PodaciKontejnera<T> extends ArrayList<T> { public PodaciKontejnera(Generator<T> gen, int kolicina) for(int i = 0; i < kolicina; i++) a d d ( g e n. ne xt () ); {

}
// Generika pomona metoda: public static <T> PodaciKontejnera<T> 1 ist(Generator<T> gen, int kolicina) { return new PodaciKontejnera<T>(gen, kolicina);

} } ///= Ovde G enerator puni kontejner eljenim brojem objekata. Dobijeni kontejner se moe proslediti konstruktoru bilo kojeg drugog kontejnera koji e podatke kopirati u sebe. Za popunjavanje postojeeg kontejnera moe se upotrebiti i m etoda addAH( ) koju im aju svi podtipovi klase Collection.

Poglavlje 17: Detaljno razmatranje kontejnera

631

Kada se koristi pom ona generika m etoda, sm anjuje se koliina koda potrebnog za u p otreb u klase. Program PodaciKontejnera je prim er projektnog obrasca A da pter (Adapter);' on prilagoava odredeni Generator konstruktoru nekog kontejnera, tj. podtipa klase Collection. Evo prim era u kojem se inicijalizuje obiekat tipa LinkedHashSet:
//: kontejneri/PodaciZaTestKontejnera.java import java.util import net.mindview.util.*; class Vlada implements Generator<String> { String[] foundation = ("strange women lying in ponds " + "distributing swords is no basis for a system of 1 1 + "government").split(" "); private int indeks; public String next() { return fo un da tion[indeks++]; }

}
public class PodaciZaTestKontejnera { public static void main(String[] args) { Set<String> set = new LinkedHashSet<String>( new PodaciKontejnera<String>(new V l a d a O , 15)); // Korienje pomone metode: s e t . a d d A l l (PodaciKontejnera.list(new Vlada(), 15)); System.out.pri n t l n (s et );

}
} /* Ispis: [strange, women, lying, in, ponds, distributing, swords, is, no, basis, for, a, system, of, government]

* ///:Elementi su u poretku kojim su bili um etani, poto L inkedH ashSet odrava ulananu listu koja zadrava redosled umetanja. Svi generatori definisani u poglavlju N izovi sada su dostupni preko adaptera PodaciK outejnera. U ovom prim eru upotrebiem o dva od njih:
//: ko ntejneri/GenerisanjePodatakaKontejnera.java // Korienje generatora definisanih u poglavlju Nizovi. import j a v a . u t i l .*; import net.mindview.util.*; public class GenerisanjePodatakaKontejnera { public static void main(String[] args) { System.out.pri ntln(new ArrayLi st<Stri ng>( Po da ci Kontejnera.1 i s t ( // Pomona metoda new Random Ge ne ra to r. St rin g(9), 10)));

O vo nije stroga definicija adaptera kao ona iz knjige Projektni obrasci, ali sm a tram d a je dovoljno precizna.

632

Misliti na Javi

System.out.println(new HashSet<Integer>( new PodaciKontejnera<Integer>( new Rand om Ge ne ra to r. In teg er (), 10)));

}
} /* Ispis: [YNzbrnyGc, F0WZnTcQr, GseGZMmJM, RoEsuEcllO, neOEdLsmw, HLGEahKcx, rEqUCBbkI, naMesbtWH, kjUrUkZPg, wsqPzDyCy] [573, 4779, 871, 4367, 6090, 7882, 2017, 8037, 3455, 299]

* ///:D uinu objekta tipa String koji pravi R andom G enerator.String odreuje konstruktor u argum entu.

Generatori mapa
Na isti nain m oem o napraviti m apu, ali za nju nam treba klasa Par, poto se za svaki poziv Generatorove m etode n e x t( ) m o ra napraviti par objekata (jedan klju i jedna vrednost) za popunjavanje mape:
//: net/mindview/util/Par.java package net.mindview.util; public class Par<K,V> { public final K kljuc; public final V vrednost; public Par(K k, V v) { kljuc = k; vrednost = v;

} } ///:Polja kljuc i v red n o st su javna i finalna, pa P ar postaje O bjekat za pretiospodataka (ili


Prenosila).

Ovaj adapter interfejsa M ap sada m oe da popunjava objekte za inicijalizaciju mape raznim kom binacijam a G eneratora, objekata tipa Iterab le i konstanti:
//: ne t/ mi nd vi ew /u ti l/ Pod ac iIzMape.java // Mapa koju podacima popunjava generatorski objekat. package net.mindview.util; import j a v a . u t i l .*; public class Poda ciIzMape<K,V> extends LinkedHashMap<K,V> { // Jedan Generator za Par: public P o d a c i I z M a p e ( G e n e r a t o r < P a r < K , V gen, int kolicina) for(int i = 0; i < kolicina; Par<K,V> p = gen.next(); put(p.kljuc, p.vrednost); i++) { {

Poglavjje 17: Detaljno razmatranje kontejnera

633

}
// Dva zasebna Generatora: public Po daciIzMape(Generator<K> genK, Ge nerator<V> genV, int kolicina) for(int i = 0 ; { i < kolicina; i++) {

p u t ( g e nK .n ex t( ), g e n V . n e x t ( ) ) ;

} }
// Generator za klju i jedna vrednost: public PodaciIzMape(Generator<K> genK, V vrednost, int kolicina){ for(int i = 0; i < kolicina; i++) { p u t ( g e nK .n ex t( ), vrednost);

} }
// Objekat tipa Iterable i Ge nerator vrednosti: public Po da ciIzMape(Iterable<K> genK, Genera to r< V> genV) for(K kljuc : genK) { put(kljuc, g e n V . n e x t ( ) ) ; {

} }
// Objekat tipa Iterable i jedna vrednost: public PodaciIzMape(Iterable<K> genK, V vrednost) for(K kljuc : genK) { put(kljuc, vrednost); {

} }
// Generike pomone metode: public static <K,V> PodaciIzMape<K,V> m a p ( G e n e r a t o r < P a r < K , V gen, int kolicina) return new PodaciIzMape<K,V>(gen, { kolicina);

}
public static <K,V> PodaciIzMape<K,V> ma p( Generator<K> genK, Generator<V> genV, int kolicina) return new P o d a c i IzMape<K,V>(genK, genV, kolicina); {

}
public static <K,V> P o d a c i IzMape<K,V> map(Generator<K> genK, V vrednost, int kolicina) return new PodaciIzMape<K,V>(genK, { vrednost, kolicina);

}
public static <K,V> PodaciIzMape<K,V> ma p( Iterable<K> genK, Ge nerator<V> genV) { return new PodaciIzMape<K,V>(genK, genV);

}
public static <K,V> PodaciIzMape<K,V> map(Iterable<K> genK, V vrednost) { return new PodaciIzMape<K,V>(genK, vrednost);

} }

III--

63 4

Misliti na Javi

Program prua izbor - moe se koristiti: jedan objekat tipa G en erato r< P ar< K ,V , dva zasebna Generatora, jedan G enerator i jedna konstan tn a vrednost, jedan objekat tipa Iterable (koji obuhvata svaki objekat tipa Collection) i jedan Generator, ili objekat tipa Iterable i jedna vrednost. Pom one generike m etode sm anjuju koliinu koda potreb n u za pravljenje objekta tipa PodacilzMape. Evo prim era korienja program a PodacilzMape. I G enerator klase Slova realizuje interfejs Iterable pravljenjem Iteratora; zato se m oe upotrebiti za testiranje m etoda PodaciIzM ape.m ap( ) koje rade sa svim objektim a koji realizuju Iterable:
//: kontejneri/TestiranjePodatakaMape.java import j a v a . u t i l .*; import net.mindview.util.*; import static ne t. mi nd vi ew .u ti l.Print.*; class Slova implements G e n e r a t o r < P a r < I n t e g e r , S t r i n g , Iterable<Integer> { private int velicina = 9; private int broj = 1; private char slovo = 'A'; public Par<Integer,String> next() return new Par<Integer,String>( broj++, "" + slovo++); {

}
public Iterator<Integer> iterator() return new Iterator<Integer>() public Integer next() public void remove() { public boolean hasNext() { } } {

{ return broj++;

{ return broj < velicina;

throw new Unsuppor te dO pe ra ti onE xc ep ti on ();

public class TestiranjePodatakaMape { public static void main(String[] // Ge nerator para: print(PodaciIzMape.map(new Slova(), // Dva zasebna generatora: print(PodaciIzMape.map(new C o u n t i n g Ge ne ra to r. Cha ra ct er (), new Ra nd om Ge nerator.String(3), 8)); // Generator kljua i jedna vrednost: print(PodaciIzMape.map(new C o u n t i n g G e ne ra to r. Cha ra ct er (), "Vrednost", 6)); // Objekat tipa Iterable i Generator vrednosti: print(PodaciIzMape.map(new S l o v a ( ) , new Ra nd om Ge ne ra to r. St rin g( 3) )); 11)); args) {

Poglav[je 17: Detaljno razmatranje kontejnera

635

// Objekat tipa Iterable i jedna vrednost: print(PodaciIzMape.map(new Slova(), Pop"));

}
} /* Ispis: {1=A, 2=B, 3=C, 4=D, 5=E, 6=F, 7=6, 8=H, 9=1, 10=J, 11=K} {a=YNz, b=brn, c=yGc, d=F0W, e=ZnT, f=cQr, g=Gse, h=GZM} {a=Value, b=Value, c=Value, d=Value, e=Value, f=Value} {l=mJM, 2=RoE, 3=suE, 4=cU0, 5=ne0, 6=EdL, 7=smw, 8=HLG} {l=Pop, 2=Pop, 3=Pop, 4=Pop, 5=Pop, 6=Pop, 7=Pop, 8=Pop}

* ///:-

I u ovom primeru smo koristili generatore iz poglavlja Nizovi. Ovim alatkama moete da generiete proizvoljan skup podataka za Mape i kontejnere (objekte tipa Collection) i zatim da ih inijalizujete konstruktorom ili metodama M ap.putA lI() odnosno Collection.addAlI().

Korienje apstraktnih klasa


Podaci za testiranje kontejnera m ogu se napraviti i nam enskim realizacijama klasa Collection i Map. Svaki kontejner iz paketa java.util im a svoju apstraktnu klasu koja delim ino realizuje taj kontejner, a vama preostaje sam o da realizujete potrebne m etode za pravljenje eljenog kontejnera. Ukoliko je dobijeni kontejner sam o za itanje, to je uobiajeno u sluaju podataka za testiranje, broj m etoda koje m orate da napravite je m inim alan. Iako to nije bilo neophodno, sledee reenje nam je dalo priliku da prikaem o jo jedan projektni obrazac Flyweight (Zam ajac). F lyw eight se koristi kada je za uobiajeno reenje neophodno previe objekata ili kada pravljenje norm alnih objekata zauzima previe prostora. Projektni obrazac F lyw cight eksternalizuje deo objekta tako to, um esto da se ceo objekat dri u sam om objektu, deo objekta ili ceo objekat trai se u efikasnijoj, spoljnoj tabeli (ili pravi nekim drugim p ro raun om koji tedi prostor). Vano je da uoite kako se nam enski objekti tipa Map i Collection Iako prave nasleivanjem klasa java.util.Abstract. Da biste napravili objekat tipa Map sam o za itanje, nasleujete klasu AbstractMap i realizujete m etodu entrySet( ). D abiste napravili objekat tipa Set samo za itanje, nasleujete klasu AbstractSet i realizujete m etode ite ra to r( ) i size( ). Skup podataka u ovom prim eru jeste m apa drava i njihovih glavnih gradova.2 Metoda glavni_gradovi( ) pravi m apu drava i glavnih gradova. M etoda im e n a ( ) pravi listu engleskih imena drava. U oba sluaja dobiete delim ini listing ukoliko eljenu veliinu naznaite celobrojnim argum entom :
//: net/mi ndvi ew/uti1/Countri e s .java // "Flyweight" Mape i Liste podataka za primer. package net.mindview.util; import j a v a . u t i l .*; import static net.mindview.uti1 .Prin t .*;

Ovi p o d a su pro n a en i na In tern etu . C'itaoci su s vrem en o m slali razne ispravke.

636

Misliti na Javi

public class Countries { public static final String[][] // Afrika { "ALG ER IA ", "A1 gi e r s " }, {"A N G O L A " ,1 1Lu an da "} , {" B E N I N " ,"Porto-Novo"}, {"B 0T S W A N A " ,"Gaber on e"} , {"BURKINA F A SO ","Ouaga do ug ou "}, {" B U R UN DI ","Buj um bu ra "}, {"CAMER00N","Yaounde"}, {" C H A D " , " N 'dj a m e n a " } , {" EG YP T" ,Cairo"}, {"CAPE V E R D E " ,"P raia"}, {" C OM OR OS1 ', "Moroni }, {"CENTRAL AFRICAN RE P U B L I C " ,"B an gu i"}, {" C ON GO ","Brazzavi11e " }, {D J I B O U T I ,"Dijibouti" } , {"EQUATORIAL G U I N E A " ,"Mal ab o"}, {"E R I T R E A " ,"Asmara"} , {"ETHIOPIA'V'Addis Ababa"}, {" G A B O N " ,1 1Li brevi 11 e " }, {"THE G A M B I A " , "Banjul1 1 }, {" G H A N A " ,"Accra"}, {"G U I N E A " ,"C on ak ry "}, {" B I S S A U V ' B i s s a u " } , {"COTE D'IVOIR (IV0RY C O A S T ) " ,"Yamou ss ou kr o" }, {" K EN YA ","Nairobi"}, {" L E S OT HO ","Mas er u"}, {" LI BE RI A" ," Mo nr ov ia" }, {"LIBYA, l,"Tripoli"}, {"M AD AG AS CA R","A nt an an ar iv o"}, {"M AL AW I","Li1o n g w e " }, {" M A L I ","Bamako"}, {" M A U RI TA NI A","N ouakchott"} , {"MA UR IT IU S V ' P o r t Louis"}, {"M 0R 0C C0 ","R ab at "}, {"MOZAMBIQ UE ", "M ap uto "}, {"NAMIBIA","Windhoek"}, {" N I G E R " ,"Ni a m e y " } , {"N IG ER IA ","A bu ja "} , {"R W A N D A " ,"Kigali" }, {"SAO TOME E P R I N CI PE ","Sao Tome"}, {"S E N E G A L " ,"D akar"} , {"S EY CH EL LE S","Victoria " } , {"SIERRA L E ON E" ," Fr ee to wn "}, {" S O M AL IA ","M og adishu"} , {"SOUTH A F R I C A " ,"Pretoria/Cape Town"}, {"SUDA N' V' Kh ar to um "}, {"S WA Z I L A N D " ,M b a b a n e " }, {"T AN Z A N I A " , "Dodoma'1} , {"T O G O ","Lo me } , {T U N I S I A " ," T un is }, {" U G A N D A " ,Kampal a " } , {"DEMOCRATIC REPUBLIC OF THE CONGO (Z AIRE)","Kinshasa"}, {" Z A M B I A " ,"Lusaka"} , {"Z IM B A B W E " ," H ar ar e"} , // Azija {"A FG HA NI ST AN","Kabu 1 " }, {" B A H RA IN ", "Manama''}, {" BA NG LA DE SH ", "D ha ka" }, { " B HU TA N","Thimp hu "}, {"BRUNEI","Bandar Seri Begawan"}, {"C AM B O D I A " ,"Phnom Penh"}, {"CHINA","Beijing"}, {"CYPRUS","Nicosia"}, {"I ND O N E S I A " ,"J akarta"}, {" I N D I A " ,"New Delhi"}, PODACI = {

{" I R A N " ,"Teh ra n"} , {"IRAQ", "B ag hd ad "} , {" I S R A E L " ,"Je rusale m " } , {J A P A N " ,"T ok yo "} , {" J O R D A N " ," A mm an "}, {"K U W A I T " ,"Kuwait City"}, {" L A O S " ,"Vien t i a n e " } , {"LE B A N O N " ,"Bei r u t" } , {" M A L A Y S I A " ,"Kuala Lumpur"}, {" M O N G O L I A " ,"U1an Bator"}, {"MYANMAR (BURMA)" ,R a n g o o n " }, {"THE M A L D I V E S " ," M al e"},

Poglavlje 17: Detaljno ra^ilatranje kontejnera

637

{ " NE PA L","Katmandu"}, {"NORTH KO R E A " , " P ,y o n g y a n g " } , {"O M A N ","M uscat"}, {"PAKI ST AN","Is lamabad"}, {" P HI LI PP IN ES ","Manila"}, {"Q A T A R " ,"Doha"}, {"SAUDI AR AB IA ", "R iy ad h" }, { "S INGAPORE","Sin g ap or e"}, {"SOUTH K O R E A " ,"Seo ul"}, {"SRI L A N K A V ' C o l o m b o " } , {" SYRIA ,"Damascus"}, {"TAIWAN (REPUBLIC OF C H I N A ) ","Taipei"}, {"T HA I L A N D " ,"Bangkok"}, {" T UR KE V',"Ankara"}, {"UNITED ARAB EMIRATES","Abu Dh ab i"}, {" V I E TN AM ","Hanoi"}, {"Y EM EN "," S a n a 1a " } , // Australjia i Okeanija {"A U S T R A L I A " ," C anberra"}, {"FIJI","Suva"}, {"K IR I B A T I " ,"Bai ri ki"}, { "MARSHALL ISLANDS","D al ap -Uliga-Darrit"}, {"M IC RO NE SI A","Pali ki r " }, {"N AU RU ","Y a re n"} , {"NEW ZE A L A N D " ,"Wel 1 i n gt on "}, {"PALAU'V'Koror''}, {"PAPUA NEW G U I N E A " ,"Port Moresby"}, {"S0L0M0N ISLA ND S","Honai r a " } , { " TO NG A","Nuku'alofa"}, {" TU VA LU ", "F on ga fa le" }, {"VANUATU","< Port-Vila"}, {"WESTERN S A M O A ,"Api a " } , // Istona Evropa i bivi SSSR {"A R M E N I A " ,"Yerevan"}, {"AZERB AI JA N","Bak u"}, {"BELARUS (BYELORUSSIA)1 1 , "Mi n s k " }, {"G E O R G I A " ,"Tbi1i s i "}, {" K AZ AK ST AN ","Almaty"}, {"KYRGY ZS TA N","A lm a-Ata"}, {"M O L D O V A " ,"Chi s i na u"}, {" R US SI A","Mos co w"} , {"T AJ IK IS TA N","D us hanbe"}, {"TURKMENISTAN","A shkabad"}, {" U K R AI NE ","Kyi v " } , {"UZBEK IS TA N","Tashkent"} , // Evropa {"ALBANIA","Tirana"}, {"A ND OR RA ","Andorra la Vella"}, {" A U S TR IA ","Vienna"} , {"B EL GI UM ","Brussels"} , { "BOSNIA AND HERZEGOVIIMA", "Saraj ev o"}, {"B UL G A R I A " ,"Sofi a " } , {"C R O A T I A " ,"Zagreb"} , {"CZECH RE P U B L I C " ,"P rague"}, {" D E N MA RK ","Copenhagen"}, {"EST ON IA ","Tal1 in n"}, {" FI N L A N D " ,"Hel si nk i" } , {" FR AN CE ","P aris"}, {"G E R M A N Y " ,"Berli n "} , {"GRE EC E","A thens" } , {"H UN GA RY ","Budapest"} , {" ICEL AN D","R ey kjavik"} , {" I R EL AN D","Dubl in " } , {"ITAL Y","Rome"}, {" L AT VI A","Rig a"}, {"L IE CH TENSTEIN","V aduz"}, {" L IT HU AN IA ","Vi1n i u s " } , {"LUXE MB OU RG ","Luxembourg" } , {" M AC ED ON IA ","Skopje"} , {" M AL TA ","Val1e t t a " } , {"M O N A C O " ,"Mon ac o"}, {"MONTE NE GR O","P od gorica"}, { "THE NE TH ER LA ND S","A msterdam"} , {"NOR WA Y","O sl o" }, { "P O L A N D " ,"Warsaw"} , {"PORTU GA L","L is bo n" }, {"ROMANI A" ," Bu ch ar est "}, {"SAN MARINO","San Mariiio"}, {"S E R B I A " ,"Belgrade"}, {"SLOVA KI A","Brati sla v a " } , {" S LO VE NI A","Ljublj a n a " } , {"S PA IN ","Madrid " } ,

638

Misliti na Javi

{" S WE DE N","Sto ck ho lm "}, {" S WI TZ ER LA ND ","B er re "}, {"UNITED KING DO M" ,l , L o n d o n " } , {"VATICAN CITY"," // Severna i Centralna Amerika {"ANTIGUA AND B A R B U D A " ,"Saint John's"}, {" BA HA MA S" ," Na ss au "}, {"BA R B A D O S " ,"Bri d g e t o w n " }, {"B E L I Z E " ,"B el mo pa n"}, {"CANADA","0ttawa"}, {"COSTA RICA","San Jose"}, {" C UB A"," H av an a"} , {" D OM IN IC A"," R os ea u"}, {"DOMINICAN REPUBLIC","Santo Domingo"}, {"EL S A L V A D O R " ,"San Salvador"}, {"GRE NA DA ","Sai nt G e o r g e 's "}, {"GUA TE MA LA ","Guatemala C i t y "}, {"HA IT I" ," Po rt -a u- Pri nc e" }, {" H ON DU RA S"," T eg uc ig al pa "}, {"J A M A I C A " ," K in gs to n"}, {"MEXICO","Mexico City"}, {"PANAMA"."Panama City"}, {"N IC A R A G U A " ," M an ag ua "}, {"ST. K I T T S " ,"-"}, "},

{"NEVI S" ," Ba ss et er re" }, {"ST. L U C I A " ," Ca st ri es "}, {"ST. VINCENT AND THE G R EN AD IN ES ", "K in gs tow n" }, {UNITED STATES OF A M E R I C A " ,"Washington, O.C.'1}, // Juna Ameri ka { "ARG EN TI NA ","Buenos Ai re s " }, {"BOLIVIA'V'Sucre (legal)/La Paz( ad mi ni st ra ti ve )"}, {" B RA ZI L","Bra si1i a " }, {" C H I L E " ," S an ti ag o"}, {"C OL OM BI A"," B og ot a"}, {"E C U A D O R " ,"Qui t o " }, {" G UY AN A","G eo rg et ow n"}, {"P A RA GU AY" ,"A s u n c i o n " }, { "PER U","Lim a"}, {"S UR I N A M E " ," P a r am ar ib o"}, {"TRINIDAD AND T O B A G O " ,"Port of Spain"}, {" U R U GU AY ","M on te vi de o"}, {"V EN E Z U E L A " ,"C ar ac as "},

};
// Realizovaemo metodu entrySet() da bismo koristili AbstractMap private static class FlyweightMapa extends Abstract Ma p< St ri ng ,St ri ng > { private static class Stavka implements Ma p. En tr y< St ri ng ,S tri ng > { int indeks; Stavka(int indeks) { this.indeks = indeks; { } public boolean e q u a l s (Object o)

return P O D A C I [ i n d e k s ] [ 0 ].equals(o);

}
public String getKey() { return P O DA CI [i nd ek s][1]; } { return PODACI [i ndeks] [1]; } { public String g e t V a l u e O

public String setValue (String vrednost)

throw new U n s u pp or te dO pe ra ti onE xc ep ti on ();

}
public int h a s h C o d e O { return PO D A C I [ i n d e k s ] [0].h a s h C o d e ( ) ;

Poglavlje 17: Detaljno razmatranje kontejnera

639

// Realizacijo m metoa size() i iterator() koristimo // klasu AbstractSet static class SkupStavki extends Ab s t r a c t S e t < M a p . E n t r y < S t r i n g , S t r i n g { private int velicina; EntrySet(int velicina) if(vel icina < 0) this.velicina = 0; // Ne mo e biti vea od niza: else if(ve1icina > PODACI.length) this.velicina = PODACI.length; else this.velicina = velicina; {

}
public int size() private class Iter implements It er a t o r < H a p . E n t r y < S t r i n g , S t r i n g { // Samo jedan objekat tipa Stavka po Iteratoru; private Stavka stavka = new Stavka(-l); public boolean hasNext() { return stavka.indeks < velicina - 1; { return velicina; }

}
public Map.Entry<String,String> next() stavka.indeks++; return stavka; {

}
public void remove() { throw new UnsupportedOpe ra ti onE xc ep ti on ();

} }
publ i c I t e r a t o r < M a p . E n t r y < S t r i n g , S t r i n g iterator() return new Iter (); {

} }
private static S e t < M a p . E n t r y < S t r i n g , S t r i n g stavke = new E n t r y S e t (P ODACI.1e n g t h ) ; public S e t < M a p . E n t r y < S t r i n g , S t r i n g entrySet() return stavke; {

} }
// Pravljenje delimine mape drava, date veliine: static Ma p< St ri ng .S tr ing> select(final return new FlyweightMapa() { { int velicina) {

public S e t < M a p . E n t r y < S t r i n g , S t r i n g entrySet() return new EntrySet(velicina);

640

Misliti na Javi

static Map<String,String> mapa = new Fl yw ei g h t M a p a ( ) ; public static Map<String,String> g l a v n i _ g r a d o v i () { return mapa; // Cela mapa

}
public static Map<String,String> gl av ni_gradovi(int velicina) return se le ct (v el ic in a); // Delimina mapa {

}
static List<String> imena = // Sva imena: public static List<String> imena() // Delimina lista: public static List<String> imena(int velicina) { return new ArrayList<String>(select(velicina).keySet()); { return imena; }

}
public static void main(String[] args) p r in t( gl av ni _g ra do vi(10)); p r in t( im en a( 10 )); print Ha sh Ma p< St ri ng ,S tr ing >( gl av ni _g ra do vi(3))); print(new LinkedHash Ma p< St ri ng, St ri ng >( gl av ni _g ra dov i(3))); pri nt(new TreeMap<Stri ng,Stri ng > ( g l a v n i _ g r a d o v i (3))); print(new Ha sh ta bl e< St ri ng ,S tri ng >( gl av ni _g ra do vi(3))); print(new Ha sh Se t< String>(imena(6))); print(new Linked Ha sh Se t< St ri ng> (i me na (6 )) ); print(new Tree Se t< St ri ng >( im ena (6 )) ); p r i n t (new ArrayLi st<Stri ng >( im en a( 6))); print(new Linked Li st <S tr in g> (im en a( 6) )); print( gl av ni _g ra do vi().get(" BR AZ IL ") ); {

}
} /* Ispis: {ALGERIA=Algiers, ANG0LA=Luanda, BENIN=Porto-Novo, BOTSWANA=Gaberone, BURKINA FASO=Ouagadougou, BURUNDI=Bujumbura, CAMEROON=Yaounde, CAPE VERDE=Praia, CENTRAL AFRICAN R E P U B L I C = B a n g u i , C H A D = N 1djamena} [ALGERIA, ANGOLA, BENIN, BOTSWANA, BURKINA FASO, B U R U N D I , CAMEROON, CAPE VERDE, CENTRAL AFRICAN REPUBLIC, CHAD] {BENIN=Porto-Novo, ANGOLA=Luanda, ALGERIA=Algiers} {ALGERIA=Algiers, ANGOLA=Luanda, {ALGERIA=Algiers, ANGOLA=Luanda, {ALGERIA=Algiers, ANGOLA=Luanda, BENIN=Porto-Novo} BENIN=Porto-Novo} BENIN=Porto-Novo}

[BURKINA FASO, BURUNDI, BOTSWANA, BENIN, ANGOLA, ALGERIA] [ALGERIA, ANGOLA, BENIN, BOTSWANA, BURKINA FASO, BURUNDI] [ALGERIA, ANGOLA, BENIN, BOTSWANA, BURKINA FASO, BURUNDI] [ALGERIA, ANGOLA, BENIN, BOTSWANA, BURKINA FASO, BURUNDI] [ALGERIA, ANGOLA, BENIN, BOTSWANA, BURKINA FASO, BURUNDI] B r as ilia

* ///:D vodim enzionalni niz PODACI tipa S trin g je javan, pa se moe upotrebljavati i drugde. FIyw eightM apa m ora realizovati m etodu e n try S e t() koja zahteva nam ensku realizaciju i klase Set i klase M ap.Entry. Uloga zamajca je u tom e to svaki objekat tipa

Poglavlje 17: Detaljno razmatranje kontejnera

641

M ap.Entry jednostavno skladiti sam o svoj indeks, a ne stvarni klju i vrednost. Kada pozovete m etodu getKey( ) ili getV alue( ), ona e vam vratiti odgovarajui elem ent PODACI odreen pom ou tog indeksa. Klasa SkupStavki se stara da njena velicina ne bude vea od niza PODACI. D rugi deo zamajca realizovan je u program u SkupStavki.Iterator. Umesto da se za svaki par u nizu PODACI pravi objekat tipa Map.Entry, pravi se samo jedna stavka m ape (Map.Entry) za svaki iterator. O bjekat Stavka se upotrebljava kao prozor u podatke; on sadri sam o indeks statinog niza znakovnih nizova. Svaki p u t kada pozovete m etodu n e x t( ) za taj iterator, indeks u objektu Stavka poveava se za 1, tako da pokazuje na sledei par elemenata, i potom se jedini objekat tipa Stavka tog Iteratora vraa iz m etode next( ).3 M etoda select( ) pravi objekat tipa FlyweightMapa koji sadri SkupStavki eljene veliine, a ovaj se koristi u m etodam a glavni grado vi( ) i im en a( ) pokazanim u metodi m a in ( ). Za neka ispitivanja, ograniena veliina klase C ountries predstavlja problem . Na isti
nain m oem o napraviti inicijalizovane nam enske kontejnere koji im aju skup podataka proizvoljne veliine. Sledea klasa je lista proizvoljne veliine koju (zapravo) unapred inicijalizujemo Integer podacim a:
//: net/mindview/uti1/BrojackaListaIntegera.java // Lista proizvoljne duine s probnim podacima. package net.mindview.util; import java.util public class BrojackaListalntegera extends AbstractList<Integer> { private int velicina; public B r o j ac ka Li st al nt eg era(int velicina) { this.velicina = velicina < 0 ? 0 : velicina;

}
public Integer get(int indeks) { return Intege r. va lu eO f(in d e k s ) ;

}
public int size() { return velicina; } { public static void main(String[] args)

System.out.println(new Br oj ac ka Li st aI nt eg era (3 0) );

}
} /* Ispis: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,

20

21, 22, 23, 24, 25, 26, 27, 28, 29]

* ///:M orate realizovati m etode g e t( ) i size( ) da biste od natklase AbstractList napravili listu sam o za itanje. O pet sm o upotrebili reenje sa zamajcem: g e t( ) pravi vrednost kada je zatraite, pa lista zapravo ne m ora da bude popunjena.
Mape u pakctu java.util prave g ru p n e kopije m eto d am a g etK ey ( ) i g etV a lu e( ) za Mape, pa ovo radi. Kada bi nam enska m apa sam o kopirala celu Map.Entry, ovakav p ristu p bi prouzrokovao problem .

642

Misliti na Javi

Evo jedne m ape koja sadri unapred inicijalizovane jedinstvene objekte tipa In teg er i String; i ona moe biti proizvoljne veliine:
//: net/mindview/util/BrojackaMapaPodataka.java // Mapa neograniene duine s probnim podacima. package net.mindview.util; import java.util public class BrojackaMapaPodataka extends AbstractMap<Integer,String> { private int velicina; private static Stringf] znakovi = " A B C D E F G H I J K L M N O P Q R S T U V W X Y Z " split(" "); public BrojackaMapaPodataka(int velicina) if(velicina < 0) this.velicina = 0; this.velicina = velicina; {

1
private static class Stavka implements Ma p.Entry<Integer,String> { int indeks; Stavka(int indeks) { this.indeks = indeks; } public boolean equals(Object o) { return Integer.valueOf(i n d e k s ) .e q u a l s ( o ) ;

}
public Integer getKey() public String getValue() return znakovi[indeks % znakovi.length] + Integer.toString(indeks / z n a k o v i. le ng th ); { return indeks; { }

}
public String setValue(String vrednost) { throw new Un su pp or te dO pe ra ti onE xc ep ti on ();

}
public int hashCode() { return Integer.valueOf(indeks).hashCode();

} }
public S e t < M a p . E n t r y < I n t e g e r , S t r i n g entrySet() // inicijalizacije: Se t< Ma p . E n t r y < I n t e g e r , S t r i n g stavke = new L i n k e d H a s h S e t < M a p . E n t r y < I n t e g e r , S t r i n g ( ) ; for(int i = 0; i < velicina; stavke.add(new Stavka(i)); return stavke; i++) { // LinkedHashSet zadrava poredak ustanovljen prilikom

}
public static void main(String[] args) { System.out.println(new Br oj ac ka MapaPodataka(60));

Poglavlje 17: Detaljno razmatranje kontejnera

643

} / * I s p is : {0=A0, 1=B0, 2=C0, 3=D0, 4=E0, 5=F0, 6=G0, 7=H0, 11 =L0, 12=M0, 13=N0, 14=00, 15=P0, 16=Q0, 17=R0, 21=V0, 22=W0, 23=X0, 24=Y0, 25=Z0, 26=A1, 27=B1, 31=F1, 32=G1, 33=H1, 34= 11, 35 = J1, 36=K1, 37=L1, 41=P1, 42=Q1, 43=R1, 44=S1, 45=T1, 46=U1, 47=V1, 51=Z1, 52=A2, 53=B2, 54=C2, 55= 02, 56=E2, 57=F2,

8= 10 , 9 = J0 , 10=K0, 18=S0, 19=T0, 20=U0, 28=C1, 29=D1, 30=E1, 38=M1, 39=N1, 40= 01, 48=W1, 49=X1, 50=Y1, 58=G2, 59=H2}

* ///:Ovde se koristi LinkedHashSet um esto da sm o napravili nam enski po d tip natklase Set, pa Flyweight nije p o tp u n o realizovan.

Veba 1: (1) Napravite listu (pokuajte da sainite i ArrayList i LinkedList) i popunite je pom ou klase Countries. Listu uredite i ispiite, zatim na nju vie p u ta prim enite CoIlections.shuffle( ) i svaki p u t je ispiite, da biste videli kako m etoda shuffle( ) svaki p u t drugaije ispremea listu. Veba 2: (2) Napravite m apu i skup koji sadre im ena svih drava koja poinju na A. Veba 3: (1) Pom ou klase Countries, vie p u ta popunite neki objekat tipa Set istim podacim a i okaite da dobijeni skup (Set) im a sam o po jedan prim erak svakog objekta. Isprobajte to s klasama HashSet, LinkedHashSet i TreeSet. Veba 4: (2) Napravite inicijalizator kolekcije koji otvara datoteku i deli je na rei m etodom TextFile( ) (interfejsa Collection), a zatim te rei koristi kao izvor podataka za dobijeni kontejner. Pokaite da to radi.

Veba 5: (3) Izm enite program BrojackaMapaPodataka.java tako da p o tp u n o realizuje projektni obrazac Flyweight tako to dodaje nam ensku klasu SkupStavki, p o p u t one u program u Countries.java.

Funkcije interfejsa Collection


U tabeli koja sledi prikazano je sve to moete da uradite s kolekcijama (nisu obuhvaene m etode koje se autom atski nasleuju iz klase Object), to znai sa skupom ili listom. (Interfejs List ima i dodatne funkcije.) Mape nisu izvedene iz interfejsa Collection, pa e biti razm otrene zasebno.
boolean add(T)

Obezbeduje da argument generikog tipa T bude u kontejneru. Vraa false ako ne doda argument. (Ovo je opciona metoda, opisana u sledeem odeljku.) Dodaje kolekciji sve elemente iz argumenta. Vraa true ako je dodat neki element. (Opciona metoda.) Uklanja sve elemente iz kontejnera. (Opciona metoda.) Vraa true ako kontejner sadri argument. Vraa true ako kontejner sadri sve elemente iz argumenta. Vraa true ako u kontejneru nema elemenata.

boolean addAllf Kolekcija<? extends T>) void clear( ) boolean contains(T) boolean containsAII( Kolekcija<?>) boolean isEmpty( )

64 4

Misliti na Javi

lterator<T> ite rato rf) boolean remove(Object) boolean removeAII) Kolekcija<?>) boolean retainAllf Kolekcija<?>) int size( J ObjectfJ to A rra y () <T> T[] toArray(T[J a)

Vraa lterator<T> koji se moe koristiti za kretanje kroz elemente kontejnera. Ako se argument nalazi u kontejneru, uklanja sejedna instanca tog elementa. Vraa true akoje neto uklonjeno. (Opciona metoda). Uklanja sve elemente koje sadri argument. Vraa true ako je neto uklonjeno. (Opciona metoda). Zadrava samo elemente koji se nalaze u argumentu (skupovni presek). Vraa true ako se neto promenilo. (Opciona metoda). Vraa broj elemenata u kontejneru. Vraa niz koji sadri sve elemente kontejnera. Vraa niz koji sadri sve elemente kontejnera ijije tip isti kao tip niza a, a ne prosto Object. (Niz se mora konvertovati u odgovarajui tip).

Im ajte u vidu da ne postoji m etoda g e t( ) za izbor elem enta nasum inim pristupom . Razlog je to to interfejs Collection sadri i Set koji odrava svoj unutranji poredak (pa bi pretraivanje nasum inim p ristu pom bilo besm isleno). Dakle, za pregled elemenata kolekcije m orate upotrebiti iterator. U narednom prim eru prikazane su sve te metode. Iako one rade sa svim klasama koje realizuju interfejs Collection, kao najmanji zajedniki imenitelj" upotrebljen je ArrayList.
//: kontejneri/MetodeKolekcija.java // ta sve moete da uradite sa svim kolekcijama. import java.util import net.mindview.util.*; import static n e t . mi nd vi ew .u ti l.Print.*; public class MetodeKolekcija { public static void main(String[] c. ad dA l1 (Countries.imen a( 6)); c.addC'deset'1) ; c.ad d( "j ed an ae st "); pr i n t ( c ) ; // Pravljenje niza od liste: Object[] niz = c.toArray(); // Pravljenje niza tipa String od liste: String[] str = c.toArray(new S t r i n g [0]); // Pronalaenje najveeg i najmanjeg elementa; // to podrazumeva razliite metode, u zavisnosti // od naina realizacije interfejsa Comparable: printC'Collections.max(c) print("Collections.min(c) = " + Col lections.max(c)) ; = " + C o l l ec ti on s. mi n( c) ); args) { C o l 1ection<String> c = new Ar ra yL i s t < S t r i n g > ( ) ;

// Dodavanje jedne kolekcije drugoj Collection<String> c2 = new ArrayList<String>(); c 2 . a d d A U (Countries. imen a( 6)); c. ad dA l1 (c2); print(c);

c . remove(Countrie s . PODACI[0] [0 ]) ;

Poglavlje 17: Detaljno razmatranje kontejnera

645

print(c); c.remove(Countries.PODACI[l] [0]); print(c); / / Uklanjanje svih komponenata koje se nalaze // u kolekciji - argumentu: c.removeAll(c2); print(c); c. ad dA l1 (c 2); print(c); // Da 1 i se odredeni element nalazi u ovoj kolekciji? String vre = C o u n t r i e s . P O D A C I [3][0]; print("c.contains(" + vre + ") = " + c. co nt ai ns (v re )); // Da li je kolekcija u ovoj kolekciji? print("c.containsAll(c2) = " + c. co nt ainsAll(c2)); Co ll ec tion<String> c3 = ( (Li st<Stri ng>)c).subList (3, 5); // Zadravanje samo onih elemenata koji se nalaze // u skupovima c2 i c3 (presek s k u p o v a ) : c 2 .r et ai nA l1 (c 3); print(c2); // Odbacivanje svih elemenata iz skupa c2 // koji se pojavljuju i u skupu c3: c2.removeAl 1 (c 3); print("c2.isEmpty() = " + c 2 .isEm pt y( )); c = new Ar r a y L i s t < S t r i n g > ( ) ; c.addAll(Countries.imena(6)); print(c); c.clear(); // Uklanjanje svih elemenata print("posle c.clear():" + c);

I
} /* Ispis: [ALGERIA, ANGOLA, BENIN, B0TSWANA, BURKINA FASO, B U RU ND I, deset, jedanaest] C o l 1ections.max(c) = deset Collections.min(c) = ALGERIA [ALGERIA, ANGOLA, BENIN, B0TSWANA, BURKINA FASO, B U RU ND I, deset, jedanaest, ALGERIA, ANGOLA, BENIN, B0TSWANA, BURKINA FASO, BURUNDI] [ANGOLA, BENIN, B0TSWANA, BURKINA FASO, BURUNDI, deset, jedanaest, ALGERIA, ANGOLA, BENIN, B0TSWANA, BURKINA FASO, BURUNDI] [BENIN, B0TSWANA, BURKINA FASO, BURUNDI, deset, jedanaest, ALGERIA, ANGOLA, BENIN, B0TSWANA, BURKINA FASO, BURUNDI] [deset, jedanaest] [deset, jedanaest, ALGERIA, ANGOLA, BENIN, B0TSWANA, BURKINA FASO, BURUNDI] c.contains(BOTSWANA) [ANGOLA, BENIN] c2.isEmpty() = true [ALGERIA, ANGOLA, BENIN, B0TSWANA, BURKINA FASO, BURUNDI] posle c . c l e a r O : [] = true c. co nt ai ns Al1 (c2) = true

* ///:-

646

Misliti na Javi

Prave se ArrayListe koje sadre razliite skupove podataka i bivaju svedene navie na Collection objekte, pa je jasno da se sem interfejsa Collection ne upotrebljava nita drugo. U m etodi m a in ( ), pom ou jednostavnih vebi prikazane su sve m etode klase Collection. N aredni odeljci u ovom poglavlju opisuju razliite realizacije interfejsa List, Set i Map. Zvezdicom (*) je uvek oznaena realizacija koju bi trebalo podrazum evano izabrati. Opisi starih klasa Vector, Stack i H ashtable dati su tek na kraju p o g lav lja- iako ne bi trebalo da ih sam i upotrebljavate, sretaete se s njim a u starom kodu.

Opcione operacije
M etode koje obavljaju razne vrste dodavanja i uklanjanja jesu opcione operacije interfejsa Collection. To znai da klasa koja realizuje interfejs ne m ora da sadri funkcionalne definicije tih m etoda. To je veom a neobian nain definisanja interfejsa. Kao to ste videli, u objektno orijentisanom program iranju interfejs je vrsta ugovora. Pom ou njega se kae: Bez obzira na to kako odluite da realizujete ovaj interfejs, jem im da ovom objektu m oete da aljete ove poruke.4 Ali opciona operacija kri to tem eljno obeanje: ne sam o da pozivanje ovih m etoda nee uraditi nita korisno, ve e i izazvati izuzetak! Kao da sm o izgubili bezbednost tipova u vreme prevoenja. Nije sve ba tako strano. U kolekciji, listi, skupu ili m api, prevodilac vas jo uvek ograniava na pozivanje iskljuivo onih m etoda koje se nalaze u tom interfejsu. To je ipak bolje od dinam ikih jezika koji pruaju m ogunost pozivanja svake m etode za svaki objekat, da bi se tek pri pokretanju program a ustanovilo da li pozvana m etoda uopte moe da radi5. O sim toga, m noge m etode iji je argum ent kolekcija, iskljuivo itaju tu kolekciju: nijedna m etoda za itanje kolekcije nije opciona. Zato bi iko zadao da je m etoda opciona? Ovakav pristup spreava eksploziju broja interfejsa. Drugaiji naini projektovanja biblioteke kontejnera uvek se zavre sa nerazm rsivom gom ilom interfejsa koji opisuju sve varijacije glavne tem e i zbog toga ih je teko savladati. Nije ak ni m ogue obuhvatiti sve specijalne sluajeve u obliku interfejsa, poto uvek m ogu da se izmisle novi interfejsi. P ristupom operacija koje nisu podrane postie se vana svrha (avine biblioteke kontejnera: kontejneri se lako savladavaju i koriste; operacije koje nisu podrane specijalan su sluaj koji se moe nauiti i kasnije. M eutim , da bi takav pristup delovao, p otrebno je sledee: I . Izuzetak tipa U n su p p o rted O p eratio n E x cep tio n m ora da sejavlja retko, tj. u veini ldasa trebalo bi da rade sve operacije, a sam o u specijalnim sluajevima operacija ne bi trebalo da bude podrana. To vai u Javinoj biblioteci kontejnera poto klase koje najee koristite (A rrayList, LinkedList, H ashSet i H ashM ap), kao i druge kon kretne realizacije, podravaju sve operacije. Ovakav pristup om oguuje da napravite novu kolekciju, da pri tom ne obezbedite definicije svih m etoda u interfejsu C ollection, a da je ipak uklopite u postojeu biblioteku.
4 5 Terrnin ,,interfejs je ovde u p o treb ljen tako da obuhvata i fo rm alnu rezervisanu re in terface i optije znaenje: m eto e p o d ran e u svim klasam a i potklasam a". M ada ovo zvui u d n o i m oda beskorisno kada je o p isan o na taj nain, videli ste, naroito u poglavlju Podaci o tipu , da takvo d inam iko p on aanje m oe biti veom a m ono.

Poglavlje 17: Detaljno razmatranje kontejnera

647

2. Ako operacija nije podrana, obino postoji velika verovatnoa da e se izuzetak

tipa U nsupportedO perationException pojaviti u vreme testiranja, a ne kada prodate program korisniku. Najzad, on ukazuje na program ersku greku: korienje realizacije na nepravilan nain. Im ajte u vidu da se nepodrane operacije m ogu otkriti tek u vreme izvravanja, te stoga predstavljaju dinam iku proveru tipova. Ako ste koristili jezik sa statinom proverom tipova kao to je C + +, m oda vam i Java izgleda kao jezik u kojem se tipovi proveravaju statino. Java svakako im a statinu proveru tipova, ali i znatnu koliinu dinam ike provere tipova, pa je teko rei da je tano jednog ili drugog tipa. Kada postanete svesni toga, poeete da uoavate i druge sluajeve dinam ike provere tipova u Javi.

Nepodrane operacije
U Javi su est izvor nepodranih operacija kontejner i pripadna struktura podataka neprom enljive duine. Takav kontejner (u gornjem prim eru, listu) od niza proizvodi m etoda A rrays.asList( ). S druge strane, vi odluujeteda li e bilo koji kontejner (ukljuujui tu i m ape) generisati izuzetke U nsupportedO perationException zbog upotrebe nepromenljivih m etoda u Idasi Collections. P rim er koji sledi sadri oba sluaja:
//: kontejneri/Nepodrzane.java // Nepodrane operacije u Ja vi ni m kontejnerima. import java.util public class Nepodrzane { static void test(String prk, List<String> lista) System .o ut .p ri nt ln (" " + prk + " "); Collection<String> c = lista; C o ll ection<String> podLista = 1 ista.p od List a ( l , 8 ) ; // Kopija podliste: Collection<String> c2 = new Ar ra yL is t< St ri ng >( pod Li st a); try { c . re ta in Al1 (c2); } catch(Exception e) { System.out .pri ntl n("retai nAl 1 (): 1 1 + e); {

}
try { c . remo ve Al1 (c2); ) catch(Exception e) { S y s t em .o ut .p ri nt ln ("r em ov eA ll(): " + e ) ;

}
try { c.clear(); } catch(Exception e) { S y s t e m . o u t .p ri nt ln ("c le ar (): " + e ) ;

}
try { c.add("X"); } catch(Exception e) { S y s t e m . o u t .pr in t l n ( " a d d (): " + e ) ;

}
try { c . a d d A l l ( c 2 ) ; } catch(Exception e) { S y s t e m . o u t .p r intln("addAl 1 (): " + e ) ;

}
try { c. r e m o v e ( " C " ) ; } catch(Exception e) { S y s t e m . o u t .pri ntl n ( " r e m o v e O : " + e ) ;

648

Misliti na Javi

// Metoda List.set() menja vrednost, ali // ne menja veliinu strukture podataka: try { lista.set(0, X"); } catch(Exception e) { System.out.p ri nt ln ("L is t. se t( ): " + e ) ;

} }
public static void main(String[] args) List<String> lista = Arrays.asList("A B C D E F G H I J K test("Arrays.asList()", l i s t a ) ; test("unmodifiableList()", Co l 1ecti o n s .unmodi fi abl eLi s t ( new ArrayList<String>(lista))); L".split(" ")); test("Promenljiva kopija", new Ar ra yL is t< St ri ng >( lis ta )); {

}
} /* Ispis: Promenljiva kopija Arrays.asList() r e t a i n A l l (): j a v a . 1ang.UnsupportedOperationException r e m o v e A U (): java.lang.UnsupportedOperationException c l e a r ( ) : java.lang.UnsupportedOperationException a d d ( ) : j a v a . 1ang.UnsupportedOperationException a d d A U (): java.lang.UnsupportedOperationException r e m o v e ( ) : java.lang.UnsupportedOperationException unmodifiableList() reta in Al1 (): j a va .lang.UnsupportedOperationException r e m o v e A U (): j a v a . 1ang.UnsupportedOperationException c l e a r ( ) : j a v a . l ang.UnsupportedOperationException a d d ( ) : java.lang.UnsupportedOperationException ad d A l 1(): j a v a . 1ang.UnsupportedOperationException r e m o v e ( ) : java.lang.UnsupportedOperationException L i s t . s e t ( ) : java.lang.UnsupportedOperationException

* ///:Poto m etoda A rrays.asList( ) pravi listu od niza odredene duine, ima smisla da bud u podrane sam o one operacije koje ne menjaju d u in u niza. Svaka m etoda koja bi prom enila d u inu pripadne struktu re podataka izazvala bi izuzetak UnsupportedO perationException koji ukazuje na poziv nepodrane m etode (greku program era). Vodite rauna o tom e da svakoj kolekji uvek moete da prosledite rezultat m etode Arrays.asList( ) kao konstruktorski argum ent (ili da pozovete m etodu addA lI( ), ili statinu m etodu C ollections.addA ll( )) da biste napravili pravi kontejner koji om oguuje upotrebu svih m etoda - to se vidi iz prvog poziva m etode test( ) u funkciji m a in ( ). Takav poziv proizvodi novu strukturu podataka prom enljive duine. Nepromenljive m etode klase Collections omotavaju kontejner posrednikom koji izaziva UnsupportedOperationF.xception ako izvrite bilo koju operaciju koja na bilo koji nain menja taj kontejner. Cilj korienja tih m etoda jeste pravljenje konstantnog kontejnerskog objekta. Naveemo kasnije oelokupan spisak nepromenljivih m etoda klase Collections.

Poglavlje 17: Detaljno razmatranje kontejnera

649

Poslednji b lo k try u m etodi te s t( ) ispituje m e to d u s e t( ) koja je deo klaseL ist.T o je zanimljivo, zato to vidite kako dobro doe granularnost tehnike nepodrana operacija dobijeni interfejs m oe da se razlikuje za je d n u m etodu izm eu objekta koji vraa A rrays.asList( ) i objekta koji vraa m etoda Collections.nepromenljivaLista( ). A rrays.asList( ) vraa listu fiksne duine, dok C ollections.neprom enljivaLista( ) proizvodi listu koja se ne m oe menjati. Kao to vidite iz rezultata, m oete m o difikovati elem ente liste koju vraa m etoda A rrays.asList( ), jer to ne bi naruilo neprom enljivu duinu te liste. S druge strane, jasno je da rezultat m etode unm odifiableList( ) ne bi trebalo da bude prom enljiv ni na koji nain. Da sm o koristili interfejse, bila bi p otreb n a dva dodatna interfejsa, jedan s funkcionalnom m etod o m s e t( ) i drugi bez nje. Za razne neprom enljive podtipove od Collection bili bi potreb n i do d atn i interfejsi. U dokum entaciji za m etodu koja koristi kolekciju, listu, skup ili m apu kao argum ent treba odrediti koje opcione m etode m oraju da b u d u realizovane.

Veba 6: (2) O bratite panju na to da klasa List im a dodatne opcione operacije koje Collection ne obuhvata. Napiite verziju program a Nepodrzane.java za testiranje tih dodatnih opcionih operacija.

Funkcije liste
Kao to ste videli, prost tip List se prilino lako koristi. Iako najee koristite m etodu a d d ( ) za um etanje objekata, m etodu g e t( ) za uzim anje objekata jedan po jedan i iterat o r ( ) za dobijanje iteratora niza, postoje i druge m etode koje m ogu da budu korisne. sta Svaka od m etoda u prim eru koji slei obavlja drugu grupu aktivnosti: ono to svaka lim oe da uradi (osnovniTest( )), kretanje po listi pom ou iteratora (k retanjeIteratoroin( )) u odnosu na izm enu objekata pom ou iteratora (prom enalteratorom f )), prikaz dejstva obrade listi (vidljivTest( )), i operacije dostupne sam o u ulananim listam a (LinkedList):
//: kontejneri/Liste.java

// ta se sve moe raditi s listama. import ja v a . u t i 1 . import net.mindview.util.*; import static ne t. mindview.uti1 .P r i n t .*; public class Liste { private static boolean b; private static String s; pri vate static i nt i ; private static Iterator<String> it; private static ListIterator<String> lit; public static void os novniTest(List<String> a) { a.add(l, "x"); // Dodavanje na lokaciju 1 a.add("x"); // Dodavanje na kraj // Dodavanje kolekcije: a . a d d A U (Countries.imena(25));

650

Misliti na Javi

// Dodavanje kolekcije poev od lokacije 3: a . ad dA l1(3, C o un tr ie s. im en a( 25 )); b = a . c o n t ai ns C' l" ); // Da li je tu? // Da li je cela kolekcija tu? b = a.containsAl l( Co un tri es .i me na (2 5) ); // Liste omoguuju nasumian pristup, to je brzo // kod ArrayList, sporo kod LinkedList: s = a.get(l); // Uzmi objekat (odreenog tipa) na lokaciji 1 i = a . i n d e x O f ("1"); // Koji je indeks objekta? b = a.isEmpty(); // Da li ima elemenata? it = a. it er at or (); // Obian Iterator lit = a. li st It er at or (); // Listlterator lit = a . l i st It er at or (3 ); // Poni od lokacije 3 i = a . la st ln de x0 f( "l "); // Poslednje poklapanje a.remove(l); // Ukloni element na lokaciji a. re mo ve (" 3" ); // Ukloni ovaj objekat a.set(l, "y"); // Postavi lokaciju 1 na "y" // Zadri sve to je u argumentu // (presek dva s k u p a ) : a.reta in Al l( Co un tr ies .i me na (2 5) ); // Obrii sve to se nalazi u argumentu: a . re mo ve Al1 (Countri e s .i m ena(25)); i = a.size(); // Kolika je lista? a.clear(); // Obrii sve elemente { 1

1 /

public static void kretanjelteratorom (List<String> a) ListIterator<String> it = a.listIterator(); b = it .h as Ne xt (); b = it.hasPreviousO ; s = it.next(); i = i t . n ex tl nd ex (); s = it .p re vi ou s( ); i = i t . p r e vi ou sI nd ex ();

}
public static void promenaIteratorom(List<String> a) { ListIterator<String> it = a. 1 i s t l t e r a t o r O ; it .a dd (" 47 "); // Mora se pomeriti na element posle i t . n ex t( ); // Obrii element iza upravo napravljenog: i t .r e m o v e ( ) ; // Mora se pomeriti i t .n e x t (); // Promeni element iza obrisanog: i t .set ("47"); na element posle remove(): a d d ():

}
public static void vidljivTest(List<String> a) { print(a);

Poglavlje 17: Detaljno razmatranje kontejnera

651

List<String> b = Countrie s. im en a( 25 ); print("b = " + b ) ; a.addAl1 (b); a.addAl1 (b); p r in t( a); // Umetanje, uklanjanje i zamena elemenata // pomou Listlteratora: ListIterator<String> x = a.listIterator(a.size()/2); x. a d d( "j ed an "); pr i n t ( a ) ; pr in t( x. ne xt () ); x.remove(); p r in t( x. ne xt () ); x . se t( "4 7" ); p r in t( a); // Kretanje kroz listu unazad: x = a. l i s t l t e r a t o r ( a . s i z e O ) ; wbile(x.hasPrevious()) printnb(x.previous() + " "); print(); print("metoda vidljivTest goto va);

)
// Postoje operacije koje mogu obaviti // samo ulanane liste: public static void testirajUlancanu() 1 1 .addAll(Countri es .imena(25)); pri nt (11); // Lista kao stek, stavljanje na stek: 1 1 .addFirst("jedan"); 11.addFi r s t( "d va "); pri nt (11 ); // "Zavirivanje" na vrh steka: print (11 .getFi r s t ()); // Kao skidanje sa steka: print(l 1 . r e m o v e F i r s t O ) ; pri n t (11 .removeFi r s t ()) ; // Lista kao red za ekanje, // izvlaenje elemenata iz "repa": pri n t (11.r e mo ve La st()); print(l 1); { LinkedList<String> 11 = new Li nk ed List<String>();

}
public static void main(String[] args) { // Pravljenje i popunjavanje nove liste svaki put: osnovniTest( new LinkedList<String>(Countries.imena(25))); osnovniTest( new Ar ra yList<String>(Countries.imena(25)));

652

Misliti na Javi

kretanjeIteratorom( new Li nk ed Li st <S tr in g> (Co un tr ie s. im en a( 25 )) ); kretanjeIteratorom( new ArrayLi st<Stri n g > ( C o u n t r i e s .i m e n a (25))); promenaI te ra to ro m( new Li nk ed Li st <S tr in g> (Co un tr ie s. im en a( 25 )) ); promenaIteratorom( new A r r a y L i s t < S t r i ng >( Cou nt ri es .i me na (2 5) )); vidljivTest( new Li nk ed Li st <S tr in g> (Co un tr ie s. im en a( 25 )) ); te st ir aj Ul an ca nu ();

}
} /* (Pokrenite da biste videli rezultat)

* ///:M etode osnovniTest( ) i k retan jeIterato ro m ( ) pozivaju se sam o da bi se prikazala odgovarajua sintaksa, a njihova p o v ratna vrednost se nigde ne koristi, iako se pam ti. U nekim sluajevima, povratna vrednost se ne p am ti jer se obino ne koristi. Pre nego to upotrebite ove funkcije, prouite sve naine njihovog korienja u dokum entaciji na Web adresi java.sun.com . Veba 7: (4) N apravite A rrayL ist i L inkedList i popunite obe generatorom C o u n trie s.iin e n a (). Ispiite svaku Iistu pom ou obinog iteratora, zatim L istlteratorom um etnite jednu listu na svaku dru gu lokaciju druge liste. Potom obavite um etanje poev od kraja prve Iiste unazad. Veba 8: (7) N apravite generiku klasu jednostruko ulanane liste i nazovite je SList. Neka ona ne realizuje interfejs List, tako je jednostavnije. Svaki Link objekat u listi treba da sadri referencu na sledei elem ent liste, ali ne na prethodni (za razliku od nje, LinkedList je dvostruko ulanana lista, to znai da odrava veze u oba sm era). Napravite sopstveni S L istlterato r koji ne realizuje L istlterato r, opet zbog jednostavnosti. Neka jedina m etoda u klasi SList sem to S tr in g ( ) bude ite r a to r ( ) koji proizvodi SListlterator. Jedini nain um etanja i uklanjanja elem enata iz objekta tipa SList om oguuje SListlterator. Napiite kod koji pokazuje rad liste Slist.

Skupovi i redosled skladitenja


Prim eri skupova u poglavlju uvanje objckata predstavljaju d obar uvod u operacije koje se m ogu obaviti sa osnovnim skupovim a. M eutim , u tim prim erim a bili su prigodno korieni unapred definisani Javini tipovi kao to su Integer i String, koji su projektovani za prim enu u kontejnerim a. Kada pravite sopstvene tipove, imajte u vidu da Set zahteva m etodu za odravanje redosleda sm etanja elem enata. Nain odravanja redosleda sm etanja m enja se u zavisnosti od realizacije interfejsa Set. Dakle, razliite realizacije interfejsa Set ne sam o da imaju razliita ponaanja, nego su razliiti i zahtevi u pogledu tipa objekta koji se moe staviti u odreeni Set:

Poglavlje 17: Detaljno razmatranje kontejnera

653

Set |interfejs)

Svaki element koji dodajete u skup mora da budejedinstven; u suprotnom, Set ne dodaje duplikat elementa. Objekii koji se dodaju u skup moraju da definiu metodu eq u a ls[J kojom se utvruje jedinstvenost objekta. Set ima istovetan interfejs kao Collettion. Interfejs skupa ne garantuje odravanje elemenata u odreenom rasporedu. Klasa koja se koristi za skupove gde je vano brzo pronalaenje elemenata. Objekti moraju da definiu i metodu hash C o d ef). Ureden skup u obliku stabla. Iz ovog skupa mo/cte da izdvojite niz ureden u odreenom redosledu. Elementi moraju da definiu i interfejs
Comparable.

HashSet* TreeSet

LinkedHashSet

Ima brzinu pretraivanja klase HashSet. ali iraerno odrava redosled kojim su elementi bili umetani (redosled umetania) pomou ulanane liste. Zato se rezultati pojavljuju u redosledu umeianja kada iterirate kroz Set. Elementi moraju definisati i metodu h as h C o d e )).

Zvezdica pored HashSet pokazuje sledee: ako nem a dri,;>ih ogranienja, to treba da bude podrazum evani izbor zato to je optim alan po brzini. O brazac za definiciju m etode hashC od e( ) bie opisan u instavku poglavlja. eq u als( ) m orate da definiete i za skladitenje s transform acijom kljvi u (heiranje) i za skladitenje u stablu, ali je m etoda h ash C o d e( ) apsolutno neophodna saino ako e se klasa nalaziti u skupu tipa HashSet (to je verovatno, poto bi ova klasa ut- avnom trebalo da bude va prvi izbor kao realizacija za Set) ili LinkedHashSet. M e u j'n , d obar stil program iranja nalae da se uvek redefinie m etoda h ashC o de( ) ako se redeimie eq u als( ). Ovaj prim er ilustruje m etode koje m oraju biti definisane i ; bi se odreeni tip uspeno upotrebljavao sa odreenom realizacijom interfejsa Set:
//: ko nt ej ne ri /T ip ov iZ aSk up ov e.java // Metode potrebne za stavljanje sopstvenog tipa u 'kjp. import java.util class TipSkupa { i nt i ; public TipSkupa(int n) { i = n; ) public boolean equals(Object o) { return o instanceof TipSkupa && (i == ((TipSkupa)o).i);

1
public String toString() { return Integer.toStrin g ( i ); }

class TipHesiranja extends TipSkupa { public TipHesiranja(int n) { super(n); public int hashCode() { return i; } }

}
class TipStabla extends TipSkupa implements Co mp arable<TipStabla> {

654

Misliti na Javi

public TipStabla(int n) { super(n); } public int compareTo(TipStabla arg) { return (arg.i < i ? -1 : (arg.i == i ? 0 : 1));

} }
public class TipoviZaSkupove { static <T> Set<T> fill(Set<T> skup, Clas s< T> tip) try { for(int i = 0; i < 10; i++) skup.add( tip.getConstruct or (in t. cl as s) .n ew ln st an ce( i) ); } catch(Exception e) { throw new RuntimeE xc ep ti on (e ); {

}
return skup;

}
static <T> void test(Set<T> skup, Class<T> tip) fill(skup, tip); fill(skup, tip); // Pokuaj dodavanja duplikata fill(skup, tip); Sy stem.out.println(skup); {

}
public static void main(String[] args) { test(new HashSet<Ti pH es ir an ja> (), T i p H e s i r a n j a . c l a s s ) ; test(new LinkedHashSet<Tip H es ir a n j a > ( ) , T i p H e s i ranja.cla s s ) ; test(new TreeSet<TipStabla > ( ) , TipStabla.class); // Ovo ne radi: test(new Ha sh Se t<TipSkupa>(), T i p S k u p a . c l a s s ) ; test(new HashSet<TipStabla > (), T i p S t a b l a . c l a s s ) ; test(new Li nk ed HashSet<TipSkupa>(), T i p S k u p a . c l a s s ) ; test(new Li nk ed Ha sh Se t< Ti pS tab la >( ), Ti p S t a b l a .cla s s ) ; try { test(new T r e e Se t< Ti pS ku pa >( ), T i p S k u p a . c l a s s ) ; } catch(Exception e) { Sy st em .out.println(e.getMessage());

}
try { test(new Tree Se t< Ti pH es ir an ja> (), Ti p H e s i r a n j a . c l a s s ) ; } catch(Exception e) { System.out.printl n ( e. ge tM es sa ge () );

} }
} /* Ispis: (primer) [2, 4, 9, 8, 6, 1, 3, 7, 5, 0] [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] [9, 8, 7, 6, 5, 4, 3, 2, 1, 0] [9, 9, 7, 5, 1, 2, 6, 3, 0, 7, 2, 4, 4, 7, 9, 1, 3, 6, 2, 4, 3, 0, 5, 0,

Poglavlje 17: Deta^'no razmatranje kontejnera

655

8, 8, 8, 6, 5, 1] [0, 5, 5, 6, 5, 0, 3, 1, 9, 8, 4, 2, 3, 9, 7, 3, 4, 4, 0, 7, 1, 9, 6, 2, 1, 8, 2, 8, 6, 7] [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9] [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]


java.lang.ClassCastException: TipSkupa cannot be cast to j a va.lang.Comparable java.lang.ClassCastException: Ti pHesiranja cannot be cast to j a va.lang.Comparable

* ///:Da bi se pokazalo koje m etode su neophodne za odreeni skup i da bi se istovrem eno izbeglo dupliranje koda, napravljene su tri klase. O snovna klasa TipSkupa prosto skladiiti jedan int i ispisuje ga m etodom to S trin g ( ). Poto sve klase uskladitene u skupovima m oraju im ati m etodu eq u als( ), i ona je uskladitena u osnovnoj klasi. Jednakost je zasnovana na vrednosti celog broja i. TipHesiranja nasleuje TipSkupa i dodaje m etodu h ashC ode( ) neophodnu da bi objekat bio smeten u realizaciju interfejsa Set s transform isanim kljuem. T ipS tabla realizuje interfejs C om p arable koji je neophodan ako e objekat biti korien u ureenom kontejneru, kao to je SortedSet. (Jedina trenutno dostupna klasa koja ga realizuje je TreeSet.) O bratite panju na to da u m etodi co m p areT o () nisam koristio jednostavnu i oiglednu konstrukciju r e tu rn i-i2. Iako je to esta greka u program iranju, sve bi radilo kako treba sam o kada bi i i i2 bili neoznaeni celi brojevi (pod uslovom da Java im a rezervisanu re u n sig n ed za neoznaen ceo broj, to nije siuaj). Greka se pojavljuje za oznaen ceo broj koji nije dovoljno velik da predstavi razliku dva oznaena cela broja (tip int). Ako je i veliki pozitivan ceo broj, a j veliki negativan ceo broj, i-j e dovesti do prekoraenja i dae negativnu vrednost, to nije ispravno. O d m etode co in p areT o () obino oekujete da proizvodi prirodan redosled usaglaen s m etodom e q u a ls ( ). Ako e q u a ls () daje tru e za odreeno poreenje, onda bi co m p areT o () trebalo da da nulu kao rezultat istog poreenja, a ukoliko e q u a ls () daje false za neko poreenje, onda bi co m p areT o () za isto poreenje trebalo da da rezultat razliit od nule. U klasi TipoviZaSkupove, m etode f ill( ) i te s t ( ) definisane su pom ou generikih tipova da bi se spreilo dupliranje koda. Ponaanje skupa (objekta tipa Set) m etoda te s t( ) proverava s tri poziva m etode fill() za ispitni skup, pri em u pokuava da u njega ubaci duplikate objekata. Metoda f ill() prim a skup proizvoljnog tipa i Class objekat istog tipa. Pomou objekta tipa Class pronalazi konstruktor koji prim a celobrojni argum ent i poziva taj ko nstruktor da bi skupu dodavala elemente. Iz rezultata vidite da H ashSet uva elem ente u nekom m isterioznom poretku (koji em o objasniti u nastavku poglavlja), L inkedH ashSet uva elem ente u poretku kojim su bili um etnuti, a TreeSet uva elem ente u ureenom poretku (zbog naina realizacije metode co m p areT o (), to je sluajno opadajui poredak).

656

Misliti na Javi

Ukoliko pokuam o da upotrebim o tipove koji ne podravaju ispravno operacije sa skupovim a koji te operacije zahtevaju, sve se rui. Smetanje objekta tipa TipSkupa ili TipStabla koji ne obuhvata redefinisanu m etodu hashC ode( ) u bilo koju realizaciju s transform isanjem kljua rezultuje dupliranjem vrednosti, ime se kri osnovno pravilo skupova (interfejsa Set). To prilino obespokojava, poto se greka ne generie ak ni tokom izvravanja. M eutim, podrazum evana m etoda hashC ode( ) legitim na je, pa je takvo ponaanje dozvoljeno, uprkos tom e to je nekorektno. Jedini pouzdan nain da se obezbedi ispravnost takvog program a jeste ukljuivanje testiranja anotacijam a &unit u build sistem (vie inform acija potraite u dodatku na adresi http://M indV iew .net/B ooks/B etterJava). Ako u objektu tipa TreeSet pokuate da upotrebite tip koji ne realizuje interfejs Comparable, dobiete odreeniji rezultat: kada TreeSet pokua da upotrebi taj objekat kao ureen (jer m etoda com pareTo( ) definie neki poredak), bie generisan izuzetak.

SortedSet
Elem enti u kontejneru tipa SortedSet zajemeno su u ureenom poretku, zbog ega sledee m etode u interfejsu SortedSet m ogu da prue dod atn u funkcionalnost:

Com parator co m parato r( ): Proizvodi C om parator koji se upotrebljava za ovaj skup (Set) ili null za prirodan poredak. Object first(): Proizvodi najnii element. Object Ia st( ): Proizvodi najvii element. SortedSet subSet(odElement, doElement): Proizvodi prikaz ovog skupa sa elementim a od odElement, ukljuivo, do doEIement, iskljuivo. SortedSet headSet(doElement): Proizvodi prikaz ovog skupa sa elem entim a m anjim od doElement. SortedSet tailSet(odElement): Proizvodi prikaz ovog skupa sa elem entim a jednakim ili veim od elem enta odElement.
Evo jednostavnog prim era:
//: ko nt ej ne ri /P ri me rZ aSo rt ed Se t.java // ta se moe uraditi import java.util import static ne t. mi nd vi ew .u ti l.Print.*; public class PrimerZaSortedSet { public static void main(String[] args) C o l1ec ti ons.addAl1 (sortedSet, "jedan dva tri cetiri pet sest sedam osam" .s p l i t (" ")); pr in t( so rt ed Se t); String najmanji = sorted Se t. fir s t (); String najveci = sortedSet.last(); pr in t( na jm an ji); pri nt(naj ve ci); { SortedSet<String> sortedSet = new T r e e Se t< St ri ng >( ); sa objektom tipa TreeSet.

Poglavlje 17: Detaljno razmatranje kontejnera

657

Iterator<Stn'ng> it = so rt ed Se t. it er at or (); for(int i = 0; i <= 6; i++) { if(i == 3) najmanji = it.next(); if(i == 6) najveci = it.next(); else i t . n ex t( );

}
print(najmanji); p r in t( na jv ec i); print(sorted Se t. su bSe t( na jm an ji, n a j v e c i ) ) ; pr in t( so rt ed Se t. he adS et (n aj ve ci )); pri n t ( s or te dS et.tai1Set(najmanj i));

}
} /* Ispis: [cetiri, dva, jedan, osam, pet, sedam, sest, tri] cetiri tri osam tri [osam, pet, sedam, sest] [cetiri, dva, jedan, osam, pet, sedam, sest] [osam, pet, sedam, sest, tri]

* ///:Imajte u vidu da SortedSet znai ureeno u skladu s funkcijom poreenja objekta, a ne ureeno po redosledu um etanja. Redosled um etanja se m oe ouvati pom ou kontejnera LinkedHashSet.

Veba 9: (2) U potrebite Ranom Generator.String za popunjavanje objekta tipa TreeSet, ali uz abecedni redosled. Ispiite TreeSet da biste proverili redosled sortiranja. Veba 10: (7) Definiite sopstveni SortedSet pom ou objekta tipa LinkedList kao pripadne realizacije.

Redovi za ekanje
Seni u aplikacijama za paralelno izvravanje, Java SE5 je realizovala Queue (red za ekanje) samo u kontejnerim a LinkedList i PriorityQueue koji se razlikuju po ponaanju, a ne po perform ansam a. Sledi elem entarni prim er s veinom realizacija reda za ekanje (koje u ovom prim eru nee sve funkcionisati), ukljuujui tu i redove za ekanje napravljene za paralelno izvravanje. Elemente treba um etati od jednog kraja, a vaditi od drugog:
//: kontejneri/PonasanjeRedaZaCekanje.java // Poredi ponaanje nekih redova za ekanje import java.util.concurrent.*; import j a v a .ut i 1.*; import net.mindview.util.*;

658

Misliti na Javi

public class PonasanjeRedaZaCekanje { private static int brojac = 10; static <T> void test(Queue<T> queue, Generator<T> gen) for(int i = 0; i < brojac; i++) queu e. of fe r( ge n. ne xt( )); while(queue.peek() != null) System.out.print(queue.remove() + " "); S y s t e m .o ut .p ri nt ln (); {

}
static class Gen implements Generator<String> { S t r i n g [] s = ("jedan dva tri cetiri pet sest sedam " + "osam devet deset").split(" "); int i ; public String next() { return s[i ++]; } args) {

}
public static void main(String[] test(new Linked Li st <S tr in g> (), new Gen()); test(new PriorityQu eu e< St ri ng> (), new Gen()); test(new Arra yB lo ck in gQ ue ue <St ri ng >( co un t), new G e n ()); test(new Concurre nt Li nk ed Qu eue <S tr in g> (), new G e n ()) ; test(new LinkedBlocki ng Qu eu e<S tr in g> (), new Gen()); test(new PriorityBlocki ng Qu eue <S tr in g> (), new Gen());

}
} /* Ispis: jedan dva tri cetiri pet sest sedam osam devet deset osam pet etiri devet jedan sedam est deset tri dva cetiri deset devet dva jedan osam pet sedam sest tri cetiri deset devet dva jedan osam pet sedam sest tri cetiri deset devet dva jedan osam pet sedam sest tri osam pet etiri devet jedan sedam est deset tri dva

* ///:Vidite da objekat tipa Q ueue daje elem ente u istom poretku u kojem su u njega bili um etani, izuzev u redovim a za ekanje s prioritetom .

Redovi za ekanje s prioritetom


Osnovno objanjenje redova za ekanje s prioritetom nai ete u poglavlju uvanje objckata. Zanimljiviji problem je lista zadataka, gde svaki objekat sadri znakovni niz i po jednu vrednost prim arnog i sekundarnog prioriteta. Uredenje te liste takode je odredeno realizacijom interfejsa C oniparable:
//: kontejneri/ListaZadataka.java // Sloenija upotreba kontejnera PriorityQueue. import j a v a . u t i l .*; class ListaZadataka extends PriorityQueue <ListaZadataka.StavkaListeZadataka> {

Poglavlje 17: Detaljno razmatranje kontejnera

659

static class StavkaListeZadataka implements Comparable <StavkaListeZadataka> { private char primarni; private int sekundarni; private String item; public StavkaListeZadataka(String td, char p r i , int sek) { primarni = p r i ; sekundarni = sek; stavka = uraditi;

}
public int compareTo(StavkaListeZadataka arg) if(primarni > arg.primarni) return vrednost+1; if(primarni == arg.primarni) if(sekundarni > arg.sekundarni) return +1; else if(sekundarni == arg.sekundarni) return 0; return -1; {

}
public String toString() sekundarni + { return Character.toString(primarni) + " + stavka;

} }
public void add(String uraditi, char p r i , int sek) { super.add(new StavkaListeZadataka(uraditi, p r i , sek));

}
public static void main(String[] args) Li st aZ ad at ak a. ad d( "Ba citi smee", ListaZadataka.add("Nahraniti psa", ListaZadataka.add("Pokositi ListaZadataka.add("Zaliti travu", { ListaZadataka ListaZadataka = new Li st aZ ad at ak a(); 'C', 4); 'A', 2); 'B', 7); 'C', 3); 'A', 1); 'B', 1);

List aZ ad a t a k a . a d d ("Nahraniti ptiicu", travnjak",

L i st aZ ad at ak a. ad d("Nahraniti maku", w h i 1e (!Li s t a Z a d a t a k a .i s E m p t y ())

Sy st em .o ut .p ri nt ln (Li st aZ ad at ak a. re mo ve ());

}
} /* Ispis: Al: Zaliti travnjak A2: Nahraniti psa Bl: Nahraniti maku B7: Nahraniti ptiicu C3: Pokositi C4: Baciti travu smee

* ///:O bratite panju na to da su u redu za ekanje s prioritetom stavke sortirane autom atski.

660

Misliti na Javi

Veba 11: (2) Napravite klasu koja sadri objekat tipa Integer inicijalizovan m etodom java.util.Random na vrednost izm edu 0 i 100. Realizujte Comparable pom ou tog Integer polja. Popunite objekat tipa PriorityQueue objektim a vae klase i izvucite te vrednosti m etodom p o I l( ) da bi se pokazalo da kontejner proizvodi oekivani poredak.

Dvostrani redovi za ekanje


D vored (dvostrani red za ekanje, engl. double-ended queue, dequeue) jeste red za ekanje u koji elemente moete dodavati i iz njega ih uklanjati sa oba kraja. Kontejner LinkedList im a i m etode koje podravaju operacije u dvostranim redovim a za ekanje, ali standardne Javine biblioteke ne sadre eksplicitan interfejs za njih. Zato LinkedList ne moe da realizuje taj interfejs i nije mogue svoenje navie na interfejs Deque, dok je u prethodnom prim eru bilo mogue svoenje na Queue. M eutim , klasu tipa D eque m oete napraviti pom ou kompozicije i jednostavno eksponirati relevantne m etode kontejnera LinkedList:
//: net/mindview/uti1/ D v o r e d .java // Pravljenje dvostranog reda za ekanje o kontejnera LinkedList. package net.mindview.util; import ja v a . u t i l .*;

p u b l i c c la s s Dvored<T> { p r i v a t e L in ke d L is t< T > dvored = new Li nkedLi s t < T > ( ) ; p u b l i c v o id a d d F i r s t ( T e) { d v o r e d . a d d F i r s t ( e ) ; } p u b l i c vo id addLast(T e) { d v o r e d . a d d L a s t ( e ) ; } p u b lic T g e tF ir s t() { re tu rn d v o r e d .g e tF ir s t( ) ; } p u b lic T g e tLa st() { re tu rn d v o re d .g e tL a s t(); } p u b l i c T r e m o v e F ir s t ( ) { r e t u r n d v o r e d . r e m o v e F i r s t ( ) ; } p u b l i c T removeLast() { r e t u r n d v o r e d . r e m o v e L a s t( ) ; } p u b lic i n t s iz e () { retu rn d v o re d .s iz e O ; } p u b lic S trin g to S trin g O { re tu rn d v o re d .to S tr in g O ; } // I druge metode po p o t r e b i . . .

} ///= Ako ovaj Dvored upotrebite u sopstvenim program im a, verovatno ete m orati da dodate jo m etoda da bi postao upotrebljiv. Sledi jednostavna provera klase Dvored:
/ / : kon tejne ri/D vo red T e st.java im p o r t n e t . m i n d v i e w . u t i l . * ; im p o rt s t a t i c n e t . m i n d v i e w . u t i l . P r i n t . * ; p u b l i c c la s s DvoredTest { s t a t i c v o id t e s t P o p u n ja v a n ja ( D v o re d < I n te g e r > dvored) f o r ( i n t i = 20; i < 27; i+ + ) d v o re d .a d d F irs t(i); f o r ( i n t i = 50; i < 55; i++) d v o re d .a d d L a s t(i);

Poglavlje 17: Detaljno razmatranje kontejnera

661

public static void main(String[] args) testPopunj avan ja (d i); pr i n t ( d i ) ; while(di.size() print(); testPopu nj av an ja (d i); w h i l e ( d i .size() != 0) printnb(di.removeLast() + " "); != 0) pr i n t n b ( d i .r e mo v e F i r s t () + 1 1 ");

Dvored<Integer> di = new Dv or e d < I n t e g e r > ( ) ;

}
} /* Ispis:

[2 6 , 25, 24, 23, 22, 21, 20, 50, 51, 52, 53, 54] 26 25 24 23 22 21 20 50 51 52 53 54 54 53 52 51 50 20 21 22 23 24 25 26

* ///:Elementi se ree umeu na oba kraja i vade sa oba kraja, pa se Dvored koristi rede od obinog reda za ekanje (Queue).

Iscrpnije o Mapama
Kao to ste saznali u poglavlju uvanje objekata, osnovna nam ena m ape ( asocijativnog niza ) jeste da odrava parove klju-vrednost (asocijacije, pridruivanja, m apiranja, preslikavanja), tako da vrednost m oete pronai pom ou kljua. Standardna Javina biblioteka sadri razliite osnovne realizacije Mape: HashMap, TreeMap, LinkedHashMap, WeakHashMap, ConcurrentHashMap i IdentityHashMap. Svima im je zajedniki osnovni interfejs Map, a razlikuju se u ponaanju i efikasnosti, poretku uvanja i prezentovanja parova, roku uvanja objekata u mapi, radu m ape u vienitnim program im a i nainu utvrivanja jednakosti kljua. Iz broja realizacija interfejsa Map trebalo bi da zakljuite koliko je ova alatka vana. Da biste mogli bolje da shvatite M ape, pogledaem o kako se pravi asocijativni niz. Evo jedne izuzetno jednostavne realizacije:
/ / : k o n te jn e ri/ A s o c ija tiv n iN iz .ja v a / / K lju e v e p r i d r u u j e v r e d n o stim a . im p o rt s t a t i c n e t . m i n d v i e w . u t i 1 . P r i n t . * ; p u b l i c c la s s A s o c i j a t i v n i N i z < K , V > { p riv a te 0 b je c t [] [] p a ro vi; p r i v a t e i n t in d e k s ; p u b l i c A s o c i j a t i v n i N i z ( i n t du zin a ) p a ro v i = new O b je c t [ d u z in a ] [ 2 ] ;

}
p u b l i c v o id p u t ( K k l j u c , V v r e d n o s t) { i f ( i n d e k s >= p a r o v i . d u z i n a ) th ro w new A r r a y I n d e x 0 u t 0 f B o u n d s E x c e p t io n ( ) ;

662

Misliti na Javi

p a ro vi [in deks+ +] = new O b j e c t [ ] {

k lju c ,

v re d n o s t } ;

}
PSuppressWarnings("unchecked") p u b l i c V g e t(K k l j u c ) { f o r ( i n t i = 0; i < in d e k s ; i+ + ) if( k lju c .e q u a ls (p a ro v i[i][0 ])) return ( V ) p a r o v i[ i] [1 ]; r e t u r n n u l l ; / / K l j u n i j e pronaen

}
public String t o S t r i n g O {

S t r i n g B u i l d e r r e z u l t a t = new S t r i n g B u i 1d e r ( ) ; f o r ( i n t i = 0; i < in d e k s ; i+ + ) { rezul tat.append(parovi [ i ] [ 0 ] . t o S t r i n g O ) ; rez u lta t.a p p e n d (" : " ) ; re z u lta t.a p p en d (p a ro v i [ i ] [1] . t o S t r i n g O ) ; i f ( i < indeks - 1) re z u lta t.a p p e n d ("\n ");

}
retu rn r e z u lt a t . t o S t r in g O ;
1

/
p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] a rg s ) { A s o c i j a t i v n i N i z < S t r i n g , S t r i n g > mapa = new A s o c i j a t i v n i N i z < S t r i n g , S t r i n g > ( 6 ) ; m ap a .p u t("n e b o", " p l a v o " ) ; m a p a . p u t ( " t r a v a " , " z e le n a ) ; mapa. p u t ( " o k e a n " , "nemi r a n " ) ; m a p a . p u t ( " s t a b lo " , " v i s o k o " ) ; m a p a . p u t ( " z e m lja " , "smea"); m ap a .p u t("s u n c e ", " t o p l o " ) ; try { m a p . p u t ( " d o d a t n i" , " o b j e k a t " ) ; / / Iza k r a j a } ca tc h (A rra y In d e x 0 u t0 fB o u n d s E x c e p tio n e) { p rin t("P re v i e o b je k a ta !");

}
p r i n t (m a p ) ; p rin t(m a p .g e t("o ke a n "));

}
} / * Is p is : P re v i e o b je k a ta ! nebo : p la vo t r a v a : zelena okean : nemiran s t a b lo : v is o k o z e m lja : smea sunce : t o p l o nemi ran

* ///:-

Poglavlje 17: Detaljno razmatranje kontejnera

663

Osnovne m etode asocijativnog niza su p u t ( ) i g e t ( ), ali je radi lakeg prikazivanja m etoda toS trin g( ) redefinisana tako da ispisuje parove klju-vrednost. Da bism o pokazali da to funkcionie, m a in ( ) uitava jedan A socijativniNiz parova znakovnih nizova i ispisuje tako dobijenu m apu, a iza toga g e t ( ) vadi iz m ape jednu od vrednosti. M etodu g e t( ) upotrebljavate tako to joj prosledite kljuc koji elite da pronae, a ona vadi i kao rezultat vraa njem u p rid ru en u vrednost ili n ull ako ga ne pronae. M etoda g e t ( ) upotrebljava verovatno najm anje efikasan nain lociranja vrednosti koji se moe zamisliti: krene od vrha niza i m etodom e q u a ls( ) poredi sve kljueve redom . Ali poenta je jednostavnost, a ne efikasnost. Dakle, gornja verzija je pouna, ali ne i veom a efikasna, a im a i neprom enljivu veliinu, to nije prilagodljivo. Sreom, M ape u paketu ja v a.u til nem aju te problem e, a m oete ih zam eniti u gornjem prim eru. Veba 12: (1) Zam enite po jedan kontejner tipa HashMap, TreeMap o d nosno LinkedHashMap u metodi m a in ( ) program a AsocijativniNiz.java. Veba 13: (4) U potrebite AsocijativniNiz.java za prebrojavanje rei, pri em u se String m apira (preslikava) u Integer. Uslunom m eto dom net.m indview.util.TextFile (iz ove knjige) otvorite tekstualnu datoteku i podelite je na rei, p ri em u su graninici razm ak i znakovi interpunkcije, i prebrojte rei u toj datoteci.

Performanse
Perform anse su temeljni problem pri radu s m apam a, jer je linearno pretraivanje metodom g e t( ) veoma spor nain traenja kljua. Ovde pom ae bri kontejner H ashM ap. Umesto sporog traenja kljua, on upotrebljava posebnu vrednost nazvanu k lju za heiranjc (engl. hash codc). Klju za heiranje predstavlja nain da se odreena inform acija u objektu pretvori u relativno jedinstven celi broj (in t) za taj objekat. M etoda h a s h C o d e () je definisana u korenskoj klasi O bject, pa svi Java objekti m ogu da proizvedu klju za heiranje. Kontejner H ashM ap uzim a h a s h C o d e () objekta i njom e brzo traga za kljuem. Tim e se dobija ogrom no poboljanje perform ansi.6 Naveemo osnovne realizacije interfejsa Map. Zvezdica kod H ashM ape pokazuje da bi je trebalo podrazum evano izabrati (ako nem a drugih ogranienja), zato to je optim izovana po brzini. Ostale realizacije im aju naglaenija druga obeleja, te su sporije od H ashM ape. Transform acija kljueva je najei nain skladitenja elem enata u mapi. Kasnije emo objasniti kako se obavlja ta transform acija.

A ko su u p rk o s o v im u b rz a n jim a p e rfo r m a n s e jo u v ek n e z a d o v o lja v a ju e , p re tra iv a n je ta b e la m o e te d o d a tn o u b rz a ti u k o lik o n a p i e te s o p s tv e n u M a p u i p rila g o ite je s v o jim tip o v im a , d a n e biste tro ili v re m e n a k o n v e rz iju u tip O b ject i iz njega. Z a jo b o lje p e rfo r m a n s e , z a lju b lje n ic i u b rz in u m o g u is k o ris titi k n jig u Tlte Art o f Computer Programming, Volume 3: Sortittg and Searching, Second Edition D o n a ld a K n u th a ; o n a e im p o m o i d a n iz o v im a z a m e n e liste s p re liv a ju im k o fa m a , to da je jo d v e p o g o d n o s ti: n jih m o e te o p tim iz o v a ti p re m a k a ra k te ris tik a m a s k la d ite n ja n a d is k u i o n i e v a m u te d e ti n ajvei d e o v re m e n a p o tr e b n o g za p ra v lje n je p o je d in a n ih z a p isa i n jih o v o p rik u p lja n je k a d a p o s ta n u sm ee.

66 4

Misliti na Javi

HashMap*

Realizacija zasnovana na tabeli transformisanih kljueva. (Upotrebite je umesto Hashtable.) Umetanje i pronalaenje parova obavlja u konstantnom vremenu. Performanse se mogu podesiti preko konstruktora koji omoguuju zadavanje kapaciteta i faktora optereenja tabele transformisanih kljueva. Poput HashMape, ali kada kroz nju iterirate, parove dobijate u redosledu umetanja ili u redosledu najdavnijeg korienja [engl. Ieast-recently-used, LRUJ. Samo malo sporija od HashMape, sem za iteriranje, kada je bra zbog ulanane liste koja odrava interni poredak. Realizacija napravljena na osnovu crveno-crnog stabla. Kada pogledate kljueve ili parove, oni e biti u ureenom poretku (koji odreuje Comparable ili Comparator). Sutina primene kontejnera TreeMap jeste da rezultate dobijate u ureenom poretku. TreeMap jejedina Mapa koja ima metodu subM ap( ), dakle jedina koja moe da vrati deo stabla. Mapa slabih kljueva koji omoguuju da objekti na koje mapa upuuje budu oslobodeni (sakupljeni u smee); koristi se za reavanje odreenih vrsta problema. Ako izvan mape ne postoje reference na odreeni klju, on moe biti pokupljen kao smee. Mapa koja bezbedno radi u vienitnom radu, a da nema zakljuavanje zbog sinhronizacije. Ovo je objanjeno u poglavlju Paralelno izvravanje. Mapa transformisanih kljueva koja za poreenje kljueva upotrebljava == umesto metode equals( J. Samo za reavanje posebnih vrsta problema; mje za optu upotrebu.

LinkedHashMap

TreeMap

WeakHashMap

ConcurrentHashMap ldentityHashMap

Za kljueve za Mapu postavljaju se isti zahtevi kao za elem ente skupa (Set). Upoznali ste ih u prim eru TipoviZaSkupove.java. Svaki klju m ora imati m etodu eq u a ls( ). Ako se klju upotrebljava u Mapi transform isanih kljueva, on m ora imati i propisnu m etodu h ash C od e( ). Klju koji se upotrebljava u TreeMapi m ora realizovati Comparable. U narednom prim eru pokazaem o op era je d ostupne preko interfejsa Map, uz pom o prethodno definisanog skupa ispitnih podataka BrojackaMapaPodataka:
/ / : k o n t e j n e r i/ M a p e . ja v a / / ta se moe u r a d i t i s mapama. im p o r t j a v a . u t i l . c o n c u r r e n t . * ; im p o r t j a v a . u t i 1 . * ; im p o r t n e t . m i n d v i e w . u t i l . * ; im p o r t s t a t i c n e t . m i n d v i e w . u t i l . P r i n t . * ; p u b l i c c la s s Mape { p u b l i c s t a t i c v o id p r i n t K e y s ( M a p < I n t e g e r , S t r in g > mapa) { p r i n t n b ( " V e l i i n a = " + m a p a .s iz e O + " , " ) ; p rin tn b ("K lju e v i: " ); p r i n t ( m a p a . k e y S e t ( ) ) ; / / Daje skup k lju e v a

}
p u b l i c s t a t i c v o id t e s t ( M a p < I n t e g e r , S t r i n g > mapa) { p r i n t ( m a p a . g e t C l a s s ( ) .g e tS im p le N a m e O ) ; m a p a .p u tA l1 (new Broja cka MapaPodataka(25)) ;

Poglavlje 17: Detaljno razmatranje kontejnera

665

/ / Ponaanje k l j u e v a u mapi je dnako j e onome u skupu ( ' S e t ' ) : m ap .putAU(new Broja ckaMapaPodataka(25)); p r in t K e y s ( m a p a ) ; / / P ra v lje n je k o le k c ije v re d n o s ti: p rin tn b ("V re d n o s ti: " ) ; p rin t(m a p a .v a lu e s ()); p rin t(m a p a ); p rin t("m a p a .c o n ta in s K e y (ll): " + m a p .c o n ta in s K e y (ll)); p rin t("m a p a .g e t(ll): " + m a p .g e t(ll)); p rin t("m a p a .c o n ta in s V a lu e (\"F O \"): " + m a p a . c o n t a in s V a lu e ( " F O " ) ) ; Integer k lju c = m a p a .k e y S e t( ).ite r a to r ( ).n e x t() ; p r i n t ( " P r v i k l j u u m a p i: " + k l j u c ) ; mapa.reinove(kl j u c ) ; p rin t K e y s (m a p a ) ; m a p a .c le a r(); p r i n t ( " m a p a . i s E m p t y ( ) : " + m a p a .is E m p ty ( )) ; mapa.putAl 1 (new BrojackaMapaPodataka(25)); / / O p e ra c ije nad t i m skupom men ja ju mapu: mapa. k e yS e t( ) . removeAl1 (mapa. ke y S e t( ) ) ; p r i n t ( " m a p a . i s E m p t y ( ) : " + mapa.isEmpty( ) ) ;

}
p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] a rg s) { te s t ( n e w H a s h M a p < I n t e g e r , S t r in g > ( ) ) ; test(new T re e M a p < In te g e r,S trin g > ()); te s t ( n e w L in k e d H a s h M a p < I n t e g e r , S t r in g > ( ) ) ; te s t ( n e w I d e n t i t y H a s h M a p < I n t e g e r , S t r i n g > ( ) ) ; t e s t ( n e w C o n c u rr e n t H a s h M a p < I n t e g e r, S t rin g > () ) ; t e s t ( n e w W e a kH a s h M a p < In te g e r,S tring > ());

}
} / * Is p is : HashMap V e l i i n a = 25, K l j u e v i : [1 5, 8, 23, 16, 7, 22, 9, 21, 6, 1, 14, 24, 4, 19, 11, 18, 3, 12, 17, 2, 13, 20, 10, 5, 0] V r e d n o s t i : [P0, 10, X0, Q0, H0, W0, J0 , V0, G0, B0, 00, Y0, EO, TO, LO, SO, DO, MO, RO, CO, NO, UO, KO, FO, AO] { 15=PO, 8=10, 23=X0, 16=Q0, 7=H0, 22=W0, 9=J0, 21=V0, 6=G0, 1=B0, 14=00, 24=Y0, 4=E0, 19=T0, 11=L0, 18=S0, 3=D0, 12=M0, 17=R0, 2=C0, 13=N0, 20=U0, 10=K0, 5=F0, 0=A0} m a p a .c o n ta in s K e y (ll): tru e mapa. g e t ( 1 1 ) : LO m a p a . c o n t a in s V a lu e (" F O " ) : t r u e P r v i k l j u u m ap i: 15 V e l i i n a = 24, K l j u e v i : [ 8 , 23, 16, 7, 22, 9, 21, 6, 1, 14, 24, 4, 11, 18, 3, 12, 17, 2, 13, 20, 10, 5, 0] mapa.i sEmpty( ) : t r u e m apa.isE mpty( ) : t r u e

19,

* ///:-

666

Misliti na Javi

M etoda printK eys( ) pokazuje kako se pravi kolekcija (Collection) od Mape. M etoda keyS et( ) proizvodi skup (Set) kljueva iz Mape. Rezultate m etode v a lu es( ) lako ete tam pati zbog poboljane podrke za tam panje u Javi SE5; ta m etoda pravi kolekciju od svih vrednosti u M api. (Vodite rauna o tom e da kljune rei m oraju biti jedinstvene, dok vrednosti m ogu im ati duplikate.) Poto M apa odrava te kolekcije, svaka izmena u kolekciji odraava se u njoj pridruenoj Mapi. O statak program a prua jednostavne prim ere svih operacija s M apam a i testira sve osnovne vrste M apa. Veba 14: (3) Pokaite da java.util.Properties radi u gornjem program u.

SortedMap
Ako im ate neku realizaju interfejsa SortedMap (od njih je dostupna sam o TreeMap), ldjuevi su zajam eno u ureenom poretku, zbog ega sledee m etode interfejsa SortedMap m ogu da prue d o d atn u funkcionalnost: Comparator com p arator( ): Proizvodi kom parator za upotrebu u ovoj Mapi ili null za p rirodni poredak. T firstK ey( ): Proizvodi najm anji klju. T lastICey(): Proizvodi najvei Jdju. SortedMap subMap(odKlju, doKlju): Proizvodi prikaz dela m ape s kljuevima od kljua odKlju, ukJjuivo, do kljua doKlju, iskljuivo. SortedMap headMap(toKey): Proizvodi prikaz dela m ape s kljuevima m anjim od kljua doKIju. SortedMap tailMap(fromKey): Proizvodi prikaz dela m ape s kljuevima jednakim ili veim od Jjua odKlju. Ovaj prim er lii na PrimerZaSortedSet.java i pokazuje to dodatno ponaanje TreeMape:
/ / : k o n te j n e r i/ P r im e r Z a S o r t e d M a p . ja v a / / ta se moe u r a d i t i s mapom TreeMap. im p o rt j a v a . u t i l . * ; im p o r t n e t . m i n d v i e w . u t i 1 . * ; im p o r t s t a t i c n e t . m i n d v i e w . u t i 1 . P r i n t . * ; p u b l i c c la s s PrimerZaSortedMap { p u b l i c s t a t i c v o id m ain ( S t r i n g [ ] arg s) { T re e M a p < I n t e g e r , S t r in g > sortedMap = new T re e M a p < In te g e r ,S tr in g > ( n e w Bro ja cka M a p a Po d a ta ka(lO )); p ri n t(sortedM ap); I n t e g e r n a jm a n ji = s o r t e d M a p . f i r s t K e y ( ) ; I n t e g e r n a jv e c i = s o rte d M a p .1a s t K e y ( ) ; p ri n t(n a jm a n ji); pri n t(n a jv e c i); I t e ra to r < In te g e r> i t = sortedM ap.keyS et(). i t e r a t o r ( ) ; f o r ( i n t i = 0; i <= 6; i+ + ) {

Poglavlje 17: Detajjno razmatranje kontejnera

667

i f ( i == 3) n a jm a n ji = i t . n e x t ( ) ; i f ( i == 6) n a jv e c i = i t . n e x t ( ) ; e ls e i t . n e x t ( ) ;

1
p rin t(n a jm a n ji); p rin t(n a jv e c i); p r i n t ( s o r t e d M a p . s u b M a p ( n a jm a n ji, n a j v e c i ) ) ; p rin t( s o r t e d M a p . h e a d M a p ( n a j v e c i) ) ; p r i n t ( s o r t e d M a p . t a i 1Map(najmanj i ) ) ;

}
} / * Is p is : {0=A0, 1=B0, 0 9 3 7 {3=D0, 4=E0, {0=A0, 1=B0, {3=D0, 4=E0, 2=C0, 3=D0, 4=E0, 5=F0, 6=G0, 7=H0, 8=10, 9=J0}

5=F0, 6=G0} 2=C0, 3=D0, 4=E0, 5=F0, 5=G0} 5=F0, 6=G0, 7=H0, 8=10, 9=J0}

* ,///:O vde su parovi uskladiteni po redosledu kljueva. Poto je m apa TreeMap ureena, koncept ,,mesta im a smisla, pa se zna ta je prvi i poslednji element i ta je podm apa sa elem entim a od - do.

LinkedHashMap
M apa LinkedHashMap transform ie sve kljueve radi brzine, ali tokom prolaska parove daje u redosledu um etanja (System .out.println( ) iterira kroz m apu, pa rezultate prolaska moete videti). Sem toga, realizacija LinkedHashMape moe se u konstruktoru konfigurisati tako da upotrebljava algoritam najdavnijeg korienja (engl. least-recendy-used, LRU) odnosno pristupanja, pa su na poetku liste elementi kojima nije pristupano (te su stoga kandiati za uklanjanje). Tim e je olakano pravljenje program a koji periodino iste smee cia bi se utedeo m em orijski prostor. Evo jednostavnog prim era u kojem su prikazane obe funkcionalnosti:
/ / : k o n t e j n e r i /PrimerZaLinkedHash Map.java / / ta se moe u r a d i t i s kontejn erom LinkedHashMap. im p o r t j a v a . u t i 1 . * ; im p o r t n e t . m i n d v i e w . u t i l . * ; im p o r t s t a t i c n e t . m i n d v i e w . u t i l . P r i n t . * ; p u b l i c c la s s PrimerZaLinkedHashMap { p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] a rg s) { L in k e d H a s hM a p <In te g e r,S trin g > linkedMap = new L in k e d H a s h M a p < In te g e r,S trin g > ( new B ro ja ckaM apaPodataka(9));

668

Misliti na Javi

pr in t(linkedMap); // Poredak najdavnijeg korienja: linkedMap = new LinkedHashMap<Integer,String>(16, 0.75f, true); linkedMap.putAll(new Broj ac ka Ma pa Po da ta ka( 9)); pr in t(linkedMap); for(int i = 0 ; i <6; i++) // Prouzrokuj pristupanje: linkedMap.get(i); print(linkedMap); 1 inkedMap.get(O); pr in t(linkedMap);

}
} /* Ispis: {0=A0, 1=B0, 2=C0, 3=D0, 4=E0, 5=F0, 6=G0, 7=H0, 8=10} {0=A0, 1=B0, 2=C0, 3=D0, 4=E0, 5=F0, 6=G0, 7=H0, 8=10} {6=G0, 7=H0, 8=10, 0=A0, 1=B0, 2=C0, 3=D0, 4=E0, 5=F0} {6=G0, 7=H0, 8=10, 1=B0, 2=C0, 3=D0, 4=E0, 5=F0, 0=A0}

* ///:Iz ispisa vidite da prolazak kroz m apu zaista daje parove u redosledu um etanja, ak i u LRU verziji. M eutim , nakon (samo) prvih est pristupanja u LRU verziji, poslednje tri stavke prelaze na poetak liste. Zatim, kada se ponovo pristupi stavci 0, ona prelazi na zaelje liste.

Transformisanje kljueva i kljuevi za heiranje


U prim erim a iz poglavlja uvanje objekato, kao H ashM ap kljuevi bile su upotrebljene unapred definisane klase. Ti prim eri su funkcionisali zato to su te unapred definisane klase imale potreban kod da m ogu ispravno da se ponaaju u ulozi kljueva. U obiajena greka je pravljenje sopstvenih klasa za kljueve u H ashM api, ali bez potrebnog koda. Na prim er, zamislite sistem za prognoziranje vrem ena koji objekte tipa M edved pridruuje objektim a tipa Prognoza. To izgleda prilino jednostavno - napravite te dve klase i objekat tipa M edved upotrebite kao klju, a objekat tipa P rognoza kao vrednost:
//: kontejneri/Medved.java // Izgleda uverljivo, ali ne radi kao klju HashMape. public class Medved { protected int broj; public Medved(int n) { broj = n; } public String toStringO { return "Medved #" + broj;

} } ///://: kontejneri/Prognoza.java

Poglavfje 17: Deta|jno razmatranje kontejnera

669

/ / P r o g n o z ira n je vremena s medvedima. im p o r t j a v a . u t i l p u b l i c c la s s Prognoza { p r i v a t e s t a t i c Random s lu c a ja n = new Random(47); p r i v a t e boolean senka = s l u c a ja n . n e x t D o u b le ( ) > 0 . 5 ; p u b lic S trin g to S tr in g ( ) { if ( s e n k a ) r e t u r n "Jo e s t sedmica z i m e ! " ; e ls e r e t u r n "Rano p r o l e e l " ;

} } ///:/ / : k o n te jn e ri/D e te k to rP ro le c a .ja v a / / Kakvo e b i t i vreme? im p o r t j a v a . l a n g . r e f l e c t . * ; im p o r t j a v a . u t i l . * ; im p o rt s t a t i c n e t . i n i n d v i e w . u t i l . P r i n t . * ; p u b l i c c la s s D e te k to r P r o le c a { / / K o r i s t i kla su Medved i l i neku njenu p o t k la s u : p u b l i c s t a t i c <T extends Medved> v o id o tk riv a n je P ro le c a (C 1 a s s < T > ty p e ) thro ws Exce p tio n { Co n s tru c to r<T > meda = t y p e . g e t C o n s t r u c t o r ( i n t . c l a s s ) ; Map<Medved,Prognoza> mapa = new HashMap<Medved,Prognoza>(); f o r ( i n t i = 0; i < 10; i+ + ) m a p a .p u t (m e d a .n e w ln s t a n c e (i) , new P r o g n o z a O ) ; p r i n t ( " m a p a = " + mapa); Medved md = m e d a .n e w ln sta n c e (3 ); p r i n t ( " T r a e n j e prognoze za " + m d); i f(m a p a .c o n ta i nsKey(md)) print(m ap a .g e t(m d )); el se p r i n t ( " N i j e pronaen k l j u : " + md);

}
p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] a rg s) throws Exce p tio n { o t k r i v a n je P r o le c a ( M e d v e d . c la s s ) ;

}
} / * Is p is : mapa= {Medved #3=Rano p r o l e e ! , Medved #7=Rano p r o l e e ! , Medved #5=Rano p r o l e e ! , Medved #9=Jo e s t sedmica z im e !, Medved #8=Jo e s t sedmica z im e !, Medved #0=Jo e s t sedmica z im e !, Medved #6=Rano p r o l e e ! , Medved #4=Jo e s t sedmica z im e !, Medved #l=Jo e st sedmica z im e ! , Medved #2=Rano p r o l e e ! } T ra e n je prognoze za Medved #3 N i j e pronaen k l j u : Medved #3

* ///:-

670

Misliti na Javi

Svaki Medved dobija identifikacioni broj da biste u H ashM api mogli da pronadete odreeni objekat tipa Prognoza tako to ete kazati: Daj m i objekat tipa Prognozapridruen objektu #3 tipa Medved. Klasa Prognoza sadri objekat tipa boolean inicijalizovan m etodom java.util.random ( ) i m etodu to S trin g( ) koja tum ai rezultate. M etoda otkrivanjeProIeca( ) napravljena je procesom refleksije da generie p rim erak klase Medved ili njene potklase i da ga upotrebljava. To e nam dobro doi kasnije, kada za reavanje ovde prikazanog problem a nasledim o nov tip klase Medved. HashMapa se popunjava objektim a tipa Medved i njim a prid ru en im objektim a tipa Prognoza. Ta HashMapa se ispisuje da biste videli da je popunjena. Zatim se Medved broj 3 koristi kao klju za pronalaenje prognoze za objekat #3 tipa Medved (za koji m oete videti da m ora biti u Mapi). Sve izgleda veom a jednostavno, aii ne radi - ne m oe da se pronae klju za #3. Problem je to to se Medved autom atski nasleuje od zajednike korenske klase Object, pa se za generisanje kljua za heiranje za svaki objekat upotrebljava m etoda h ash C o d e( ) klase Object. O na za heiranje podrazum evano upotrebljava sam o adresu svog objekta. Zato prvi prim erak Medved(3) ne proizvodi klju za heiranje jednak kljuu za heiranje drugog prim erka Medved(3), koji sm o pokuali da upotrebim o za pretraivanje. M oda mislite da je dovoljno napisati odgovarajuu redefiniciju m etode h ash C od e(). Ali ni to nee funkcionisati sve dok ne uradite jo neto: redefiniete m etodu eq u a ls() koja je takoe deo klase Object. HashMapa upotrebljava eq u a ls( ) prilikom utvrivanja da li je klju jednak nekom kljuu u tabeli. Prava m etoda eq u als( ) m ora zadovoljiti sledeih pet uslova: 1 . Refleksivna: Za svaki x, x.equals(x) treba da vraa true. 2. Simetrina: Za svaki x i y, x.equals(y) treba da vraa true ako i sam o ako y.equals(x) vraa true. 3. Tranzitivna: Za svaki x, y i z, ako x.equals(y) vraa true i y.equals(z) vraa true, onda x.equals(z) treba da vraa true. 4. Konsistentna: Za svaki x i y, svaki poziv x.equals(y) treba dosledno da vraa true ili dosledno da vraa false, ukoliko se inform acije upotrebljene pri utvrivanju jednakosti objekata ne prom ene. 5. Za svaki x razliit od null, x.equals(null) treba da vraa false. Ponavljam, podrazum evana m etoda O bject.equals( ) jednostavno poredi adrese objekata, pa jedan Medved(3) nije jednak drugom objektu Medved(3). Dakle, da biste u HashMapi mogli da upotrebljavate sopstvene klase kao kljueve, m orate redefinisati i hashC od e( ) i eq u a ls( ), kao to je uraeno u sledeem reenju problem a s medvedom:
/ / : k o n t e jn e r i/ M e d v e d 2 . ja v a / / Klasa u p o t r e b lje n a kao k l j u u HashMapi / / mora r e d e f i n i s a t i metode hashCode() i e q u a l s ( ) . p u b l i c c la s s Medved2 extends Medved ( p u b l i c M edved2(int n) { s u p e r ( n ) ; } p u b l i c i n t hashCode() { r e t u r n b r o j ;

Poglavjje 17: Detaljno razmatranje kontejnera

671

public boolean equals(0bject o) { return o instanceof Medved2 && (broj == ((Medved2)o).broj);

} } ///://: kontejneri/DetektorProleca2.java // Klju koji funkcionie. public class DetektorProleca2 { public static void main(String[] args) throws Exception { DetektorProleca.otkrivanjeProleca(Medved2.class);

}
} /* Ispis: mapa = {Medved #2=Rano prolee!, Medved #4=Jo est sedmica zime!, Medved #9=Jo est sedmica zime!, Medved #8=Jo est sedmica zime!, Medved #6=Rano prolee!, Medved #l=Jo est sedmica zime!, Medved #3=Rano prolee!, Medved #7=Rano prolee!, Medved #5=Rano prolee!, Medved #0=Jo est sedmica zime!} Traenje prognoze za Medved #3 Rano prolee!

* ///:M edved2.hashCode( ) vraa broj medveda kao vrednost transform isanog kljua. U ovom prim eru, program er treba da se postara da svaki medved ima jedinstven ID broj. O d m etode hashC od e( ) ne zahteva se da vraa jedinstvene identifikatore (to em o detaljnije objasniti u nastavku poglavlja), ali m etoda eq u a ls( ) m ora strogo da utvrdi da li su dva objekta ekvivalentna. Ovde m etoda eq u als( ) proverava broj medveda, pa ako u mapi HashMap postoje dva objekta tipa Medved2 sa istim brojem medveda, stvar nee funkcionisati. Iako izgleda kao da m etoda eq u a ls( ) proverava samo da li je njen argum ent instanca od Medved2 (pom ou rezervisane rei instanceof, objanjene u poglavlju Podaci o tipu), instanceof zapravo potiho proverava i da li je objekat jednak null, poto instanceof daje rezultat false ako je njen levi argum ent jednak null. Ako je tip odgovarajui i nije null, porede se vrednosti broj u oba objekta. Iz ispisa rezultata vidite da je ponaanje sada ispravno. Kada pravite klase za upotrebu u kontejneru tipa HashSet, m orate o bratiti panju na iste stvari kao kada ih upotrebljavate kao kljueve u kontejneru tipa HashMap.

Nain rada metode hashCodef )


Prethodni prim er je tek prvi korak u ispravnom reavanju problem a. Iz njega vidite sledee: ukoliko ne redefiniete m etode h ashC od e( ) i e q u a ls( ) za svoj klju, struktura transform isanih podataka (HashSet, HashMap, LinkedHashSet ili LinkedHashMap) verovatno nee ispravno raditi s tim kljuem. Da biste pronali dobro reenje problem a, treba da razum ete ta se deava u n u ta r strukture transform isanih podataka.

672

Misliti na Javi

Prvo, prisetite se zato transform iem o kljueve: hteli sm o m ogunost da jedan objekat pronadem o pom ou drugog. To se m oe postii i pom ou kontejnera TreeMap ili ak i realizacijom sopstvene Mape. N asuprot realizaciji s transform isanjem kljueva, naredni prim er realizuje Mapu p om ou p ara ArrayList kontejnera. Za razliku od prim era AsocijativniNiz.java, u narednom se interfejs Map p o tp u n o realizuje, to objanjava postojanje m etode en tryS et( ):
//: kontejneri/SporaMapa.java // Mapa realizovana ArrayListama. import java.util.*; import net.mindview.util.*; public class SporaMapa<K,V> extends AbstractMap<K,V> { private List<K> kljucevi = new ArrayList<K>(); private List<V> vrednosti = new ArrayList<V>(); public V put(K kljuc, V vrednost) { V staraVrednost = get(kljuc); // Stara vrednost ili null if(!kljucevi.contains(kljuc)) { kljucevi.add(kljuc); vrednosti.add(vrednost); } else
v r e d n o s t i. s e t ( k lju c e v i, in d e x O f ( k lju c ) , vred n o s t); r e t u r n s t a ra V re d n o s t ; p u b l i c V g e t ( O b je c t k l j u c ) { / / k l j u c j e t i p a O b je c t , ne t i p a K if(!k lju c e v i.c o n ta in s (k lju c )) r e t u r n n u l 1; r e t u r n v r e d n o s t i . g e t ( k l j u c e v i . in d e x O f ( k 1j u c ) ) ;

}
p u b l i c S e t< M a p .E n tr y < K ,V e n t r y S e t ( ) { S e t< M a p .E n try < K ,V skup= new H a s h S e t< M a p .E n tr y < K ,V ( ) ; I t e r a t o r < K > ki = k l j u c e v i . i t e r a t o r ( ) ; Iterator<V > vi = v r e d n o s t i . i t e r a t o r ( ) ; w h ile (k i,h a s N e x t()) skup.add(new M a p . E n t r y < K , V > ( k i , n e x t ( ) , v i . n e x t ( ) ) ) ; r e t u r n skup;

}
p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] args) { Sp o ra M a p a < S trin g ,S trin g > m= new S p o r a M a p a < S t r i n g , S tr in g > ( ) ; m . p u t A l1 ( C o u n t r i e s . g l a v n i _ g r a d o v i ( 1 5 ) ) ; S y s te m .o u t.p rin tln (m ); S y s te m .o u t. p r i n t l n ( m . g e t ( " B0TSWANA") ) ; S y s te m .o u t.p rin tln (m .e n try S e t( ) ) ;

}
} / * Is p is : {CAMEROON=Yaounde, CHAD=N' djamena, C0NG0=Brazzavi11 e, CAPE VERDE=Praia, ALGERIA=Algiers, C0M0R0S=Moroni, CENTRAL AFRICAN REPUBLIC=Bangui, B0TSWANA=Gaberone, BURUNDI=Bujumbura, BENIN=Porto-Novo,

Poglav[je 17: Detaljno ra-nlatranje kontejnera

673

EQUATORIAL GUINEA=Malabo, EGYPT=Cairo, ANGOLA=Luanda, BURKINA FAS0=0uagadougou, D J I B O U T I = D ijib o u ti} Gaberone [CAMEROON=Yaounde, CHAD=N'djamena, CONGO=Brazzaville, CAPE VERDE=Praia, ALGERIA=Algiers, C0M0R0S=Moroni, CENTRAL AFRICAN REPUBLIC=Bangui, BOTSWANA=Gaberone, BURUNDI=Bujumbura, BENIN=Porto-Novo, EQUATORIAL GUINEA=Malabo, EGYPT=Cairo, ANGOLA=Luanda, BURKINA FAS0=0uagadougou, D J I B O U T I = D ijib o u ti]

* ///:M etoda p u t() jednostavno sm eta kljueve i vrednosti u odgovarajue ArrayListe. U skladu sa interfejsom Map, m ora da vrati stari klju ili null ako stari klju ne pronae. Takoe u skladu sa specifikacijama za interfejs Map, m etoda g e t ( ) daje n ull ako se traeni klju ne pronae u m api SporaMapa. Ukoliko traeni klju postoji, upotrebljava se za pronalaenje num erikog indeksa koji pokazuje njegovo m esto u Listi kljucevi i taj broj se koristi kao indeks kojim se proizvodi pridruena vrednost iz Liste vrednosti. O bratite panju na to da je kljuc u m etodi g e t ( ) tipa Object, a ne param etrizovanog tipa K kao to biste mogli oekivati (i koji je naravno bio upotrebljen u p rim eru AsocijativniNiz.java). Ovo je posledica tako kasnog ubacivanja generikih tipova u J a v u - da su generiki tipovi postojali u prvobitnom izdanju jezika, m etoda g e t( ) mogla bi da specificira tip svog param etra. M etoda M ap.entrySet( ) m ora da proizvede skup objekata lipa Map.Entry. Meutim, interfejs Map.Entry opisuje stru k tu ru koja se m enja u zavisnosti od realizacije, pa ako elite da pravite sopstveni tip Mape, m orate da definiete i realizaciju od Map.Entry:
/ / : k o n t e j n e r i/ S t a v k a M a p e . ja v a / / Jednostavan i n t e r f e j s M ap.E ntry za p rim e re r e a l i z o v a n j a i n t e r f e j s a Map. im p o r t j a v a . u t i 1 p u b l i c c la s s StavkaMape<K,V> implements Map.Entry<K,V> { p riv a te K k lju c ; p r i v a t e V vrednost; p u b l i c StavkaMape(K k l j u c , V v r e d n o s t) { th is .k lju c = k lju c ; t h i s . v re d n o s t = v r e d n o s t ;

}
p u b l i c K g e tK e y () { r e t u r n k l j u c ; } p u b l i c V g e t V a lu e ( ) { r e t u r n v r e d n o s t ; } p u b l i c V se tV a lu e (V v) { V r e z u lt a t = vrednost; v r e d n o s t = v; re tu rn r e z u lta t;

}
p u b l i c i n t hashCode() { r e t u r n ( k l j u c = = n u l l ? 0 : k lju c . h a s h C o d e O ) ~ ( v r e d n o s t = = n u ll ? 0 : v r e d n o s t . h a s h C o d e O ) ;

674

Misliti na Javi

}
p u b l i c boolean e q u a l s ( 0 b j e c t o) { i f ( ! ( o in s t a n c e o f StavkaMape)) r e t u r n f a l s e ; StavkaMape sm = (StavkaMape)o; return ( k l j u c == n u l l ? sm.getKey() == n u l l : k l j u c . e q u a l s ( s m . g e t K e y ( ) ) ) && (v re d n o st == n u l l ? sm .getV alue()= = n u l l : v r e d n o s t . e q u a l s ( s m . g e t V a l u e ( ) ) ) ;

}
p u b lic S trin g to S tr in g O { r e tu r n k l ju c + " =" + vrednost; }

} ///:Ovde veoma jednostavna klasa StavkaMape uva i vraa kljueve i vrednosti. To je u m etodi entrySet( ) iskorieno za pravljenje skupa parova klju - vrednost. O bratite panju na to da entrySet( ) upotrebljava HashSet za uvanje parova, pa StavkaMape jednostavno koristi m etodu h ash C od e( ) objekta kljuc. Iako je ovo reenje veom a jednostavno i naizgled funkcionie u trivijalnom testu u m etodi SporaM apa.m ain( ), to nije korektna realizacija zato to se pravi kopija skupova kljucevi i vrednosti. Korektna realizacija skupa en trySet( ) treba da prui u v id u Mapu, a ne da pravi njenu kopiju, i taj uvid e om oguiti modifikaciju prvobitne m ape (to kopija ne om oguuje). Priliku da reite ovaj problem nai ete u vebi 16. Vodite rauna o tom e da m etoda e q u a ls () klase StavkaM ape m ora da proverava i kljueve i vrednosti. Znaenje m etode h a s h C o d e () bie opisano uskoro. Predstavu sadraja klase SporaM apa u obliku znakovnog niza (String) autom atski pravi m etoda to S trin g () definisana u klasi A bstractM ap. U m etodi S p o ra M ap a .m a in () uitava se S poraM apa i zatim se prikazuje njen sadraj. Poziv m etode g e t( ) pokazuje da to funkcionie. Veba 15: (1) Ponovite vebu 13 uz upotrebu m ape SporaM apa. Veba 16: (7) Prim enite testove iz program a M aps.java na klasu S poraM apa da biste proveriii kako funkcionie. Popravite sve to u klasi SporaM apa ne funkcionie korektno. Veba 17: (2) Realizujte ostatak interfejsa M ap za klasu SporaM apa. Veba 18: (3) Napravite SporSkup po uzoru na m apu SporaM apa.java.

Transformisanje kljueva zbog brzine


S poraM apa.java pokazuje da nije teko napraviti nov tip Mape. Ali kao to joj ime nago vetava, SporaM apa nije ba brza, pa je verovatno ne bi koristio niko ko im a neku drugu m ogunost. Problem je pronalaenje kljua; kljuevi se ne dre u odreenom poretku, pa se za njim a traga jednostavnom linearnom pretragom . Linearno pretraivanje je najsporiji nain pronalaenja. Razlog za transform isanje kljueva je brzina: zbog toga je pretraivanje brzo. Poto je brzina pronalaenja kljueva usko grlo, jedno od reenja problem a jeste da se kljuevi dre u ureenom poretku i da se zatim pretraivanje obavlja m etodom C ollectio n s.b in ary S earch () (u jednoj vebi proi ete kroz taj postupak).

Poglavlje 17: Detaljno razmatranje kontejnera

675

Transform isanje ide jo korak dalje jer im plicira da sam o elite da klju smestite negde gde se m oe brzo pronai. Najbra struk tura za skladitenje grupe elemenata je niz, pa e on biti upotrebljen za predstavljanje inform acija o kljuu (rekao sam inform acija o kljuu, a ne sam og kljua). Budui da je veliina niza neprom enljiva, im am o problem : elimo da sm estim o neodreen broj vrednosti u M apu, ali ako je broj kljueva odreen veliinom niza, kako em o to postii? O dgovor je da taj niz nee sadrati kljueve. Iz objekta kljua bie izveden broj - indeks u tom nizu. Taj broj je k lju za heiranje (engl. hash code) koji daje m etoda h a s h C o d e () (u reniku raunarske nauke nju nazivam o he fu n k c ija ilifu n k c ija za transform isanje kljueva), defm isana u klasi O bject i verovatno redefinisana u vaoj klasi. Da bi se reio problem niza neprom enljive veliine, vie kljueva m oe dati isti indeks. Dakle, m ogu nastati sudari (engl. collisions). Stoga nije vano koliki je niz; u njem u e biti mesta za kljueve za heiranje svih objekata kljueva. Zato postupak pronalaenja vrednosti poinje izraunavanjem kljua za heiranje koji se upotrebljava kao indeks niza. Kada biste m ogli jem iti da nee biti sudaranja (to je m ogue ako im ate neprom enljiv broj vrednosti), onda biste imali savrenu fu n k c iju za transform isanje kljueva, ali to je poseban sluaj.7 U svim drugim sluajevima, sudaranjem se bavi spoljno nadovezivanje (engl. externalchaining): niz ne pokazuje neposredno na vrednost, nego na listu vrednosti. Za tim vrednostim a traga se m etodom e q u a ls() na linearan nain. Naravno, taj deo pretraivanja je m nogo sporiji, ali ako je funkcija za transformisanje kljua dobra, u svakom odeljku liste bie tek nekoliko vrednosti. Stoga se umesto pretraivanja cele liste, brzo skae na odeljak u kojem za pronalaenje vrednosti treba uporediti tek nekoliko stavki. To je m nogo bre i zato je kontejner H ashM ap tako brz. Poto sada znate osnove transform isanja kljueva, m oem o da realizujemo jednostavnu M apu s transform isanim kljuevima:
/ / : k o n t e jn e ri/ J e d n os ta vn a H a sh M a p a .ja v a / / Prim e r mape s t r a n s f o r m is a n im k lju e v im a . im p o r t j a v a . u t i 1 im p o r t n e t . m i n d v i e w . u t i l . * ; p u b l i c c la s s JednostavnaHashMapa<K,V> extends AbstractMap<K,V> { / / I z a b e r i t e p rim b r o j za v e l i i n u he t a b e l e , / / da bi se p o s t i g l a ravnomerna ra s p o d e la : s t a t i c f i n a l i n t VELICINA = 997; / / Ne moete im a t i f i z i k i n iz g e n e r i k i h t i p o v a , / / a l i moete s v e s t i n a v i e na nje g a : @SuppressWarni ng s("u n ch ecke d ") L in k e d L is t< S ta v k a M a p e < K ,V [ ] k ofe = new L in k e d L i s t[ V E L I C I N A ] ; p u b l i c V p u t(K k l j u c , V v r e d n o s t) { V s t a ra V re d n o s t = n u l l ; i n t indeks = M a t h . a b s ( k l ju c .h a s h C o d e O ) % VELICINA;

l ' Javi SE5, savrena funkcija za tran sfo rm isan je kljueva (he funkcija) realizovana je u kontejn erim a E num M ap i EnumSet, zato to enum definie neprom enljiv broj instanci. Videti poglavlje

Nabrojoni tipovi.

676

Misliti na Javi

i f ( k o f e [ i n d e k s ] == n u l l ) k o f e [ in d e k s ] = new L in k e d L i s t< S ta v k a M a p e < K ,V ( ) ; Lin ke d Lis t< S ta v ka M a p e< K ,V kofa = k o f e [ i n d e k s ] ; StavkaMape<K,V> par = new StavkaM ape<K,V >(klju c, v r e d n o s t ) ; boolean pronadjen = f a l s e ; L is t I t e r a t o r < S t a v k a M a p e < K , V i t = k o f a . l i s t I t e r a t o r ( ) ; w h ile (it.h a s N e x t()) { StavkaMape<K,V> iP a r = i t . n e x t ( ) ; if(iP a r.g e tK e y () .e q u a ls (k lju c )) { sta ra V re d n o st = i P a r . g e t V a l u e ( ) ; i t . s e t ( p a r ) ; / / Zameni s t a r o novim pro nadje n = t r u e ; bre ak;

) }
if(Ip ro n a d je n ) k o fe [in d e k s ].a d d (p a r); r e t u r n s t a ra V re d n o s t;

}
p u b l i c V g e t ( O b je c t k l j u c ) { i n t indeks = M a t h . a b s (k lju c .h a s h C o d e O ) % VELICINA; i f ( k o f e [ i n d e k s ] == n u l l ) r e t u r n n u l l ; for(StavkaMape<K,V> iP a r : k o f e [ i n d e k s ] ) i f(iP a r.g e tK e y (). e q u a ls (k lju c )) re tu rn iP a r .g e tV a lu e () ; retu rn n u ll;

}
p u b l i c S e t< M ap .E ntry< K ,V e n t r y S e t ( ) { S e t< M ap .E ntry < K ,V skup= new HashSet<Map. E n t r y < K , V ( ) ; fo r(L in k e d L is t< S ta v k a M a p e < K ,V ko fa : k o fe ) { i f ( k o f a == n u l l ) c o n t in u e ; for(StavkaMape<K,V> mpar : kofa) s k u p .ad d (m p a r);

}
r e t u r n skup;

}
p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] args) { Jednosta vnaHashMapa<Strin g,String> m = new J e d n o s ta v n a H a s h M a p a < S trin g ,S trin g > (); m.putAl 1 ( C o u n t r i e s . g l a v n i j r a d o v i ( 2 5 ) ) ; S y s te m .o u t.p rin tln (m ); S y s t e m . o u t . p r in t ln ( m . g e t ( " E R I T R E A " ) ) ; S ys te m .o u t.p rin tln (m .e n try S e t());

}
} / * Is p is : {CAMEROON=Yaounde, C0NG0=Brazzavi11e, CHAD=N1djamena, COTE D'IVOIR (IVORV COAST)=Yamoussoukro, CENTRAL AFRICAN REPUBLIC=Bangui, GUINEA=Conakry, BOTSWANA=Gaberone, BISSAU=Bissau, EGYPT=Cairo, ANGOLA=Luanda, BURKINA FAS0=0uagadougou, ERITREA=Asmara, THE GAMBIA=Banjul, KENYA=Nairobi, GAB0N=Librevi11e, CAPE VERDE=Praia,

Poglav[je 17: Detaljno razmatranje kontejnera

677

ALGERIA=Algiers, C0M0R0S=Moroni, EQUATORIAL GUINEA=Malabo, BURUNDI=Bujumbura, BENIN=Porto-Novo, LESOTHO=Maseru, mdANA=Accra, DJIBOUTI=Diji bouti, ETHIOPIA=Addis Ababa} Asmara [CAMEROON=Yaounde, C0NG0=Brazzavi 11e, CHAD=N'djamena, COTE D'IVOIR (IV0RY COAST)=Yamoussoukro, CENTRAL AFRICAN REPUBLIC=Bangui, GUINEA=Conakry, BOTSWANA=Gaberone, BISSAU=Bissau, EGYPT=Cairo, ANGOLA=Luanda, BURKINA FAS0=0uagadougou, ERITREA=Asmara, THE GAMBIA=Banjul, KENYA=Nairobi, GABON=Librevi 11e, CAPE VERDE=Praia, ALGERIA=Algiers, C0M0R0S=Moroni, EQUATORIAL GUINEA=Malabo, BURUNDI=Bujumbura, BENIN=Porto-Novo, LES0TH0=Maseru, mdANA=Accra, DJIBOUTI=Dijibouti, ETHIOPIA=Addis Ababa]

* ///:Poto ,,odeljke he tabele esto nazivaju kofe, niz koji u p ro g ram u predstavlja tabelu nazvan je kofe. Da bi se postigla ravnom erna raspodela, broj kofa je obino p rim broj.8 O bratite panju na to da se radi o ulananoj listi L inkedList koja autom atski razeava sudare: svaka nova stavka se jednostavno dodaje na kraj liste u odreenu kofu. Iako Java ne dozvoljava pravljenje niza generikih objekata, m ogue je napraviti refereticu na takav niz. Ovde je podesno svesti navie na takav niz, da bi se spreilo d o d atn o svoenje u nastavku program a. Kada - m etodom p u t( ) - treba obaviti um etanje u m apu, h a sh C o d e( ) se poziva za klju, a rezultat se pretvara u pozitivan broj. Za uklapanje rezultujueg broja u niz kofe koristi se operator m odulo (%) i veliina niza. Ako se za m esto na koje rezultat treba ubaciti dobije null, to znai da nem a elem enata koji su transform isanjem dospeli na to mesto, pa se pravi nova ulanana lista (LinkedList) za uvanje objekta koji je transform isanjem upravo dospeo na njega. M eutim , norm alan postupak se sastoji od toga da se u listi potrae duplikati, pa ako postoje, stara vrednost se smeta u prom enljivu staraVrednost, a nova vrednost zam enjuje staru. Indikator pronadjen pam ti da li je p ronaden stari par klju - vrednost, i ako nije, novi par se dodaje na kraj liste. M etoda g e t ( ) za vadenje vrednosti pom ou kljua, izraunava indeks u nizu kofe na isti nain kao put( ) (tim e se jem i da ete dospeti u isti odeljak liste). Ukoliko postoji neka ulanana lista, u njoj se trai vrednost koja odgovara datom kljuu. Imajte u vidu podatak da ova realizacija nije optim izovana za perform anse, nego sam o pokazuje operacije koje obavlja m apa transform isanih kljueva. Pogledajte izvorni kod za java.util.HashMap ako vas zanim a optim izovana realizacija. Takoe, jednostavnosti radi, JednostavnaHashMapa metodi en tryS et( ) pristupa jednako kao SporaMapa, to je previe pojednostavljeno i ne bi funkcionisalo u Mapi opte nam ene. Veba 19: ( 1) Ponovite vebu 13 uz upotrebu m ape JednostavnaHashMapa. Veba 20: (3) Prepravite program JednostavnaHashMapa tako da prijavljuje sudare i testirajte ga dodavanjem istog skupa podataka dvaput, im e ete izazvati sudare.
s Ispostavlja se d a p rim broj zapravo nije idealan kao veliina kofa za heiranje, i novije realizacije heiranja u Javi upotrebljavaju veliinu jednaku nekom step en u b roja dva (a to je rezultat opsenog ispitivanja). Deljenje i o sta ta k o d deljenja su najsporije o p eracijek o jeo b av ljaju savrem eni procesori. Ako je d u in a ta b e le je d n a k a n e k o m s te p e n u b ro ja d v a , u m e s to d e lje n ja m o e se u p o tr e b iti m a s k ira n je . P o to je get( ) n ajea o p e ra c ija , d e lje n je (% ) p ro u z ro k u je \ eliki d e o tro k o v a k o je e lim in i e p ris tu p
ste p e n b ro ja d v a" (ali m o e d a u ti e i na n eke h a s h C o d e ( ) m e to d e ).

678

Misliti na Javi

Veba 21: (2) Prepravite program JednostavnaHashMapa tako da prijavljuje broj ,,proba p otrebnih kada doe do sudara. D rugim reima, koliko p u ta se m o ra pozvati n e x t() za Iteratore koji prolaze ulananim listam a u potrazi za p o d u d arn im elementima? Veba 22: (4) Realizujte m etode clea r( ) i rem ove( ) za kontejner JednostavnaHashMapa. Veba 23: (3) Realizujte ostatak interfejsa Map za kontejner JednostavnaHashMapa. Veba 24: (5) Po uzoru na prim er u program u JednostavnaHashMapa.java, napravite i testirajte JednostavanHashSkup. Veba 25: (6) U m esto da za svaku kofu upotrebljavate po jedan Listlterator, prepravite klasu StavkaMape tako da bude sam ostalna jednostruko ulanana lista (svaka StavkaMape treba da im a vezu unapred ka sledeoj klasi StavkaMape). Prepravite ostatak koda u program u JednostavnaHashMapa.java tako da novi pristup ispravno funkcionie.

Redefinisanje metode hashCodef)


Poto sada razum ete kako radi transform isanje kljueva, im a smisla da i sami napiete m etodu h ash C od e(). Pre svega, vi ne pravite stvarnu vrednost koja se upotrebljava za indeksiranje niza kofa. O na se m enja u zavisnosti od kapaciteta odreenog H ashM ap objekta, a taj kapacitet se m enja u zavisnosti od napunjenosti kontejnera i fakto ra optereenja (objasniem o taj term in kasnije). Stoga e vrednost koju proizvede vaa m etoda h a s h C o d e () biti jo m enjana da bi se napravio indeks niza kofa (u program u JednostavnaH ashM apa, proraun se svodi na operaciju m odulo s veliinom niza kofa). Pri pravljenju m etode h a s h C o d e () najvanije je da ona daje istu vrednost za odreeni objekat svaki p u t kada bude pozvana, bez obzira na to kada je pozvana. Ukoliko dobijete objekat koji pravi jednu h a s h C o d e () vrednost kada se m etodom p u t ( ) umee u H ashM apu, a drugu kada se m etodom g e t( ) vadi iz nje, neete moi da pronalazite i vadite objekte iz m ape. Stoga ako se rezultat vae m etode h a s h C o d e () bude m enjao u zavisnosti od prom enljivih podataka u objektu, korisnik m ora biti svestan da e izmena tih podataka prouzrokovati pravljenje razliitogkljua, jer se generie drugaiji h a s h C o d e (). Sem toga, hashC ode( ) verovatno neefegenerisati na osnovu jedinstvenih inform acija objekta konkretno, vrednost koju daje rezervisana re th is predstavlja lo h a s h C o d e (), zato to u tom sluaju ne m oete generisati klju identian onom e koji je upotrebljen za um etanje (m etodom p u t ( )) prvobitnog para klju - vrednost. To je bio problem u program u D etektorProleca.java, zato to podrazum evana realizacija m etode h a s h C o d e () kao ulazni podatak objekta koristi njegovu adresu. Zbog toga treba upotrebiti one informacije iz objekta koje ga identifikuju smisleno. Jedan prim er vidi se u klasi String. Znakovni nizovi (Stringovi) imaju posebno obeleje: ukoliko program im a vie S trin g objekata koji sadre identine sekvence znakova, onda se svi ti S trin g objekti preslikavaju (m apiraju) na isto m esto u m em oriji. Stoga ima smisla da funkcije h a s h C o d e () proizvedene za dve zasebne instance znakovnog niza Z dravo b u d u identine. To vidite u ovom program u:

Poglavlje 17: Detaljno razmatranje kontejnera

679

//:

k o n t e j n e r i/ K 1 ju e v iZ a T ra n s f o r m is a n je Z n a k o v n ih N iz o v a . ja v a

p u b l i c c la s s K 1 ju e viZ a T ra ns fo rm isan je Z n ak o vn ih N iz o v a { p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] a rg s) { S t r i n g [ ] dvazdravo = "Zdravo Z d r a v o " . s p l i t ( " " ) ; S y s t e m . o u t . p r in t ln ( d v a z d r a v o .h a s h C o d e O ) ; S y s t e m . o u t . p r i n t l n ( d v a z d r a v o .h a s h C o d e O ) ;

}
} /* Is p is : ( p r im e r)

69609650 69609650 * ///:Oigleno je da se hashC od e( ) za tip String izraunava na osnovu sadraja datog znakovnog niza (objekta tipa String). Dakle, da bi m etoda hashC od e( ) bila delotvorna, m ora biti brza i sm isaona, tj. m ora generisati vrednost na osnovu sadraja objekta. Ne zaboravite da ta vrednost ne m ora biti jedinstvena bavite se brzinom , a ne jedinstvenou - ali kom binacija m etoda h ash C od e( ) i eq u a ls( ) m ora u potpunosti odrediti identitet objekta. Poto se hashC od e( ) dodatno obraduje pre pretvaranja u indeks kofa, opseg vrednosti rezultata te m etode nije vaan; dovoljno je da ona generie ceo broj (int). Ima tu jo neto: dobra m etoda hashC o e( ) treba da daje ravnom erno raspodeljene vrenosti. Ako se te vrednosti negde gomilaju, onda e kontejner HashMap ili HashSet na nekim mestim a biti gue popunjen i nee biti onoliko brz kao to m oe biti uz ravnom erno raspodeljene rezultate funkcije za heiranje. U knjizi Efikasno program iranje na Javi (M ikro knjiga, 2004), Joshua Bloch daje osnovni recept za generisanje pristojne m etode hashC od e( ): 1 . U celobrojnu prom enljivu (tipa int) nazvanu rezultat smestite neki broj razliit od nule, recimo 17. 2. Za svako znaajno polje f u objektu (tj. svako polje koje m etoda eq u a ls() uzim a u obzir) izraunajte celobrojni (int) hashC od e( ) c:
Tip polja booleari byte, char, short ili int long float double Object, gde metoda equals( ) poziva equals( ) za to polje Niz Algoritam za izraunavanje c = (f ? 0 : 1) c = (int)f c = (int)(f A (f > 3 2 )) c = Float.floatTolntBits(f); long 1 = Double.doubleToLongBits(f); c = (int)(l A (1 > 32)) c = f.hashCode( ) Na svaki element primenite gornja pravila

680

Misliti na Javi

3. K som binujte goreizraunate kljueve za transform isanje: rezultat = 37 * rezultat + c; 4. Vratite rezultat. 5. Pogledajte dobijenu m etod u h a sh C od e() i postarajte se da jednake instance imaju jednake kljueve za transform isanje. Naredni prim er je napisan po p retho dn im sm ernicam a:
/ / : ko n tejne ri/P re b ro ja n Z n a k o v n iN iz .ja v a / / P r a v l j e n j e dobre metode hashCode(). im p o r t j a v a . u t i l . * ; im p o r t s t a t i c n e t . m i n d v i e w . u t i l . P r i n t . * ; p u b l i c c la s s Pre b ro ja n Z n a ko v n iN iz { p r i v a t e s t a t i c L i s t < S t r i n g > n a p r a v lje n o = new A r r a y L i s t < S t r i n g > ( ) ; p r i v a t e S t r i n g s; p r i v a t e i n t i d = 0; p u b l i c P r e b r o ja n Z n a k o v n iN iz ( S t r in g znn) { s = znn; n a p ra v lje n o .a d d (s ); / / i d j e ukupan b r o j i n s t a n c i datog znakovnog / / n iz a k o je k o r i s t i P re b ro ja n Z n a k o v n iN iz : f o r ( S t r i n g s2 : n a p r a v lje n o ) if( s 2 .e q u a ls ( s )) i d++;

}
p u b lic S trin g to S trin g O { r e t u r n "Znakovni n i z : " + s + i d : 1 1 hashCode(): " + hashCode(); " + id +

}
p u b l i c i n t hashCode() { / / Veoma je d n o sta va n p r i s t u p : / / r e t u r n s.hashCode() * i d ; / / Upotrebiemo r e c e p t Joshue Blocha: i n t r e z u l t a t = 17; r e z u l t a t = 37 * r e z u l t a t + s .h a s hC od e (); r e z u l t a t = 37 * r e z u l t a t + i d ; retu rn r e z u lt a t ;

}
p u b l i c boolean e q u a l s ( 0 b j e c t o) { r e t u r n o in s t a n c e o f P re b ro ja n Z n a k o v n iN iz && s . e q u a l s ( ( ( P r e b r o j a n Z n a k o v n i N i z ) o ) . s ) && i d == ( ( P r e b r o ja n Z n a k o v n iN iz ) o ) . i d ;

}
p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] a rg s) { M a p < P r e b ro ja n Z n a k o v n iN iz ,In te g e r> mapa = new H a s h M a p < P r e b ro ja n Z n a k o v n iN iz ,In te g e r > () ; P r e b r o ja n Z n a k o v n iN iz [ ] pzn = new P re b ro ja n Z n a k o v n iN iz ; f o r ( i n t i = 0; i < p z n . d u z in a ; i+ + ) {

Poglavlje 17: Detaljno razmatranje kontejnera

681

p z n [ i ] = new P r e b r o ja n Z n a k o v n iN iz ( " z d r a v o " ) ; m a p a . p u t ( p z n [ i ] , i ) ; / / Automatsko pakovanje i n t -> I n t e g e r

}
p rint(m ap a ); f o r ( P r e b r o ja n Z n a k o v n iN iz p z n a k o v n in iz : pzn) { p r in t( " T r a im " + p z n a k o v n in iz ); p rin t(m a p a .g e t(p z n a k o v n in iz ));

} }
} / * I s p i s : ( p r im e r ) { S t r i n g : zd ra vo i d : 4 hashCode(): 146450=3, S t r i n g : zdravo i d : 1 hashCode(): 146447=0, S t r i n g : zd ra vo i d : 3 hashCode(): 146449=2, S t r i n g : zd ra vo i d : 5 hashCode(): 146451=4, S t r i n g : zd ra vo i d : 2 hashCodeO: 146448=1} Tra im Znakovni n i z : zd ra vo i d : 1 hashCode(): 146447

0
Tra im Znakovni n i z : zd ra vo i d : 2 hashCode(): 146448
1

T ra im Znakovni n i z : zd ra vo i d : 3 hashCode(): 146449

2
Traim Znakovni n i z : zd ra vo i d : 4 hashCode(): 146450 3 Tra im Znakovni n i z : zdra vo i d : 5 hashCode(): 146451 4

* ///:PrebrojanZnakovniNiz sadri jedan znakovni niz (String) i jedan id koji predstavlja broj objekata tipa PrebrojanZnakovniNiz koji sadre identian znakovni niz. Prebrojavanje se obavlja u konstruktoru, iteriranjem kroz statinu listu ArrayList u kojoj su uskladiteni svi znakovni nizovi. O be m etode, i h ash C od e( ) i eq u a ls( ), proizvode rezultate na osnovu oba polja; da uzim aju u obzir sam o objekat tipa String ili sam o id, deavalo bi se da razliite vrednosti dobiju duplikate istog kljua. U m etodi m a in ( ), pom ou istog znakovnog niza napravljeno je vie objekata tipa PrebrojanZnakovniNiz, kako bi se pokazalo da duplikati prave jedinstvene vrednosti zbog uzim anja u obzir id(entifikatora) broja duplikata. Prikazana je i HashMapa da biste videli kakav je njen unutranji poredak (ne moe se uoiti nikakva pravilnost), a zatim se svaki klju trai pojedinano, kako bi se pokazalo da m ehanizam pronalaenja radi kako treba. Kao drugi prim er, razm otriem o klasu Jedinka koja je bila upotrebljena kao osnovna klasa biblioteke podaciotipu.ljubim ci definisane u poglavlju Podaci o tipu. Klasa Jedinka je bila upotrebljena u tom poglavlju, ali je njena definicija odloena do ovoga da biste shvatili realizaciju:
/ / : p o d a c io tip u /1 ju b im c i/J e d in k a .ja v a package p o d a c i o t i p u . l j u b i m c i ; p u b l i c c la s s Je d in ka implements Comparable<Jedinka> { p r i v a t e s t a t i c long b r o j a c = 0; p r i v a t e f i n a l long i d = b ro ja c + + ;

682

Misliti na Javi

p r i v a t e S t r i n g ime; p u b l i c J e d in k a ( S t r in g ime) { t h i s . i m e = ime; } / / 'im e ' i s opciono: p u b l i c J e d in k a () {} p u b lic S trin g to S trin g O { r e t u r n g e tC la ss ().g e tS im p le N a m e () + (ime == n u l l ? " " : " + im e );

}
p u b l i c long i d ( ) { r e t u r n i d ; } p u b l i c boolean e q u a ls ( O b je c t o) { r e t u r n o i n s t a n c e o f J e d in k a && i d == ( ( J e d i n k a ) o ) . i d ;

}
p u b l i c i n t hashCode() { i n t r e z u l t a t = 17; i f ( i m e != n u l l ) r e z u l t a t = 37 * r e z u l t a t + im e .h a sh C o d e (); r e z u l t a t = 37 * r e z u l t a t + ( i n t ) i d ; return re z u lta t;

}
p u b l i c i n t compareTo(Jedinka a rg ) { / / Prvo se porede imena k ia s a : S t r i n g prv o = g e t C la s s ( ) . g e tS im p l e N a m e ( ) ; S t r i n g a rg P rv i = a r g . g e t C l a s s ( ) .g e tS im pleN am e (); i n t p rv o P o re d je n je = p rv o . c o m p a r e T o ( a rg P rv i) ; i f ( p r v o P o r e d j e n j e != 0) r e t u r n p rv o P o re d je n je ; i f ( i m e != n u l l && a rg .im e != n u l l ) { i n t d ru g o P o re d je n je = im e .c o m p a re T o (a r g .im e ) ; i f ( d r u g o P o r e d j e n j e != 0) r e t u r n d ru g o P o re d je n je ;

}
r e t u r n ( a r g . i d < i d ? -1 : ( a r g . i d == i d ? 0 : 1 ) ) ;

}
} lll--~

M etoda co m p areT o () im a hijerarhiju poreenja, tako da proizvodi sekvencu ureenu prvo po stvarnom tipu, zatim po im enu ako ono postoji, i najzad po redosledu pravljenja. U ovom prim eru videete kako sve to radi:
/ / : k o n t e j n e r i / T e s t i r a n j e J e d i n k i . ja v a im p o rt h o ld in g .M a p O f L i s t; im p ort p o d a c i o t i p u . l j u b i m c i im p o rt j a v a . u t i l p u b l i c c la s s T e s t i r a n j e J e d i n k i { p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] a rg s ) { Set<Jedinka> I j u b i m c i = new T r e e S e t < J e d i n k a > ( ) ; f o r ( L i s t < ? extends LJubimac> 11 j :

Poglavlje 17: Deta[jno razmatranje kontejnera

683

M a p O f L is t . l j u b i t e l j i L j u b i m a c a . v r e d n o s t i { ) ) f o r(L J u b im a c l j : l l j ) 1ju b im c i .a d d ( l j ) ; S y s te m .o u t.p rin tln (lju b im c i);

}
} / * Is p is : [Cat E l s i e May, Cat P in k o la , Cat S h a c k le t o n , Cat S t a n f o rd aka S t i n k y el Negro, Cymric M o l l y , Dog M a r g r e t t , Mutt Sp ot, Pug Louie aka L ouis S n o r k e ls t e i n Dupree, Rat F i z z y , Rat F r e c k ly , Rat. Fuzzy

* ///:Poto svi ovi ljubim ci im aju im ena, ureuje se prvo po tipu, zatim po im enu i najzad po im enu u n u ta r njihovog tipa. N apisati odgovarajue m etode h ash C od e( ) i eq u als( ) za novu klasu um e da bude zapetljano. Alatke koje e vam pri tom e pom oi pronai ete u Apache projektu Jakarta C om m ons na adresi jakarta.apache.org/com m ons, p o d lan g (ovaj projekat im a i m nogo drugih potencijalno korisnih biblioteka, pa izgleda da je to odgovor Javine zajednice na lokaciju u'mv.froosf.org zajednice korisnika C + + -a). Veba 26: (2) U program PrebrojanZnakovniNiz dodajte char polje koje se takoe inicijalizuje u konstruktoru i prepravite m etode h ash C od e( ) i eq u a ls( ) tako da obuhvate vrednost ovog polja. Veba 27: (3) Prepravite m etodu h ash C od e( ) u program u PrebrojanZnakovniNiz.java tako to ete ukloniti kom binaciju sa id, i pokaite da PrebrojanZnakovniNiz i dalje radi kao klju. Zato ovaj pristup ne valja? Veba 28: (4) Dodavanjem m etoda h a sh C o d e( ), eq u a ls( ) i realizovanjem interfejsa Comparable za svaki tip N_torke prepravite net/m indview/util/N_torka.java tako da postane klasa opte nam ene.

Izbor realizacije
Dosad je trebalo da shvatite sledee: iako postoje sam o etiri osnovna tipa kontejnera - M ap, List, Set i Q ueue - svaki od tih interfejsa im a vie realizacija. Ako vam treba funkcionalnost odreenog interfejsa, kako da izaberete njegovu realizaciju? Svaka realizacija ima svoja obeleja, prednosti i nedostatke. Na prim er, na slici s poetka ovog poglavlja vidite da je obeleje klasa Hashtable, Vector i Stack to to su nasleene iz prethodnih verzija Jave (engl. legacy), kako se stari kod ne bi ruio zbog prevoenja novim prevodiocim a (ali ih ne treba koristiti u novim program im a). Vrste redova za ekanje u (Q ueues) u Javinim bibliotekam a razlikuju se sam o po nainu prim anja i vraanja vrednosti (vanost toga objasniem o u poglavlju Paralclno
izvravanje).

Razlika izmeu kontejnera esto se svodi na to kakvi su ,,u pozadini- tj. koje strukture podataka fiziki realizuju eljeni interfejs. Na prim er, poto ArrayList i LinkedList realizuju interfejs List, elem entarne List operacije su iste bez obzira na to koju od njih upotrebite. M eutim , u pozadini A rrayListe je niz, dok je LinkedList realizovana na nain uobiajen za dvostruko ulananu listu, kao grupa pojedinanih objekata koji sadre

684

Misliti na Javi

podatke i reference na prethodni i sledei elem ent u listi. Upravo zato, ako nam eravate da m nogo um eete i uklanjate elem ente usred liste, koristite LinkedList. (LinkedList im a i dodatn u funkcionalnost, utvrenu u listi AbstractSequentialList.) Ali ako vam to nije potrebno, uzm ite listu ArrayList koja je obino bra. Kao drugi prim er, Set se moe realizovati kao TreeSet, HashSet ili LinkedHashSet.9 Ponaanje svakog od tih skupova je drugaije: HashSet je podesan za tipinu u p o treb u i najbre obavlja pretraivanje, LinkedHashSet uva parove u poretku um etanja, a u pozadini skupa TreeSet nalazi se m apa TreeMap koja uvek daje ureen skup. Realizaciju birate u skladu s ponaanjem koje vam je potrebno. Ponekad razliite realizacije odredenogkontejnera im aju neke zajednike operacije, ali su perform anse tih operacija razliite. U tom sluaju, realizaciju birate na osnovu toga koliko esto koristite odreenu operaciju i koliko brza ona m ora da bude. U takvim sluajevim a se o razlikama izm eu realizacija kontejnera m oe zakljuivati i kroz ispitivanje perform ansi.

Struktura za ispitivanje performansi


Napravio sam stru k tu ru (engl. fram ew ork) za ispitivanje u koju sam stavio osnovne funkcije za postupak ispitivanja, da bih izbegao dupliranje koda i obezbedio ujednaenost ispitivanja. Sledei program pravi osnovnu klasu od koje m oete napraviti listu anonim nih unutranjih klasa, po jednu za svako ispitivanje. Postupak ispitivanja se sastoji od pozivanja svake od tih unutranjih klasa. Tim e je olakano dodavanje i uklanjanje novih vrsta ispitivanja. Ovo je jo jedan prim er projektnog obrasca Tem plate M eth o d (ablonska m etoda). Iako redefinisanjem m etode T est.ispit( ) za svako razliito ispitivanje sledim o tipian pristup Tem plate M ethod, u ovom sluaju je osnovni kod (onaj koji se ne m enja) u zasebnoj klasi Tester.1 0 Tip kontejnera koji se ispituje opisan je generikim param etrom C:
/ / : k o n te jn e ri/T e s t.ja v a / / S t r u k t u r a za vremensko i s p i t i v a n j e k o n t e jn e r a . p u b l i c a b s t r a c t cla ss Test<C> { S t r i n g ime; p u b l i c T e s t ( S t r i n g ime) { t h i s . i m e = ime; } / / R e d e f i n i i t e ovu metodu za svaku v r s t u i s p i t i v a n j a . / / Vraa s t v a r n i b r o j p o n a v l ja n ja i s p i t i v a n j a . a b s t r a c t i n t i s p i t ( C k o n t e j n e r , Pa ra m ls p it a p i ) ;

} III-Svaki objekat tipa Test skladiti ime tog testa (ispitivanja). Kada pozovete m etodu ispit( ), m orate joj dati kontejner koji treba ispitati i ,,prenosioca ili objekat za prenos podataka koji sadri sve param etre tog ispitivanja. To su param etri velicina, jednak bro^ Ui k a o E n u m S e t o d n o s n o C o p y O n W r itc A r ra y S e t, to su sp e c ija ln i slu ajev i. N e p o ri e m d a p o s to je d ru g e s p e c ija liz o v a n e realizacije ra z n ih k o n te jn e rs k ih in te rfe js a , ali u o v o m o d e ljk u p o k u a v a m d a s a g le d a m o p tiju sliku. U s m i lja n ju g e n e ri k ih tip o v a p o d e s n ih za o v o p o g la v ljc p o m o g a o m i je K rz v sz to f Sohole\vski.

Poglavlje 17: Detajjno razmatranje kontejnera

685

ju elem enata u kontejneru, i petlje, koji odreuje broj iteracija za to ispitivanje. Ti param etri se m ogu koristiti u svakom ispitivanju ali i ne m oraju. Svaki kontejner e biti podvrgnut nizu poziva m etode is p it( ), svaki p u t s razliitim param etrim a ispitivanja (objektom tipa Paramlspita), pa Paramlspita sadri i dve statine m etode n iz ( ) za lako pravljenje nizova Paramlspita objekata. Prva verzija m etode n iz ( ) p rim a listu prom enljivih argum enata koja naizm enino sadri vrednosti velicina i petlji, a druga verzija prim a listu iste vrste, sem to su vrednosti sm etene u znakovne nizove (objekte tipa String) - pa se moe upotrebljavati za ralanjivanje argum enata na kom andnoj liniji:
/ / : k o n te jn e ri/P a ra m ls p ita .ja v a / / " O bjeka t za prenos pod ata ka ". p u b l i c c la s s Pa ra m ls p it a ( p u b lic fin a l i n t v e lic in a ; p u b lic f in a l i n t p e t lje ; p u b lic P a ra m ls p ita (in t v e lic in a , th is .v e lic in a = v e lic in a ; th is . p e t lje = p e tlje ;

in t p e tlje )

}
/ / P r a v l j e n j e n iz a o b je k a ta t i p a Pa ra m ls p it a od sekvence p r o m e n l j i v i h / / argumenata: p u b l i c s t a t i c P a r a m ls p it a [ ] n i z ( i n t . . . v r e d n o s t i ) { in t v e lic in a = v re d n o s ti.le n g th /2 ; P a r a m ls p it a [ ] r e z u l t a t = new P a r a m l s p i t a [ v e l i c i n a ] ; i n t n = 0; f o r ( i n t i = 0; i < v e l i c i n a ; i+ +) r e z u l t a t [ i ] = new P a r a m I s p i t a ( v r e d n o s t i [ n + + ] , v r e d n o s t i [ n + + ] ) ; retu rn r e z u lta t;

}
/ / P r e t v o r i n iz o b je k a ta t i p a S t r i n g u n i z o b je k a ta t i p a P a r a m ls p it a : p u b l i c s t a t i c P a r a m ls p it a [ ] n i z ( S t r i n g [ ] v r e d n o s t i ) { i n t [ ] v r d n s t i = new i n t [ v r e d n o s t i . l e n g t h ] ; f o r f i n t i = 0; i < v r d n s t i . 1 e n g th ; i++) v r d n s t i[ i] = In te g e r.d e c o d e (v re d n o s ti[i]); retu rn n iz ( v r d n s t i) ;

} } ///:S truk turu za ispitivanje koristite tako to odreenoj m etodi Tester.pokreni( ) (to su prigodne preklopljene generike m etode zahvaljujui kojima ne m orate toliko da kucate) prosledite kontejner koji treba ispitati i Listu Test objekata. Tester.pokreni( ) poziva odgovarajui preklopljeni konstruktor, a zatim poziva m etodu vrem enskoIspit( ) koja izvrava svako ispitivanje navedeno u listi za taj kontejner. vrem enskoIspit( ) ponavlja svako ispitivanje za svaki od Paramlspita objekata u listi listaParam. Poto se listaParam inicijalizuje pom ou statinog niza podrazumevaniParam, zadavanjem novih vrednosti niza podrazumevaniParam moete da prom enite param etre listaParam za sva ispiti-

686

Misliti na Javi

vanja, a za jedno ispitivanje param etre listaP aram m oete da prom enite prosleivanjem liste listaP aram prilagoene za to ispitivanje:
/ / : k o n te jn e ri/T e s te r.ja v a / / P r im e n ju je Test o b je k t e na l i s t e r a z l i i t i h im p o r t j a v a . u t i l

k o n t e jn e r a .

p u b l i c c la s s Tester<C> { p u b l i c s t a t i c i n t s i r i n a P o l j a = 8; p u b l i c s t a t i c P a r a m ls p it a G podrazumevaniParam= P a r a m l s p i t a . n i z ( 10, 5000, 100, 5000, 1000, 5000, 10000, 50 0 ); / / R e d e f i n i i t e ovo da b i s t e m o d i f i k o v a l i i n i c i j a l i z a c i j u / / pre i s p i t i v a n j a : protected C i n i c i j a l i z u j ( i n t v e lic in a ) { re tu rn ko n tejne r; } protected C ko n tejne r; p r i v a t e S t r i n g n a s lo v = p riv a te L ist< T e st< C is p it iv a n ja ; p riv a te s t a t i c S trin g p o lje S tr in g () r e t u r n "%" + s i r i n a P o l j a + " s " ;

}
p riv a te s t a t ic S trin g p o lje B ro jO r e t u r n "%" + s i r i n a P o l j a + " d " ; {

}
p r i v a t e s t a t i c i n t s i r i n a V e l i c i n e = 5; p r i v a t e s t a t i c S t r i n g p o l j e V e l i c i n e = "%" + s i r i n a V e l i c in e + " s " ; p r i v a t e P a r a m ls p it a [ ] lis t a P a r a m = podrazumevaniParam; p u b l i c T e ste r(C k o n t e j n e r , L i s t < T e s t < C i s p i t i v a n j a ) { t h is . k o n t e jn e r = k o ntejner; t h i s . is p itiv a n ja = is p itiv a n ja ; i f ( k o n t e j n e r != n u l 1) n a slo v = k o n t e j n e r . g e t C l a s s ( ) .getSim ple N am e();

}
p u b l i c T e s te r(C k o n t e j n e r , L i s t < T e s t < C i s p i t i v a n j a , P a r a m ls p it a [ ] lis t a P a r a m ) { th is (k o n te jn e r, is p itiv a n ja ) ; t h i s . 1istaParam = lis t a P a r a m ;

}
p u b l i c v o id z a d a jN a s lo v ( S t r in g novNaslov) n a slo v = novNaslov; {

}
/ / Pomone g e n e r i k e metode : p u b l i c s t a t i c <C> v o id pokre ni (C k n t n r , L i s t < T e s t < C i s p i t i va nja ) { new T e s t e r < C > ( k n t n r , i s p i t i v a n j a ) . v r e m e n s k o ls p it ( ) ;

}
p u b l i c s t a t i c <C> vo id p o kre n i( C k n t n r , L i s t < T e s t < C i s p i t i v a n j a , P a r a m ls p it a [ ] lis ta P a r a m ) { new T e s t e r < C > ( k n t n r, i s p i t i v a n j a , 1is t a P a r a m ) . v r e m e n s k o ls p it ( ) ;

Poglavlje 17: Detaljno razmatranje kontejnera

687

p r i v a t e v o id p r i k a z i Z a g l a v l j e ( ) { / / I z r a u n a j i r i n u i dopuni znacima in t s ir in a = s irin a P o lja * is p it iv a n ja . s iz e ( ) + s irin a V e lic in e ; i n t d u z in a C r t ic a = s i r i n a - n a s l o v . l e n g t h ( ) - 1; S t r i n g B u i l d e r z g l a v l j e = new S t r i n g B u i l d e r ( s i r i n a ) ; f o r ( i n t i = 0 ; i < d u z i n a C r t i c a / 2 ; i+ + ) z g la v lje .a p p e n d ( '- '); z g la vlje .a p pe n d O ' ) ; z g la v lje .a p p e n d (n a s lo v ); z g la v lje .a p p e n d (' ' ) ; f o r ( i n t i = 0; i < d u z i n a C r t i c a / 2 ; i+ + ) z g la v lje .a p p e n d ( '- '); S y s te m .o u t.p rin tln (z g la v l j e ) ; / / I s p i s i v a n j e z a g l a v l j a ko lo n a : S ys te m .o u t.fo rm a t(p o lje V e lic in e , " v e l . " ) ; fo r(T e s t i s p i t : is p itiv a n ja ) S ys te m .o u t.fo rm a t(p o lje S trin g (), is p it . im e ) ; S y s te m .o u t.p rin tln ();

}
/ / Pokreni i s p i t i v a n j a za ovaj k o n t e j n e r : p u b l i c v o id v r e m e n s k o I s p i t( ) { p rik a z iZ a g la v lje O ; f o r ( P a r a m I s p i t a param : li s t a P a r a m ) { S y s t e m . o u t . f o r m a t ( p o l j e V e l i ci ne, p a ra m . v e li c i n a ) ; f o r(T e s t< C > i s p i t : i s p i t i v a n j a ) { C kkontejner = i n i c i j a l i z u j(p a ra m .v e li c in a ) ; lo ng s t a r t = System.nanoTime(); / / Poziv r e d e f i n i s a n e metode: i n t p o n a v l ja n ja = i s p i t . i s p i t ( k k o n t e j n e r , param); long t r a j a n j e = System.nanoTime() - s t a r t ; lo ng vremeZaJednoPonavljanje = t r a j a n j e / p o n a v l ja n ja ; / / Nanosekunde S y s t e m . o u t . f o r m a t ( p o l j e B r o j ( ) , vrem eZaJednoPonavlja nje );

}
S y s te m .o u t.p rin tln ();

} } } ///:M etode p oljeString( ) i p oljeB roj( ) proizvode znakovne nizove za form atiranje rezultata prilikom ispisa. Standardnu irinu za form atiranje m enjate m odifikovanjem statine vrednosti sirinaPolja. Metoda prikaziZaglavlje( ) form atira i ispisuje zaglavlje s podacima iz svakog ispitivanja. Ako vam je potrebna specijalna inicijalizacija, redefiniite m etodu in icijalizu j( ). O na proizvodi inicijalizovan objekat kontejner odgovarajue veliine - moete modifikovati postojei objekat kontejner ili napraviti novi. U m etodi isp it( ) vidite da se rezultat hvata u lokalnoj referenci nazvanoj kontejner, to om oguuje da uskladiteni lan kontejner zam enite p otp u n o drugaije inicijalizovanim kontejnerom .

688

Misliti na Javi

Povratna vrednost svake m etode T est.ispit( ) m ora biti broj operacija obavljenih tokom tog ispitivanja, to se koristi za izraunavanje broja nanosekundi potrebnih za svaku operaciju. Imajte u vidu da m etoda S ystem .nanoT im e( ) obino daje vrednosti ija granularnost prem auje jedan (i m enja se u zavisnosti od raunara i operativnog sistema), a to e prouzrokovati odreenu koliinu um a u rezultatim a. Rezultati se m ogu m enjati u zavisnosti od raunara na kojem se ispitivanja obavljaju; ova ispitivanja su nam enjena sam o za relativno poreenje perform ansi razliitih kontejnera, a ne za apsolutno m erenje tih perform ansi.

Performanse razliitih Lista


Evo ispitivanja perform ansi osnovnih operacija u listam a. Poreenja radi, prikazane su i najvanije operacije u redovim a za ekanje (Q ueue). Za ispitivanje svake klase kontejnera napravljene su dve zasebne Iiste testova. U ovom sluaju, operacije u redovim a za ekanje odnose se sam o na ulanane liste (LinkedList).
/ / : k o n tejne ri/P e rfo rm a n s eL is ta .ja v a / / Pokazuje r a z l i k e u performansama L i s t a . / / {Argumenata: 100 500} Malo, da bi i s p i t i v a n j e b u i l d a b i l o k r a t k o im p ort j a v a . u t i l . * ; im p ort n e t . m i n d v i e w . u t i l . * ; p u b l i c c la s s P e rfo rm a n seL is ta { s t a t i c Random s lu c a ja n = new Random(); s t a t i c i n t p o n a v l ja n ja = 1000; s t a t ic L is t< T e s t< L is t< In te g e r > i s p it iv a n ja = new A r r a y L is t < T e s t < L i s t < I n t e g e r > ( ) ; s t a t i c L i s t < T e s t < L i n k e d L i s t < I n t e g e r > is p it iv a n ja R e d a Z a C e k a n je = new A r r a y L i s t < T e s t < L i n k e d L i s t < I n t e g e r > ( ) ; s ta tic { is p itiv a n ja .a d d (n e w T e s t< L is t< In te g e r ( " a d d " ) { i n t i s p i t ( L i s t < I n t e g e r > l i s t a , P a r a m ls p ita p i ) { in t p e tlje = p i . p e tlje ; i n t v e lic in a L iste = p i . v e l i c i n a ; f o r ( i n t i = 0 ; i < p e t l j e ; i+ + ) { 1i s t a . c l e a r ( ) ; f o r ( i n t j = 0; j < v e l i c i n a L i s t e ; j+ + ) lis ta .a d d (j);

}
return p e t lje * v e lic in a L is te ;

} });
is p i t i v a n j a . a d d ( n e w T e s t < L i s t < I n t e g e r ( " g e t " ) { i n t i s p i t ( L i s t < I n t e g e r > l i s t a , P a r a m ls p ita p i ) { i n t p e t lje = p i . p e t l j e * p o n a v lja n ja ; in t v e lic in a L is te = l is t a . s iz e ( ) ; f o r ( i n t i = 0; i < p e t l j e ; i++)

Poglavlje 17: Detafjno razmatranje kontejnera

689

1i s t a . g e t ( s l u c a j a n . n e x t I n t ( v e l i c i naLi s t e ) ) ; return p e t lje ;

} });
is p itiv a n ja .a d d (n e w T e s t< L is t< In te g e r ( " s e t" ) { i n t i s p i t ( L i s t < I n t e g e r > l i s t a , P a ra m ls p ita p i ) { i n t p e t l j e = p i . p e t l j e * p o n a v lja n ja ; in t v e lic in a L is te = lis t a . s iz e ( ) ; f o r ( i n t i = 0; i < p e t l j e ; i+ + ) 1i s t a . s e t ( s l u c a j a n . n e x t l n t ( v e l i c i n a L i s t e ) , 4 7 ); retu rn p e t lje ;

} });
is p itiv a n ja .a d d (n e w T e s t< L is t< In te g e r ( " ite ra d d " ) { i n t i s p i t ( L i s t < I n t e g e r > l i s t a , P a r a m ls p it a p i ) { f i n a l i n t PETLJE = 1000000; i n t p o lo v in a = l i s t a . s i z e ( ) / 2; L is tIte ra to r< In te g e r> i t = lis t a . lis t lt e r a t o r ( p o lo v in a ) ; f o r ( i n t i = 0; i < PETLJE; i+ + ) it.a d d (4 7 ); r e t u r n PETLJE;

} });
is p itiv a n ja .a d d (n e w T e s t< L is t < I n t e g e r ( " in s e r t " ) { i n t i s p i t ( L i s t < I n t e g e r > l i s t a , P a r a m ls p ita p i ) { in t p e tlje = p i.p e tlje ; f o r ( i n t i = 0; i < p e t l j e ; i++) l i s t a . a d d ( 5 , 4 7 ) ; / / M in im iz o v a n je t r o k o v a / / nasuminog p r i s t u p a n j a return p e t lje ;

} });
i s p itiva n ja .ad d (n ew T e s t< L is t< In te g e r ("re m o v e ") { i n t i s p i t ( L i s t < I n t e g e r > l i s t a , P a ra m ls p ita p i ) { in t p e t lje = p i.p e tlje -, in t v e lic in a = p i.v e lic in a ; f o r ( i n t i = 0; i < p e t l j e ; i+ + ) { 1i s t a . c l e a r ( ) ; 1i s t a . a d d A l 1 (new B r o j a c k a L i s t a l n t e g e r a ( v e l i c i n a ) ) ; whi 1e ( l i s t a . s i z e O > 5) 1i s t a . r e m o v e ( 5 ) ; / / M in im iz o v a n je tr o k o v a / / nasuminog p r i s t u p a n j a

}
retu rn p e t lje * v e lic in a ;

} });
/ / I s p i t i v a n j a ponaanja reda za e ka n je : is p it iv a n ja R e d a Z a C e k a n je .a d d (n e w T e s t < L in k e d L i s t< In te g e r ("a d d F irs t")

690

Misliti na Javi

i n t i s p i t ( L i n k e d L i s t < I n t e g e r > l i s t a , P a r a m ls p ita p i ) in t p e tlje = p i.p e tlje ; in t v e lic in a = p i. v e lic in a ; f o r ( i n t i = 0; i < p e t l j e ; i+ + ) { lis ta .c le a r(); f o r ( i n t j = 0; j < v e l i c i n a ; j+ + ) 1i s t a . a d d F i r s t ( 4 7 ) ;

}
retu rn p e t lje * v e lic in a ;

} });
is p itiv a n ja R e d aZ a C e k a n je .a d d (ne w T e s t < L in k e d L i s t< In te g e r ("a d d L a s t") { i n t i s p i t ( L i n k e d L i s t < I n t e g e r > l i s t a , P a r a m ls p ita p i ) in t p e tlje = p i.p e tlje ; in t v e lic in a = p i.v e lic in a ; f o r ( i n t i = 0; i < p e t l j e ; i+ + ) { lis ta .c 1 e a r(); f o r ( i n t j = 0; j < v e l i c i n a ; j+ + ) lis ta .a d dL a st(4 7); {

}
retu rn p e t lje * v e lic in a ;

} });
i s p i tivanja RedaZaCekanje .add( new T e s t < L i n k e d L i s t < I n t e g e r ( " r m F i r s t " ) { i n t i s p i t ( L i n k e d L i s t < I n t e g e r > l i s t a , P a ra m ls p ita p i ) { in t p e tlje = p i.p e tlje ; in t v e lic in a = p i.v e lic in a ; f o r ( i n t i = 0; i < p e t l j e ; i++) { 1i s t a . c l e a r ( ) ; 1i s t a . a d d A l 1 (new B r o j a c k a L i s t a l n t e g e r a ( v e l i c i n a ) ) ; w h i l e ( l i s t a . s i z e ( ) > 0) 1i sta.rem oveF i r s t ( ) ;

}
return p e t lje * v e lic in a ;

} });
is p itiv a n ja R e d aZ a C e ka n je .a d d (ne w T e s t < L i n k e d L i s t < I n t e g e r ("rm L ast") { i n t i s p i t ( L i n k e d L i s t < I n t e g e r > l i s t a , P a ra m ls p it a p i ) { i n t p e t l j e = p i . p e t 1j e ; in t v e lic in a = p i.v e lic in a ; f o r ( i n t i = 0; i < p e t l j e ; i+ + ) { 1i s t a . c l e a r ( ) ; 1i s t a . a d d A l 1 (new B r o j a c k a L i s t a l n t e g e r a ( v e l i c i n a ) ) ; w h i l e ( l i s t a . s i z e ( ) > 0) lis ta .re m o v e L a s t();

Poglavlje 17: Detaljno razmatranje kontejnera

691

}
return p e t lje * v e lic in a ;

} }); }
s t a t i c c la s s I s p i t i v a c L i s t a extends T e s t e r < L i s t < I n t e g e r { p u b lic Is p itiv a c L is ta ( L is t< In te g e r > k o n tejne r, L is t< T e s t< L is t< In te g e r> is p itiv a n ja ) { s u p e r ( k o n t e jn e r , i s p i t i v a n j a ) ;

}
/ / Pre svakog i s p i t i v a n j a popuni do o d g ova ra ju e v e l i i n e : @0verride p r o t e c t e d L i s t < I n t e g e r > i n i c i j a l i z u j ( i n t v e l i c i n a ) { k o n te jn e r.c le a r(); k o n t e j n e r . a d d A ll( n e w B r o j a c k a L i s t a I n t e g e r a ( v e l i c i n a ) ) ; return kontejner;

}
/ / Pomona metoda: p u b l i c s t a t i c v o id p o k r e n i ( L i s t < I n t e g e r > l i s t a , L is t< T e s t< L is t< In te g e r > is p it iv a n ja ) { new I s p i t i v a c L i s t i ( 1 i s t a , i s p i t i v a n j a ) . v r e m e n s k o I s p i t ( ) ;

} }
p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] a rg s) { i f ( a r g s . 1 ength > 0) Tester.podrazumevaniParam = P a r a m l s p i t a . n i z ( a r g s ) ; / / Na n iz se mogu p r i m e n i t i samo sle dea dva i s p i t i v a n j a : T e s te r< L is t< In te g e r is p itN iz a = new T e s t e r < L i s t < I n t e g e r ( n u l l , i s p i t i v a n j a . p o d L i s t a ( l , 3 ) ) { / / Ovo e b i t i pozvano pre svakog i s p i t i v a n j a . / / P r o iz v o d i l i s t u n e p ro m e n ljiv e v e l i i n e s nizom u p o z a d i n i : @0verride p r o t e c t e d L is t < I n t e g e r > i n i c i j a l i z u j ( i n t v e l i c i n a ) { In te g e r[] ia = G e n e ra te d .n iz (In te g e r.c la s s , new C o u n t ingG enerator. I n t e g e r O , s i z e ) ; return A r r a y s . a s L is t ( ia ) ;

} };
is p i t N i z a . z a d a j N a s l o v ( " N i z kao l i s t a " ) ; is p itN iz a .v re m e n s k o Is p it(); Tester.podrazu meva ni Param= Pa ra m ls p it a . n i z ( 10, 5000, 100, 5000, 1000, 1000, 10000, 20 0 ); i f ( a r g s . l e n g t h > 0) Tester.podrazumevaniParam = P a r a m l s p i t a . n i z ( a r g s ) ; Is p itiv a c L is ta .p o k re n i(n e w A rra y L is t< In te g e r> (), i s p i t i v a n j a ) ; I s p i t i v a c L i s t a . p o k r e n i (new L i n k e d L i s t < I n t e g e r > ( ) , i s p i t i v a n j a ) ; I s p i t i v a c L i s t a . p o k r e n i (new V e c t o r < I n t e g e r > ( ) , i s p i t i v a n j a ) ; T e s t e r . s i r i n a P o l j a = 12; T e s t e r < L i n k e d L i s t < I n t e g e r is p it iv a n je R e d a Z a C e k a n je =

692

Misliti na Javi

new T e s te r< L in k e d L i s t < I n t e g e r ( new L i n k e d L i s t < I n t e g e r > ( ) , i s p it iv a n ja R e d a Z a C e k a n je ) ; is p it iv a n je R e d a Z a C e k a n j e . z a d a j N a s l o v ( " I s p i t i v a n j a reda za e k a n je 1 '); is p i t i v a n je R e d a Z a C e k a n je . v r e m e n s k o I s p it ( ) ;

}
} / * I s p i s : ( p r im e r) Niz kao 1i s t a v e l. get set 10 130 183 164 100 130 1000 129 165 10000 129 165 v e l. 10 100 1000 10000 v e l. 10 100 1000 10000 v e l. 10 100 1000 10000 v e l. 10 100 1000 10000 *///-A rra y L is t remove set ite ra d d in s e r t 3952 446 191 435 3934 296 191 247 2202 923 194 839 14042 6880 7333 190 L in k e d L i s t remove add get s e t it e r a d c 1 i n s e r t 262 164 658 366 182 198 457 108 201 106 202 230 136 239 430 133 1289 1353 255 239 13648 13187 435 172 - V e c to r remove add get s e t it e r a d d 1 i n s e r t 3635 253 145 187 290 129 292 144 263 3691 72 190 2162 927 145 193 846 99 14730 7135 108 145 186 6871 -------- I s p i t i v a n j a reda za e ka n je --------rmFi r s t rmLast addFi r s t addLast 251 253 199 163 179 92 180 98 212 216 99 93 384 262 109 111 add 121 72 98 122 get 139 141 141 144

O svakom ispitivanju treba dobro razmisliti, da ne biste dobijali besmislene rezultate. Na prim er, ispitivanje operacije add brie Listu i zatim je popunjava do zadate veliine liste. Zato je poziv m e to d e c le a r() deo ispitivanja i moe uticati na izm ereno vreme, naroi to u malim ispitivanjima. Iako prethodni rezultati izgledaju prilino razum no, struktura za ispitivanje bi se mogla preraditi tako da pozivpriprem ne m e to d e - to bi, u ovom sluaju, obuhvatilo i poziv m etode c le a r() - bude izvan petlje u kojoj se meri vreme. Vodite rauna o tom e da svako ispitivanje m ora tano izraunati broj obavljenih operacija i vratiti tu vrednost iz m etode is p i t( ), da bi m erenje vrem ena bilo tano.

Poglavlje 17: Detaljno razmatranje kontejnera

693

U ispitivanjima operacija get i set upotrebljava se generator sluajnih brojeva za nasum ino pristupanje Listi. Iz ispisa vidite da su za Listu napravljenu na osnovu niza i za ArrayList ta pristupanja brza i ujednaenog trajanja bez obzita na veliinu liste, d o k za ulananu listu (LinkedList) trajanje pristupanja znatno rastc z a vee liste. O igledno je da ulanane liste nisu dobro reenje ukoliko nam eravate da obavljate m nogo nasum inih pristupanja. U ispitivanju operacije iteradd, za um etanje novih elemena* upotrebljen je iterator u sredini liste. Za kontejner ArrayList to postaje skupo kako lista i aste, ali za LinkedList relativno je jeftino, a i konstantnog trajanja bez obzira na veliiru. To im a smisla zato to ArrayList tokom um etanja m ora da napravi prosto r i kopira sve svoje reference od tog mesta unapred. To postaje skupo kako ArrayList postaje sve ' ca. O bjekat tipa LinkedList treba sam o da povee nov elem ent i ne m ora da m enja ost?i - h liste, p a bi reijski trokovi te operacije trebalo da b u d u priblino jednaki, bez obziia a a veliinu liste. U ispitivanju operacija insert i remove kao m esto um etanja odnosno uklanjanja koristi se lokacija broj 5, a ne neki kraj Liste. U lanana lista (LinkedL isr; im a poseban algoritam za krajnje lokacije Liste - tim e se poveava brzina kada se ulanana lista upotrebljava kao red za ekanje (Queue). M edutim , ukoliko elem ente dodajete t : nklanjate u sredini liste, prouzrokujete trokove nasum inog pristupanja koji se razlikuju za razne realizacije Lista (kao to ste videli). Poto su um etanja i uklanjanja na istoj loka 'i broj 5, trebalo bi da su trokovi nasum inog pristupanja zaneinarivi i da vidim o sa o trokove um etanja i uklanjanja, ali i da ne vidim o bilo kakvu posebnu optimizaciju za krajeve ulanane liste (LinkedList). Iz rezultata vidite da su trokovi um etanja i uklanja:ija iz ulanane liste veoma mali i da se ne menjaju u zavisnosti od veliine liste, ali su tre ovi (naroito um etanja) u objekat tipa A rrayList veom a veliki i poveavaju se s veliinom liste. Iz ispitivanja redova za ekanje (objekata tipa Queue) viditc icoliko brzo ulanana lista umee i uklanja elemente s krajnjih taaka liste, to je optim abio za ponaanje redova za ekanje. O bino je dovoljno samo pozvati m etodu T ester.p o k ren i() i proslediti joj kontejner i spisak ispitivanja. M eutim , ovde m oram o redefinisati m etodu sn ic ija liz u j() da bi Lista bila obrisana i ponovo popunjena pre svakog ispitivanja - inae bi Lista tokom raznih ispitivanja izgubila kontrolu nad svojom veliinom. IspitivacLisfa nasleduje klasu Tester i obavlja inicijalizaciju uz pom o objekta tipa B rojackaL istalntegera. Redefinisana je i pom ona m etoda pokreni( ). eleli bism o i da uporedim o pristupanje nizovim a s p n stu p an jem kontejnerim a (prvenstveno kontejnerim a tipa A rrayList). U prvom ispitivanju u n u tar m etode m a in ( ), pom ou anonim ne unutranje klase napravljen je poseban nbjekat tipa Test. M etoda in ic ija liz u j() tako je redefinisana da pravi nov objekat svaki pul kada b ude pozvana (zanem aruje uskladiteni objekat kontejner, pa je za ovaj ko n struktor klase T ester argum ent k o n te jn er jednak null). Novi objekat se pravi m etodam a G e n e ra te d .n iz () (definisanom u poglavlju N izovi) odnosno A rrays.asL ist(). U ovom sluaju rnogu se obaviti sam o dva ispitivanja, zato to elem ente ne moete um etati u L istu napravljenu na osnovu niza niti ih uklanjati, pa je za izbor ispitivanja iz spiska isp itiv an ja upotrebljena m etoda L ist.p o d L ista ().

694

Misliti na Javi

Za operacije nasum inog (nesekvencijalnog) pristupanja g e t ( ) i s e t ( ), Lista napravljena na osnovu niza neznatno je bra od kontejnera tipa ArrayList, ali su iste te operacije m nogo skuplje kada se koriste ulanane liste, poto klasa LinkedList nije nam enjena za operacije nasum inog (nesekvencijalnog) pristupanja. Klasu Vector treba izbegavati; ona je u biblioteci sam o da bi se obezbedila podrka za stari kod (u ovom program u deluje sam o zato to je radi kom patibilnosti s novim kodom preraena tako da postane Lista). CopyOnWriteArrayList je posebna realizacija klase List koja se upotrebljava u program iranju za paralelno izvravanje. N ju em o razm otriti u poglavlju Paralelno izvravan je.V t rovatno je najbolja politika uzeti ArrayList za podrazum evani tip kontejnera i prei na LinkedList ako je p o trebn a dodatna funkcionalnost ili se ispostavi da m nogo um etanja u sredinu liste i uklanjanja iz nje oslabljuje perform anse. Ako radite s grupom elem enata neprom enljive veliine, upotrebite Listu - m etodom Arrays.asList( ) - napravljenu na osnovu niza ili pravi niz, ako je potrebno. Veba 29: (2) Izm enite program PerformanseLista.java tako da Liste um esto Integera skladite objekte tipa String. Za pravljenje niza ispitnih vrednosti upotrebite Generator iz poglavlja N izovi. Veba 30: (3) U poredite perform anse operacije C o lle c tio n s.so rt() u kontejnerim a tipa A rrayL ist odnosno LinkedList. Veba 31: (5) Napravite kontejner koji kapsulira niz objekata tipa S tring i dozvoljava sam o dodavanje i vaenje znakovnih nizova, pa se tokom upotrebe ne postavlja pitanje svoenja na druge tipove. Ako unutranji niz nije dovoljno velik za sledee dodavanje, kontejner treba da autom atski prilagodi njegovu veliinu. U m etodi m a in ( ) uporedite perform anse tog kontejnera i objekta tipa A rrayL ist< String> . Veba 32: (2) Ponovite p reth od nu vebu za kontejner celih brojeva (in t) i uporedite njegove perform anse i perform anse objekta tipa A rrayL ist< Integer> . Neka poreenje perform ansi obuhvati i postupak poveavanja svakog objekta u kontejneru za 1. Veba 33: (5) Napravite objekat tipa FastTraversalLinkedList koji interno upotrebljava ulananu listu (objekat tipa LinkedList) za brza dodavanja i uklanjanja, a objekat tipa ArrayList za brza prolaenja kroz kontejner i operacije g e t ( ). Ispitajte ga prepravljenim program om Perform anseL ista.java.

Opasnosti od mikroporeenja performansi


Kada piete program e za m ikroporeenjeperform ansi, uvajte se da ne pretpostavljate previe i toliko suzite testove da oni m ere vreme izvravanja sam o onih operacija koje se ispituju. Takoe, m orate paziti na to da ispitivanje bude dovoljno dugako da njegovi rezultati bu du statistiki pouzdani i uzm ite u obzir to da se neke Java HotSpot tehnologije ukljuuju tek nakon to se program izvrava odreeno vreme (to se m ora uzeti u obzir i kad se prave kratki program i). Rezultati e se razlikovati u zavisnosti od raunara i JVM-a koje upotrebljavate, pa bi trebalo da sam i obavite pretho dn a ispitivanja kako biste proverili da li su rezultati slini

Poglavlje 17: Detaljno razmatranje kontejnera

695

onim a navedenim u ovoj knjizi. Ne pokuavajte da izmerite apsolutna trajanja izvravanja pojedinih operacija, nego uporedite perform anse raznih vrsta kontejnera. Pored toga, program za optim izaciju e verovatno bolje analizirati perform anse nego to vi to m oete. Java se isporuuje s jednim program om za optim izaciju (videti dodatak na adresi http://M itidV iew .net/B ooks/B etterJava), a postoje i program i za optim izaciju nezavisnih proizvoaa, te besplatni/otvorenog izvornog koda i komercijalni. Srodan je prim er koji se bavi m etodom M a th .ra n d o m (). Daje li ona brojeve izmeu nule i jedinice, ukljuivo ili iskljuivo s brojem 1? M atem atiki reeno, jesu li njeni rezultati u intervalu (0,1), ili [0,1], ili (0,1] ili [0,1)? (Uglasta zagrada znai ,,obuhvata, a okrugla ,,ne obuhvata") M oda e nam odgovoriti program za ispitivanje:
/ / : k o n t e j n e r i / G r a n i ceOdRandom.java / / Daje l i metoda Math.random() b ro je v e 0 . 0 i / / {RunByHand} im p o r t s t a t i c n e t . m i n d v i e w . u t i l . P r i n t . * ; p u b l i c c la s s GraniceOdRandom { s t a t i c v o id u p o tr e b a ( ) { p rin t("U p o tre b a :"); p rin t ( " \ tG r a n ic e 0 d R a n d o m d o n j a " ) ; p rin t ( " \ tG r a n ic e 0 d R a n d o m g o r n j a " ) ; S y s te m .e x it(l);

1.0?

}
p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] a rg s) i f ( a r g s . l e n g t h != 1) u p o t r e b a ( ) ; if(a rg s .e q u a ls ("d o n ja ")) { w h i1e(Math .random() != 0 .0 ) ; / / Pokuavaj i d a l j e p rin t("D a la 0 .0 !" ) ; {

}
e ls e i f ( a r g s . e q u a l s ( " g o r n j a " ) ) w h i1e (M a th . random() != 1.0) ; / / Pokuavaj i d a l j e p rin t("D a la 1 .0 !" ) ; {

}
el se u p o tr e b a O ;

) III-Program pokreete upisivanjem jedne od sledeih instrukcija na kom andnu liniju:


ja v a GraniceOdRandom donja

ili
ja v a G r a n iceOdRandom g o rn ja

696

Misliti na Javi

U oba sluaja m oraete runo da prekinete izvravanje program a, pa izgleda kao da M ath.random ( ) nikada ne daje ni 0.0 niti 1.0. Ali tu vas ovakav eksperim ent moe prevariti. Ako uzm ete u obzir da izm eu 0 i 1 postoje oko 262 razliita dvostruka razlomka, verovatnoa da e se eksperim entalno dobiti bilo koji od tih pojedinanih brojeva tako je mala da prevazilazi ivotni vek raunara, pa ak i eksperim entatora. Ispostavlja se da metoda M ath.random ( ) m eu svojim rezultatim a daje i 0.0. Ili, m atem atiki reeno, njeni rezultati su u intervalu [0,1). Dakle, m orate paljivo analizirati svoje eksperim ente i shvatiti njihova ogranienja.

Izbor skupa (realizacije interfejsa Set)


U zavisnosti od traenog ponaanja, m oete izabrati TreeSet, HashSet ili LinkedHashSet. N aredni program za ispitivanje pokazuje razliite perform anse tih realizacija:
/ / : k o n te jn e r i/P e r fo rm a n s e S k u p o v a .ja v a / / Pokazuje r a z l i i t e performanse Skupova. / / { A rg s : 100 5000} Malo, da b i t e s t i r a n j e b u i l d a b i l o k r a t k o im p o r t j a v a . u t i l . * ; p u b l i c c la s s PerformanseSkupova { s t a t i c L is t < T e s t < S e t < I n t e g e r > i s p i t i v a n j a = new A r r a y L i s t < T e s t < S e t < I n t e g e r > ( ) ; s ta tic { is p itiv a n ja .a d d (n e w T e s t< S e t< In te g e r ("a d d ") { i n t i s p i t ( S e t < I n t e g e r > skup, P a r a m ls p ita p i ) { in t p e tlje = p i.p e tlje ; in t v e lic in a = p i. v e lic in a ; f o r ( i n t i = 0; i < p e t l j e ; i+ + ) { s k u p .c le a r(); f o r ( i n t j = 0; j < v e l i c i n a ; j+ + ) s k u p .a d d (j);

}
retu rn p e t lje * v e lic in a ;

} });
is p itiv a n ja .a d d (n e w T e s t< S e t< In te g e r ("c o n ta in s ") i n t i s p i t ( S e t < I n t e g e r > skup, P a r a m ls p ita p i ) i n t p e t l j e = pi . p e t l j e ; i n t opseg = p i . v e l i c i n a * 2; f o r ( i n t i = 0; i < p e t l j e ; i+ + ) f o r ( i n t j = 0; j < opseg; j+ + ) s k u p .c o n ta in s (j); r e t u r n p e t l j e * opseg; { {

} });
is p itiv a n ja .a d d (n e w T e s t< S e t < I n t e g e r ( " it e r a t e " ) i n t i s p i t ( S e t < I n t e g e r > skup, P a r a m ls p it a p i ) { i n t p e t l j e = p i . p e t l j e * 10; f o r ( i n t i = 0; i < p e t l j e ; i+ + ) { {

Poglavlje 17: Detaljno razmatranje kontejnera

697

Ite ra to r< In te g e r> i t w h ile (it.h a s N e x t()) it.n e x t();

= s k u p .ite ra to r();

}
retu rn p e t lje * s k u p .s iz e ();

} }); }
p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] a rg s) { i f ( a r g s . l e n g t h > 0) Tester.podrazumevaniParam = P a r a m l s p i t a . n i z ( a r g s ) ; T e s t e r . s i r i n a P o l j a = 10; T e s t e r . p o k r e n i( n e w T r e e S e t < I n t e g e r > ( ) , i s p i t i v a n j a ) ; T e s t e r . p o k r e n i( n e w H a s h S e t < I n t e g e r > ( ) , i s p i t i v a n j a ) ; T e s t e r . p o k r e n i( n e w L in k e d H a s h S e t < I n t e g e r> ( ) , i s p i t i v a n j a ) ;

}
} / * I s p i s : ( p r im e r ) ----------------------- TreeSet ----------------------v e l. add c o n t a i n s ite ra te 10 746 173 89 100 264 501 68 714 1000 410 69 10000 1975 552 69 -------- HashSet -------v e l. add c o n t a i ns ite ra te 10 308 91 94 100 178 75 73 1000 216 110 72 10000 711 215 100 - - LinkedHashSet - add c o n t a in s v e l. i te ra te 10 350 65 83 100 270 74 55 54 1000 303 111 10000 1615 256 58

* ///:Perform anse skupa HashSet po pravilu su bolje od onih skupa TreeSet, naroito pri dodavanju elem enata i traganju za njim a (m etodom co n ta in s( )), to su dve najvanije operacije. TreeSet postoji zato to svoje elem ente odrava u ureenom poretku, pa ga upotrebljavam o sam o onda kada nam treba ureen Set. Zbog unutranje stru k tu re potrebne za ureivanje i zato to se iteriranje ee izvodi, ono je obino bre u kontejneru tipa TreeSet nego tipa HashSet. O bratite paniu na to da su um etanja u skupu LinkedHashSet dugotrajnija nego u skupu HashSet; to vai zato to odravanje ulanane Iiste prouzrokuje doatne trokove, jer se dodaje onim a za kontejner s transform isanjem kljua. Veba 34: ( 1) Izm enite PerformanseSkupova.java tako da objekti tipa Set skladite znakovne nizove (String), a ne Integere. Za pravljenje ispitnih vrednosti upotrebite Generato r i/ poglavlja I\ri:o \i.

698

Misliti na Javi

Izbor mapa (realizacija interfejsa Map)


Ovaj program pokazuje razliite perform anse raznih realizacija interfejsa Map:
/ / : ko n te jn e r i/P e r fo rm a n s e M a p a .ja v a / / Pokazuje r a z l i i t e performanse r a z n ih Mapa. / / { A r g s : 100 5000} Malo, da bi t e s t i r a n j e b u i l d a b i l o k r a t k o im p o rt j a v a . u t i l p u b l i c c la s s PerformanseMapa { s t a t i c L is t < T e s t < M a p < I n t e g e r , I n t e g e r > i s p i t i v a n j a = new A r r a y L i s t < T e s t < M a p < I n t e g e r , I n t e g e r > ( ) ; s ta tic { is p i t i v a n j a . a d d ( n e w T e s t < M a p < I n t e g e r , I n t e g e r ( " p u t " ) { i n t i s p i t ( M a p < I n t e g e r , I n t e g e r > mapa, P a ra m ls p it a p i ) { in t p e tlje = p i.p e tlje ; in t v e lic in a = p i. v e lic in a ; f o r ( i n t i = 0; i < p e t l j e ; i+ + ) { m a p a .c le a r(); f o r ( i n t j = 0; j < v e l i c i n a ; j+ + ) m a p a . p u t ( j, j ) ;

}
retu rn p e t lje * v e lic in a ;

} });
is p i t i v a n j a . a d d ( n e w T e s t < M a p < I n t e g e r , I n t e g e r ( " g e t " ) { i n t i s p i t ( M a p < I n t e g e r , I n t e g e r > mapa, P a r a m ls p ita p i ) { in t p e tlje = p i . p e tlje ; i n t opseg = p i . v e l i c i n a * 2; f o r ( i n t i = 0; i < p e t l j e ; i+ + ) f o r ( i n t j = 0; j < opseg; j+ + ) m a p a .g e t(j); r e t u r n p e t l j e * opseg;

f);
i s p i t i v a n j a . a d d ( n e w Test<Map<Integer, I n t e g e r ( " i t e r a t e " ) i n t i s p i t (M a p < I n t e g e r ,I n t e g e r> mapa, Pa ra m ls p it a p i ) { i n t p e t l j e = p i . p e t l j e * 10; f o r ( i n t i = 0; i < p e t l j e ; i ++) { I t e r a t o r i t = mapa. e n t r y S e t ( ) . i t e r a t o r ( ) ; w h i1e ( i t . hasNext( ) ) it.n e x t(); {

)
r e t u r n p e t l j e * m a p a . s iz e ( ) ;

} }); }
p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] i f ( a r g s . l e n g t h > 0) a rg s) {

Poglavfje 17: Detaljno razmatranje kontejnera

699

Tester.podrazumevaniParam = P a r a m l s p i t a . n i z ( a r g s ) ; T e s t e r . p o k r e n i(n e w T r e e M a p < I n t e g e r , I n t e g e r > ( ) , i s p i t i v a n j a ) ; T e s t e r . p o k r e n i(n e w H a s h M a p < I n t e g e r , I n t e g e r > ( ) , i s p i t i v a n j a ) ; T e s t e r . p o k r e n i(n e w L in k e d H a s h M a p < I n t e g e r , I n t e g e r > ( ) . i s p i t i v a n j a ) ; T e s te r.p o k re n i( new I d e n t i t y H a s h M a p < I n t e g e r , I n t e g e r > ( ) , i s p i t i v a n j a ) ; T e s t e r . p o k r e n i(n e w W eakH a sh M a p < In te g e r,In te g e r> (), i s p i t i v a n j a ) ; T e s t e r . p o k r e n i( n e w H a s h t a b l e < I n t e g e r , I n t e g e r > ( ) , i s p i t i v a n j a ) ;

}
} /* v e l. 10 100 1000 10000 Is p is : (p r im e r)

put get i t e r a t e 748 168 100 264 76 506 771 450 78 2962 561 83 -------- HashMap get i t e r a t e v e l. put 10 281 76 93 100 70 73 179 1000 267 102 72 10000 265 97 1305 -- LinkedHashMap put get i t e r a t e v e l. 10 354 100 72 100 273 89 50 1000 385 222 56 341 56 10000 2787 IdentityHashMap ---------v e l. put get i t e r a t e 144 101 10 290 287 100 204 132 1000 508 336 77 266 56 10000 767 WeakHashMap vel . put get i t e r a t e 10 484 146 151 117 100 292 126 1000 411 136 152 10000 138 555 2165 ----- Hashtable - put get i t e r a t e v e l. 264 113 10 113 100 181 105 76 201 80 1000 260 134 77 10000 1245 *///:-

700

Misliti na Javi

U svim realizacijama interfejsa Map, sem u IdentityHashMap, s porastom Mape um etanje postaje znatno sporije. M eutim , pretraivanje je po pravilu m nogo bre od um etanja, to je dobro zato to se stavke obino m nogo ee trae nego to se umeu. Performanse mape Hashtable priblino su jednake onim a m ape HashMap. Poto je HashMap predviena da zameni Hashtable, te zato u pozadini upotrebljava isti m ehanizam za skladitenje i pretraivanje (o kojem em o kasnije govoriti), ovo nas ne udi. K ontejner TreeMap po pravilu je sporiji od kontejnera HashMap. Kao i TreeSet, TreeMap om oguuje pravljenje ureene liste. Stablo (engl. tree) je uvek ureeno, pa eksplicitno sortiranje nije neophodno. Kada pop un ite TreeMap, m oete pozvati m etodu k eyS et( ) koja e dati skup (Set) kljueva, a zatim m etodu toA rray( ) koja e dati njihov niz. Zatim moete pozvati statinu m etod u Arrays.binarySearch( ) koja brzo pronalazi objekte u tom ureenom nizu. Naravno, to im a smisla sam o ako je ponaanje kontejnera HashMap neprihvatljivo, poto ba HashMap brzo pronalazi kljueve. Sem toga, HashMap ete lako napraviti od kontejnera TreeMap - treba da napravite sam o jedan objekat ili da pozovete m etodu p u tA ll( ). Najzad, ako vam treba m apa, trebalo bi da upotrebfjavate HashMap; TreeMap koristite sam o ukoliko vam treba stalno ureena mapa. Za um etanja je LinkedHashMap sporiji od m ape HashMap, zato to pored strukture podataka s transform isanim kljuevima odrava i ulananu listu (da bi ouvao redosled um etanja). Ali zbog te liste bra je iteracija. Performanse kontejnera IdentityHashM ap razlikuju se zato to on za poreenja upotrebljava ==, a ne m etodu eq u a ls( ). WeakHashMap je opisana u nastavku poglavlja. Veba 35: (1) Izmenite program PerformanseMapa.java tako da obuhvati ispitivanja kontejnera SporaMapa. Veba 36: (5) Izmenite kontejner SporaMapa tako da um esto dve ArrayListe ima jedan kontejner ArrayList iji su objekti tipa StavkaMape. Dokaite da izm enjena verzija radi ispravno. Ispitajte brzinu nove m ape program om PerformanseMapa.java. Potom iznienite m etodu p u t( ) tako da nakon unoenja svakog para poziva s o r t( ), a izm enite i metodu g e t ( ) tako da za pronalaenje kljua koristi ColIections.binarySearch( ). Uporedite perform anse nove verzije i starih verzija. V r eba37: (2) Izmenite kontejner JednostavnaHashMapa tako da upotrebljava ArrayListe um esto LinkedLista. Izmenite program PerformanseMapa.java tako da poredi per form anse te dve realizacije.

Od ega zavise performanse kontejnera H ashM ap


Kontejner tipa HashMap moe se runo podesiti tako da ima bolje perform anse u konkretnoj aplikaciji. M orate poznavati sledeu term inologiju da biste shvatili od ega zavise perform anse prilikom podeavanja kontejnera HashMap:
K a pa citet. Broj kofa u tabeli. P oetni ka p a c ite t. Broj kofa nakon pravljenja tabele. HashMap i HashSet imaju kon struktore koji onioguuju zadavanje poetnog kapaciteta. V eliina: Broj stavki tren u tn o u tabeli.

Poglavlje 17: Detaljno razmatranje kontejnera

701

F a k to r optereenja: veliina/kapacitet. Faktor optereenja od 0 im a prazna tabela, 0,5 napola pu n a tabela itd. Malo popunjena tabela nem a m nogo sudara i zato je optim alna za um etanje i pretraivanje (ali e usporiti postupak prolaska kroz kontejner iteratorom ). HashMap i HashSet im aju konstruktore koji om oguuju zadavanje faktora optereenja, pa kada se ovaj faktor optereenja dostigne, kontejner e autom atski priblino dvaput poveati kapacitet (broj kofa) i preraspodelie postojee objekte u novi skup kofa (to se naziva ponovno heiranje, tj. ponovno transform isanje kljueva).

Podrazum evani faktor optereenja kontejnera HashMap iznosi 0,75 (ponovno transfo rm isa n je kljueva kontejncra pokree se tek kada se p opuni tri etvrtine tabele). Izgleda da je to d o bar kom prom is izm eu utroenog vrem ena i prostora. Vei faktor optereenja sm anjuje prostor potreban za tabelu ali poveava trokove pretraivanja, to je vano poto je pretraivanje - zajedno s g e t ( ) i p u t ( ) - najea operacija. Ukoliko znate da ete u kontejneru tipa HashMap skladititi m nogo stavki, napravite ga s dovoljno velikim poetnim kapacitetom da biste spreili dodatne trokove zbog autom atskog ponovnog transform isanja kljueva.1 1 Veba 38: (3) Potraite klasu HashMap u IDK dokum entaciji. Napravite kontejner tipa HashMap, popunite ga elem entim a i izraunajte faktor optereenja. Ispitajte brzinu pretraivanja ove mape, zatim pokuajte da poveate brzinu tako to ete napraviti nov kontejner tipa HashMap veeg poetnog kapaciteta i kopirati stare m ape u novu, a potom ponovo pokrenite svoje ispitivanje brzine nove mape. Veba 39: (6) Klasi JednostavnaHashMapa dodajte privatnu m etodu rehash( ) koja se poziva kada faktor optereenja premai 0,75. Tokom ponovnog transform isanja kljueva ud vostruite broi kofa, a zatim pronadite prvi vei prim t>roj i neka to bude novi broj kofa.

Uslune metode
Za kontcjnere postoji vie sam ostalnih uslunih m etoda, napisanih u obliku statinih metoda klase java.util.Collections. Ve ste u po /n ali neke od njih, kao to su ad d A ll(), reverscO rder( ) i binarySearch( ). I sledeoj tabeli navodim o ostale (sinhronizovane i neprom enljive uslune m etode bie opisane u narednim odeljcima). U tabeli su generiki tipovi upotrebljeni sam o tam o gde su relevantni:

Jo slu ia B loch m i jc p isao : S m a tr a m d a s m o p o g re ili k a d a s m o d o z v o lili d a p o je d in o s ti realizacije (k a o to su vcliin a hc ta b c lc i ta k to r o p tc re c c n ja ) u u u n a se in te rfc js e za p ro g r a m ira n je ap lik acija. K lijcn t hi m o d a trc h a lo d a n a m s a o p ti m a k s im a ln u o c k iv a n u v e li in u k o n tc jn e ra , a m i bi tre b a lo d a p re u z m e m o d alji p o sao . K lijen ti c la k o n a n c ti vi.e tc tc n e g o k o ris ti u k o lik o s a m i b ira ju v rc d n o s ti za o ve p a ra m e trc . R a z m o tr im o p a r a m c ta r c a p a c ity ln c r c m e n t klasc V e c to r k a o k ra jn ji sluaj. To n ik o n ik a d a n c hi trc b a lo d a z ad a je , a m i sm o p o g re ili to s m o to z a d a v a n jc o m o g u ili. U k o lik o m u z a d a tc h ilo k o ju v re d n o s t ra z li itu o d n u lc , lin c a rn i a s im p to ts k i tro k o v i sek v e n c c d o d a v a n ja p o s ta ju kvad r a tn i. D ru g im re im a , u n i ta v a ju se p e rfo r m a n s e . S v rc m e n o m s m o se o p a m e tili u to m p o g le d u . K o n tc jn e r I d e n tity H a s h M a p , u o p tc n c m a p a r a m c tr e za p o d e a v a n je n isk o g n ivoa.

702

Misliti na Javi

checkedCollection( Kolekcija<T>, Class<T> tip) checkedList( List<T>, Class<T> tip) checkedM ap(M ap<K,V>, Class<K> keyType, Class<V> valueType) checkedSet(Set<T>, Class<T> tip) checkedSortedM ap ( SortedMap<K,V>, Class<K> keyType, Class<V> valueType) checkedSortedSet( SortedSet<T>, Class<T> tip) max(Kolekcija) m in(Kolekcija) m ax(Kolekcija, Komparator) m in(Kolekcija, Komparator) indexOfSubList(List izvor. List odredite) lastlndexO fSubList(List izvor. List odredite) replaceAII(List<T>, T staraVrednost, T novaVrednost) reverse(List) reverseOrder( ) reverseOrder( Komparator<T>) rotate(List, int udaljenost) shuffle(List) shufflefList, Random) sort(List<T>) sort(List<T>, Kom parator<? super T> c) copy(List<? super T> odredite. List<? extends T> izvor) swap(List, int i, int j) fill(List<? super T>, T x)

Proizvode dinamiki bezbednu (u pogledu tipova) realizaciju Kolekcije ili nekog njenog konkretnog podtipa. Koristite ih kada nije mogue upotrebiti statiki proverenu verziju.

U poglavlju Generiki tipovi ove metode su bile navedene pod naslovom Dinamika bezbednost tipova".

Daje najvei ili najmanji element argumenta koristei metodu prirodnog poredenja objekata u Kolekciji. Daje najvei ili najmanji element Kolekcije koristei Komparator

Proizvodi poetni indeks prvog mesta na kojem se


odredite pojavljuje unutar izvora ili -1 ako takvog mesta

nema. Proizvodi poetni indeks poslednjeg mesta na kojem se


odredite pojavljuje unutar izvora ili -1 ako takvog mesta

nema. Zamenjuje sve primerke objekta staraVrednost objektom


novaVrednost

Obre redosied svih eiemenata liste. Vraa Kom parator koji obre prirodni poredak kolekcije objekata koja realizuje interfejs Comparable<T> Druga verzija obre redosled datog Kom paratora Premeta sve elemente unapred za udaljenost; one s kraja vraa na poetak. Nasumino permutuje datu listu. Prvi obiik ima sopstveni izvor siuajnih brojeva. dok ga u drugom obliku sami zadajete. Ureduje listu List<T> po njeni>m prirodnom redosledu. U drugom obliku sami zadajete Kom parator za uredivanje Kopira elem ente iz izvora u odredite Zamenjuje elemente na mestima i i j u listi List Veiovatno bra od one koju moete sami da napiete. Zamenjuje sve elemente liste objektom x

Poglavfje 17: Detal)no razmatranje kontejnera

703

nCopies{int n, T x) disjoint(Kolekcija, Kolekcija) frequency(Kolekcija, Object x) emptyList( ) emptyMap( ) emptySet( ) singletonfT x) singletonList(T x) singletonMap(K kljuc, V vrednost) list(Enumeration<T> e)

Vraa nepromenljivu listu List<T> veliine n ije sve reference upuuju na objekat x Vraa true ako date kolekcije nemaju zajednikih elemenata. Vraa broj elemenata Kolekcijejednakih objektu x. Vraa nepromenljivu praznu listu, mapu ili skup Oni su generiki, pa e rezultujua Kolekcija biti parametrizovana u eljeni tip. Vraa nepromenljivu listu, mapu ili skup (Set<T>, List<T> odnosno Map<K,V>) sa samojednom stavkom, napravljenom na osnovu datih argumenata. Proizvodi ArrayList<T> koja sadri elemente u poretku kojim ih vraa (stari) Enumeration (prethodnik Iteratora). Za konvertovanje starog koda. Proizvodi stari Enumeration<T> za dati argument.

enumeration(Kolekcija <T>)

Im ajte u vidu da m i n ( ) i m a x ( ) rade sa objektim a tipa C ollection, ne sa Listam a, pa ne m orate b rin uti da li je data kolekcija ureena ili nije. Ve sm o spom enuli da za Listu ili niz treba da pozovete m etodu s o r t ( ) pre poziva m etode b in a ry S e a rc h (). U narednom prim eru prikazaemo osnove korienja uslunih m etoda iz gornje tabele:
//: kontejneri/UsluzneMetode.java primeri usiunih metoda klase Collections.

// Jednostavni

import ja v a . u t i 1 .*; import static net.mindview.uti 1 .P r i n t .* ; public class Us l uzneMetode { static List<String> lista = Arrays.asList( "jedan Dva tri Cetiri pet sest j e da n" .s plit (" ")); public static void main(String[] args) p r i n t (1is t a ) ; p r i n t ( " 11 ista' disjoint (Cetiri)?: " + Coll e c t i o n s .di sjoi n t (1i sta, Col 1e c t i o n s .si n g l e to nL is t("Ceti r i "))) ; print("mks: print("min: " + C o l 1ections.max(lis t a ) ) ; " + Collections.min(lista)); " + C o l1ec ti ons.max(lis t a , " + Col 1 e c ti on s. mi n(1 ista, {

print("mks uz komparator: print("min uz komparator: List<String> podlista =

Stri n g .C A S E I N S E N S ITI V E O R D E R ) ); Stri ng .C ASE_INSENSITIVE_ORDER));

A r r a y s . a s L i s t ( " C e t i r i pet s e s t " . s p l i t ( " ) ) ; p r i n t ( in d e ksP o d liste : " +


Collec ti on s. in de ks Pod liste(lista, po d l ista )); p r i n t ( "p os le dn ji IndeksPodliste: " + Collections.poslednj iIndeksPodli s t e (1i sta, podli s t a ) );

704

Misliti na Javi

C o lle c t io n s . r e p la c e A ll(1i s t a , " je d a n ", "D a"); p rin t("re p la c e A U : " + l i s t a ) ; C o l1e c t i o n s . r e v e r s e ( l i s t a ) ; p rin t("o b rn u to : " + li s t a ) ; C o lle c tio n s .r o ta te ( lis ta , 3); p rin t("ro tira n o : " + lis t a ) ; L is t< S trin g > iz v o r = A rra y s .a s L is t("u m a t r i c i " . s p l i t ( " " ) ) ; C o lle c tio n s .c o p y (lis ta , iz v o r ); p rin t("k o p ija : " + lis t a ) ; C o l l e c t i o n s . s w a p ( l i s t a , 0, l i s t a . s i z e ( ) - 1 ); p rintC 'z a m e na : " + l i s t a ) ; C o l l e c t i o n s . s h u f f l e ( l i s t a , new Random(47)); p rin t("is p re tu ra n o : " + l i s t a ) ; C o ll e c t i o n s . f i 11(1 i s t a , " p o p " ) ; print("popuna: " + l i s t a ) ; p r i n t ( " b r o j primeraka r e i ' p o p ' : " +
C o l 1ections. fr eq ue nc y(1ista, p o p " ) ); List<String> duplikati = Collections.nCopies(3, "snap"); pr in t( "d up li ka ti: " + duplikati);

p r i n t ( " ' 1i s t a ' d i s j o i n t

' d u p l i k a t i '? :

" +

Collections.disjoint(lista, d u p l i k at i) );

/ / P r a v l j e n j e s ta ro g i t e r a t o r a E num era tio n: Enum era tio n<String> e = C o l1e c t i o n s . e n u m e r a t i o n ( d u p l i k a t i ) ; V e c t o r < S t rin g > v = new V e c t o r < S t r i n g > ( ) ; w h i1e(e.hasMoreElements ( ) ) v . a d d E lement( e . n e x t E l ement( ) ) ;
// Pretvaranje starog Vectora u objekat // tipa List pomou iteratora Enumeration: ArrayList<String> arrayList = Col lections.lista(v.elements()); print("arrayList: " + arrayList);

}
} /* Ispis: [jedan, Dva, t r i , Cetiri, pet, sest, jedan] 'lista' disjoint (Cetiri)?: mks: tri min: etiri mks uz komparator: Dva min uz komparator: pet i n d e ks Po dl is te: 3 poslednjilndeksPodliste: replaceAll: obrnuto: rotirano: kopija: zamena: 3 [Da, Dva, tri, Cetiri, pet, sest, Da] false

[Da, sest, pet, Cetiri, t r i , Dva, Da] [tri, Dva, Da, Da, sest, pet, Cetiri] [u, matrici, Da, sest, pet, Cetiri] [Cetiri, matrici, Da, sest, pet, u] [sest, matrici, Cetiri, Da, pet, u]

ispreturano:

Poglavlje 17: Detaljno razmatranje kontejnera

705

popuna: [pop, pop, pop, pop, pop, pop, pop] b r o j priraeraka r e i ' p o p ' : 7 d u p l i k a t i : [snap, snap, snap] ' l i s t a ' d i s j o i n t d u p l i k a t i '? : tru e a r r a y L i s t : [snap, snap, snap]

* ///= Ponaanje svake uslune m etode vidi se iz ispisa rezultata. O bratite panju na razliku izm eu rezultata m e to d a m in () im a x ( ) uzString.CASE_INSENSITIVE_ORDERComparator; razlog je zanem arivanje razlike izm eu velikih i malih slova.

Ureivanje i pretraivanje lista (realizacija interfejsa List)


Uslune m etode za sortiranje i pretraivanje lista im aju ista im ena i potpise kao one za ureivanje nizova objekata, ali to su statine m etode klase C ollections, a ne klase Arrays. Evo prim era u kojem se koriste m etode za liste iz paketa Utilities.java:
/ / : k o n te jn e ri/L is tS o rtS e a rc h .ja v a / / U r e iv a n je i p r e t r a i v a n j e l i s t a us lu n im metodama k la s e Col l e c t i o n s . im p o rt j a v a . u t i l im p o r t s t a t i c n e t . m i n d v i e w . u t i l . P r i n t . * ; p u b l i c c la s s L is t S o r t S e a r c h { p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] a rg s ) { L is t< S trin g > l i s t a = new A r r a y L i s t < S t r i n g > ( U t i 1i t i e s . 1 i s t a ) ; 1i s t a . a d d A l 1 ( U t i 1i t i e s . 1i s t a ) ; p rin t(lis ta ); C o l l e c t i o n s . s h u f f l e ( l i s t a , new Random(47)); p rin t("Is p re tu ra n a : " + lis t a ) ; / / U k la n ja n je p o s l e d n j i h elemenata pomou L i s t l t e r a t o r a : L i s t I t e r a t o r < S t r i n g > i t = 1i s t a . 1i s t l t e r a t o r ( l O ) ; w h i l e ( i t . hasN ext( ) ) { it.n e x t(); it.re m o v e ();

}
p rint("S kraena: " + l i s t a ) ; C o lle c tio n s .s o rt(1i s t a ) ; print("U re e n a : " + l i s t a ) ; S trin g k lju c = 1i s t a . g e t (7 ); i n t indeks = C o l1e c t i o n s . b i n a r y S e a r c h ( l i s t a , k l j u c ) ; p r i n t ( " M e s t o k l j u a " + k l j u c + " j e " + indeks + " , l i s t a . g e t ( " + indeks + " ) = " + 1 i s t a . g e t ( i n d e k s ) ) ; C o l l e c t i o n s . s o r t ( l i s t a , String.CASE_INSENSITIVE_ORDER); p r i n t ( " U r e e n a bez o b z ir a na v e l i k a i mala s lo v a : " + l i s t a ) ; k l j u c = 1i s t a . g e t ( 7) ; indeks = C o l l e c t i o n s . b i n a r y S e a r c h ( l i s t a , k l j u c , String.CASE INSENSITIVE ORDER);

706

Misliti na Javi

p r i n t ( " M e s t o k l j u a " + k l j u c + " i s " + indeks + " , 1i s t a . g e t ( " + indeks + " ) = " + 1i s t a . g e t ( i n d e k s ) ) ;

}
} / * Is p is : [je d a n , Dva, t r i , C e t i r i , p e t , s e s t , je d a n , je d a n , Dva, t r i , C e t i r i , pet, s e s t , je d a n ] I s p r e t u r a n a : [ C e t i r i , p e t , je d a n , je d a n , Dva, s e s t , s e s t , t r i , t r i , p e t , C e t i r i , Dva, je d a n , je d a n ] Skraena: [ C e t i r i , p e t , je d a n , je d a n , Dva, s e s t , s e s t , t r i , t r i , p e t] Ureena: [ C e t i r i , Dva, je d a n , je d a n , p e t , p e t , s e s t , s e s t , t r i , t r i ] Mesto k l j u a s e st j e 7, 1i s t a . g e t (7) = sest Ureena bez o b z ir a na v e l i k a i mala s lo v a : [ C e t i r i , Dva, je d a n , je d a n , p e t , p e t , s e s t , s e s t , t r i , t r i ] Mesto k l j u a t r i j e 7, l i s t a . g e t ( 7 ) = t r i

* ///:Kao pri pretraivanju i ureivanju nizova, i ovde vai sledee: ukoliko listu uredite pom ou nekog kom paratora, pom ou istog kom paratora m orate obaviti i b inarno pretraivanje - m etodom b in a ry S ea rc h (). U ovom program u prikazana je i m etoda sh u ffle() klase C ollections koja nasum ino m enja redosled elemenata liste. Jedan L istlterator je napravljen na odreenom mestu ispreturane (engl. shuffle) liste i upotrebljen za uklanjanje elemenata od tog mesta do kraja liste. Veba 40: (5) Napravite klasu s dva objekta tipa S tring i realizujte u njoj interfejs Com p arab le tako da poreenje uzima u obzir sam o prvi objekat tipa String. Popunite jedan niz i jedan objekat tipa A rrayList objektim a vae klase, koristei generator RandoinG enerator. Pokaite da se sortiranje obavlja ispravno. Potom napravite C o m p arato r koji uzim a u obzir samo drugi objekat tipa S trin g i pokaite da je sortiranje opet ispravno. Obavite i binarno pretraivanje pom ou svoje realizacije interfejsa C om parator. V eba41: (3) Izmenite klasu iz prethodne vebe tako da radi s kontejnerim a tipa H ashSet i kao klju u kontejnerim a tipa H ashM ap. V eba42: (2) Izmenite vebu 40 tako da lista bude sortirana abecedno.

Kako kolekciju ili mapu uiniti nepromenljivom


esto je pogodno imati verziju kolekcije ili m ape samo za itanje. Klasa C ollections om oguuje da to uradite prosledivanjem originalnog kontejnera m etodi koja vraa njegovu verziju samo za itanje. Ta m etoda ima vie varijanata: za kolekcije (ako datu kolekciju ne moete tretirati kao konkretniji tip), liste, skupove i mape. U sledeem prim eru prikazan je pravilan nain pravljenja verzije sam o za itanje svakog od nabrojanih vrsta kontejnera:
/ / : k o n t e j n e r i /S a m o Za C it a n je .ja v a / / Pomou metoda C o l1e c t i o n s . u n m o d i f i a b l e. im p o rt j a v a . u t i l . * ; im p o rt n e t . m i n d v i e w . u t i l . * ;

Poglavlje 17: Detafjno razmatranje kontejnera

707

im p o r t s t a t i c n e t . m i n d v i e w . u t i l . P r i n t . * ; p u b l i c c la s s SamoZaCitanje { s t a t i c C o l l e c t i o n < S t r i n g > podaci = new A r r a y L i s t < S t r i n g > { C o u n t r i e s . i m e n a ( 6 ) ) ; p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] a rg s) { C o lle c tio n < S trin g > c = C o lle c tio n s .u n m o d ifia b le C o lle c tio n ( new A r r a y L i s t < S t r i n g > ( p o d a c i ) ) ; p r i n t ( c ) ; / / ita n je fu n k c io n i e / / ! c . a d d ( " j e d a n " ) ; / / Nema menjanja L is t< S trin g > a = C o lle c tio n s .u n m o d ifia b le L is t( new A r r a y L i s t < S t r i n g > ( p o d a c i ) ) ; L is tIte ra to r< S trin g > l i t = a . 1 i s t I t e r a t o r ( ) ; p r i n t ( l i t . n e x t ( ) ) ; / / ita n je fu n k c io n i e / / ! l i t . a d d ( " j e d a n " ) ; / / Nema menjanja S e t < S t r in g > s = C o l l e c t i o n s . u n m o d i f i a b l e S e t ( new H a s h S e t < S t r in g > ( p o d a c i) ) ; p r i n t ( s ) ; / / i t a n j e f u n k c io n i e / / ! s . a d d ( " j e d a n " ) ; / / Nema menjanja / / Za S o rte dS e t: S e t < S t r in g > ss = C o l l e c t i o n s . u n m o d i f i a b l e S o r t e d S e t ( new T re e S e t < S t r in g > ( p o d a c i) ) ; M a p < S t r in g , S t r in g > m = C o l1e c t i o n s . unmodifia ble Map( new HashMap<Strin g . S t r i n g > ( C o u n t r i e s . g l avni g r a d o v i ( 6 ) ) ) ; p r i n t ( m ) ; / / i t a n j e f u n k c io n i e / / ! m .p u t("R a lf" , "Z d ra vo!"); / / Za SortedMap: M a p < S t r in g , S t r in g > sm = C o l1e c t i ons.unmodi f ia b le S o r te d M a p ( new T r e e M a p < S t r in g . S t r in g > ( C o u n t r ie s . g l a v n i _ g r a d o v i ( 6 ) ) ) ;

}
} / * Is p is : [ALGERIA, ANGOLA, BENIN, BOTSWANA, BURKINA FASO, BURUNDI] ALGERIA [BURKINA FASO, BURUNDI, BOTSWANA, BENIN, ANGOLA, ALGERIA] {BURKINA FASO=Ouagadougou, BURUNDI=Bujumbura, BOTSWANA=Gaberone, BENIN=Porto-Novo, ANGOLA=Luanda, ALGERIA=Algiers}

* ///:Poziv neprom enljivc m etode za odreeni tip ne prouzrokuje proveru u vreme prevodenja, ali nakon to se ta transform acija obavi, poziv bilo koje m etode koja menja sadraj tog kontejnera prouzrokuje U n su pp orted O peratio n E x cep tio n .

708

Misliti na Javi

U svakom sluaju, kontejner m orate p o p u n iti sm islenim podacim a pre nego to ga pretvorite u verziju samo za itanje. N akon uitavanja, najbolje je zam eniti postojeu referencu kontejnera referencom koju proizvodi poziv ,,unm odifiable m etode. Time izbegavate rizik od nehotinog pokuaja m enjanja sadraja nakon to ste ga uinili neprom enljivim . S druge strane, ova alatka om oguuje i da prom enljivi kontejner zadrite u obliku privatnog lana odreene klase i da iz poziva neke m etode vratite referencu tog kontejnera sam o za itanje. Dakle, kontejner m oete izm eniti iznutra iz klase, a svi drugi m ogu sam o da ga itaju.

Sinhronizovanje kolekcije ili mape


Rezervisana re synchronized ini vaan deo vienitnog izvravanja, ali je ona toliko kom plikovana da je neem o nainjati do poglavlja Paralelno izvravanje. Ovde navodimo sam o to da klasa Collections m oe autom atski da sinhronizuje ceo kontejner. Sintaksa je slina ,,unm odifiable m etodam a:
/ / : k o n te jre ri/S in h ro n iz a c ija .ja v a / / Upotreba C o l l e c t i o n s . s y n c h r o n i z e d metoda. im p o rt j a v a . u t i l p u b l i c c la s s S i n h r o n i z a c i j a { p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] a rg s ) { C o lle c tio n < S trin g > c = C o l1e c t i o n s. s ynchro ni zedCol 1e c t i o n ( new A r r a y L i s t < S t r i n g > ( ) ) ; L i s t < S t r i n g > l i s t a = C o l1e c t i o n s . s y n c h r o n i z e d L i s t ( new A r r a y L i s t < S t r i n g > ( ) ) ; S e t < S t r in g > s = C o l1e c t i o n s . s y n c h r o n iz e d S e t ( new H a s h S e t < S t r in g > ( ) ) ; S e t < S t r in g > ss = C o l1e c t i o n s . s y n c h ro n iz e d S o r te d S e t( new T r e e S e t < S t r i n g > ( ) ) ; M a p < S t r in g , S t r in g > m = C o l1e ctio n s .s y n c h r o n iz e d M a p ( new H a s h M a p < S t r in g , S t r in g > ( ) ) ; M a p < S t r in g , S t r in g > sm = C o l1e c t io n s . s y n c h r o n i zedSortedMap( new T r e e M a p < S t r in g , S t r i n g > ( ) ) ;

} } ///:Najbolje je novi kontejner odm ah proslediti preko odgovarajue ,,synchronized metode, kao u gornjem prim eru. Tako se ne m oe dogoiti da sluajno eksponirate nesinhronizovanu verziju.

Brzo otkazivanje
Javini kontejneri imaju i m ehanizam koji spreava da vie procesa istovremeno menja sadraj kontejnera. Problem nastaje ako ste usred iteriranja kroz kontejner, a neki drugi proces uskoi i um etne, ukloni ili izmeni neki objekat u tom kontejneru. Moda ste taj ele-

Poglavfje 17: Detaljno razmatranje kontejnera

709

m ent kontejnera ve proli, m oda je on ispred vas, m oda e se kontejner sm anjiti nakon to pozovete m etodu s iz e ( ) - scenarija za katastrofu im a koliko hoete. Biblioteka Java kontejnera upotrebljava m ehanizam brzog otkazivanja (engl. fail-fast); u kontejneru trai izm ene koje nije prouzrokovao va proces lino. Ako otkrije da neko drugi m enja sadraj kontejnera, odm ah generie izuzetak ConcurrentModificationException. Taj aspekat se naziva brzo otkazivanje - problem se ne trai naknadno, nekim sloenijim algoritm om . M ehanizam brzog otkazivanja je sasvim lako videti u praksi - dovoljno je napraviti iterato r i zatim neto dodati kolekciji na koju iterator pokazuje:
/ / : ko n te jn e ri/B rz o O tk a z iv a n je .ja v a / / Pokazuje ponaanje nazvano " b rz o o t k a z i v a n j e " . im p o r t j a v a . u t i l . * ; p u b l i c c la s s B r z o O tk a z iv a n je { p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] a rg s) { C o l l e c t i o n < S t r i n g > c = new A r r a y L i s t < S t r i n g > ( ) ; Ite ra to r< S trin g > i t = c . i t e r a t o r ( ) ; c.add("N eki o b je k a t" ) ; try { S trin g s = i t . n e x t ( ) ; } c a t c h ( C o n c u r r e n t M o d if ic a t io n E x c e p t io n e) { S y s te m .o u t.p rin tln (e );

} }
} / * Is p is : j a v a . u t i 1 .Concu rrentModi f i c a t i o n E x c e p t i o n

* ///:Izuzetak se ogodio zato to je neto sm eteno u kontejner nakon pribavljanja iteratora od tog kontejnera. M ogunost da dva dela program a m enjaju isti kontejner proizvodi neodreeno stanje, pa vas izuzetak obavetava kako bi trebalo da izmenite kod u ovom sluaju, pribavite iterator tek nakon dodavanja svih elem enata u kontejner. K ontejneri C oncu rren tH ash M ap , C opyO nW riteA rrayList i C opyO nW riteA rraySet koriste tehnike kojim a se izbegava generisanje izuzetka C o n c u rre n tM odificationExceptions.

uvanje referenci
Biblioteka java.lan g .ref sadri skup klasa koje om oguuju veu fleksibilnost u sakupljanju smea. Te klase su naroito korisne kada radite s velikim objektim a koji m ogu iscrpeti m em oriju. O d apstraktne klase Reference nasledene su tri klase: SoftReference, W eakR eference i P hantom R eference. Svaka od njih sakupljau smea prua drugaiji nivo indirekcije ukoliko je objekat o kojem se radi dostian sam o preko jednog od tih Reference objekata. Ako je objekat d ostia n , m oe se pronai negde u program u. To m oe znaiti da im ate obinu referencu steka koja upuuje pravo na objekat, ali mogli biste im ati i referencu objekta koji sadri referencu objekta o kojem se radi; tih meuveza m oe biti mnogo.

710

Misliti na Javi

Ukoliko je objekat dostian, sakuplja smea ga ne m oe osloboditi jer ga program jo uvek koristi. Ako objekat nije dostian, ne postoji nain da ga program upotrebi, pa je bezbedno sakupiti ga u smee. Objekte tipa Reference koristite kada i dalje hoete da uvate referencu odreenog objekta - taj objekat hoete da dosegnete - ali takoe hoete da om oguite sakupljau smea da taj objekat oslobodi. Dakle, postoji nain da objekat koristite, ali ako se ukae mogunost da se m em orija iscrpe, dozvoljavate da taj objekat bude osloboen. To se postie korienjem objekta tipa Reference kao posrednika (engl. p roxy ) izmeu vas i obine reference. Sem toga, taj objekat ne sme im ati obine reference (one koje nisu om otane u Reference objekte). Ako sakuplja smea otkrije da je odreeni objekat dostian preko obine reference, taj objekat nee biti osloboen. Reference tipa SoftReference, VVeakReference i PhantomReference sve su ,,slabije i odgovaraju razliitim nivoima dostinosti. Meke reference (engl. soft references) slue za realizovanje kea, tj. ostava koje uvaju m em oriju. Slabe reference (engl. weak references) slue za realizovanje kanonizujuih m apiranja (preslikavanja)" - gde se instance objekata m ogu istovrem eno upotrebljavati na vie mesta u program u, da bi se utedela m em orija to ne spreava da njihovi kljuevi (ili vrednosti) budu sakupljeni u smee. Fantomske reference (engl. p h a n to m references) slue za planiranje operacija ienja pre (memorijske) sm rti na fleksibilniji nain nego to bi to bilo mogue s Javinim m ehanizm om finalizacije. Reference tipa SoftReference i WeakReference m oete ali i ne m orate smestiti u ,,red referenci za ekanje (ReferenceQueue) - tako je nazvan uredaj za akcije ienja ,,pre (m em orijske) sm rti) - dok se PhantomReference m oe napraviti sam o u tom ReferenceQueue. Evo jednostavnog prim era:
/ / : ko n te jn e ri/R e fe re n c e .ja v a / / Prim er o b je k a ta t i p a Reference im p o r t j a v a . l a n g . r e f im p o r t j a v a . u t i l . * ; c la s s VeomaVeliki { p r i v a t e s t a t i c f i n a l i n t VELICINA = 10000; p r i v a t e long'[] l a = new 1ong[V ELICINA]; p r iv a te S trin g id e n t; p u b l i c V e o m a V e lik i( S t r in g i d ) { id e n t = i d ; } p u b lic S trin g to S tr in g O { re tu rn id e n t; } p r o t e c t e d v o id f i n a l i z e ( ) { S y s t e m . o u t . p r i n t l n ( " F i n a l i zo va n je " + i d e n t ) ;

} }
p u b l i c c la s s Reference { p r i v a t e s t a t i c ReferenceQueue<VeomaVeli k i> rq = new ReferenceQueue<VeomaVeli k i > ( ) ; p u b l i c s t a t i c v o id checkQueue() { Reference<? extends VeomaVeliki> urd = r q . p o l l ( ) ; i f ( u r d != n u l l )

Poglavlje 17: Detaljno razmatranje kontejnera

711

S y s t e m . o u t . p r i n t l n ( " U n u t a r reda:

" + u rd .g e t());

}
p u b l i c s t a t i c vo id m a i n ( S t r i n g [ ] a rg s ) { i n t v e l i c i n a = 10; / / I l i i z a b e r i t e v e l i i n u sa komandne l i n i j e : i f ( a r g s . l e n g t h > 0) v e l i c i n a = new I n t e g e r ( a r g s ) ; L in k e d l_ is t< S o ftR e fe re n c e < V e o m a V e 1 ik i sa = new L in kedLi st<SoftReference<VeomaVel i k i ( ) ; f o r ( i n t i = 0; i < v e l i c i n a ; i+ + ) { s a . add(new SoftReference<VeomaVeli k i >( new V e o m a V e lik i( " S o f t " + i ) , r q ) ) ; System .out . p r i n t l n ("Tek n a p r a v lje n o : " + s a . g e t L a s t O ) ; checkQueue();

}
L in k e d List< W e a kR e fe re n c e < V e o m a V e liki wa = new L in k e d L is t< W e a k R e f e re n c e < V e o m a V e lik i () ; f o r ( i r i t i = 0 ; i < v e l i c i n a ; i+ + ) { wa.add(new WeakReference<VeomaVeli k i > ( new VeomaVeliki("Weak " + i ) , r q ) ) ; S y s t e m . o u t . p r i n t l n ("Tek n a p r a v lje n o : " + w a . g e t L a s t ( ) ) ; c heckQueue();

}
SoftReference<VeomaVeli k i> s = new SoftR efe re nce<VeomaVelik i>(new VeomaVeli k i ( " S o f t " ) ) ; WeakReference<VeomaVeliki> w = new WeakReference<VeomaVeliki>(new V e o m a V e lik i( " W e a k " ) ) ; S yste m .g c(); Li nkedLi st<PhantomReference<VeomaVel i k i pa = new Li nkedLi st<PhantomReference<VeomaVel i k i ( ) ; f o r ( i n t i = 0; i < v e l i c i n a ; i+ +) { pa.add(new PhantomReference<VeomaVeli k i > ( new VeomaVeliki("Phantom " + i ) , r q ) ) ; S y s t e m . o u t . p r i n t l n ( " T e k n a p r a v lje n o : " + p a . g e t L a s t ( ) ) ; checkQ ueue();

} }
} /* ( P o k r e n it e da b i s t e v i d e l i re z u lta te ) * / / / : -

Kada pokrenete ovaj program (preusm erite njegov izlaz u tekstualnu datoteku da biste rezultate mogli da gledate stranicu po stranicu), videete da objekti bivaju sakupljani u smee iako im i dalje m oete pristupati preko objekta tipa Reference - referencu objekta pribavljate m etodom g e t ( ). Videete i to da ReferenceQueue uvek proizvodi null objekat tipa Reference. Da bi taj objekat postao upotrebljiv, nasledite odreenu klasu Reference, a novoj klasi dodajte upotrebljivije metode.

712

Misliti na Javi

Kontejner WeakHashMap
Biblioteka kontejnera im a posebnu m apu za skladitenje slabih referenci: to je W eakHashMap. Ta klasa olakava pravljenje kanonizovanih m apiranja (preslikavanja). Takvim m apiranjem tedite m em oriju jer pravite sam o jednu instancu odreene vrednosti. Kada program u zatreba ta vrednost, on potrai postojei objekat u tom m apiranju i upotrebi njega (um esto da ga pravi od nule). M apiranje m oe napraviti vrednosti u sklopu svoje inicijalizacije, ali se vrednosti ee prave tek na zahtev. Poto je ovo tehnika za utedu m em orije, ba je podesno to WeakHashMap om oguuje skupljau smea da autom atski isti njene kljueve i vrednosti. S kljuevima i vrednostim a koje elite da smestite u m apu WeakHashMap ne m orate da radite nita posebno; sam a m apa ih autom atski om otava u objekte tipa WeakReference. O kida koji dozvoljava ienje jeste injenica da se klju vie ne upotrebljava, kao to je prikazano u narednom prim eru:
/ / : k o n t e j n e r i/ K a n o n s k o M a p ir a n j e . ja v a / / Prim e r s kontejnerom WeakHashMap. im p o r t j a v a . u t i l c la s s Element { p r iv a te S trin g id e n t; p u b l i c E le m e n t (S tr in g i d ) { i d e n t = i d ; } p u b l i c S t r ir . g t o S t r i n g ( ) { r e t u r n i d e n t ; } p u b l i c i n t hashCode() { r e t u r n i d e n t .h a s h C o d e ( ) ; } p u b l i c boolean e q u a ls (O b je c t r ) { r e t u r n r i n s t a n c e o f Element && i d e n t . e q u a l s ( ( ( E le m e n t)r ) . i d e n t ) ;

}
p r o t e c t e d v o id f i n a l i z e ( ) { System .out. p r i n t ln ( " F in a liz o v a n je " + g e t C l a s s ( ) .getSimpleName() + " " + i d e n t ) ;

} }
c la s s K l j u c extends Element { p u b lic K lju c (S trin g id ) { s u p e r(id );

}
c la s s Vrednost extends Element { p u b l i c V r e d n o s t ( S t r in g i d ) { s u p e r ( i d ) ;

}
p u b l i c c l a s s KanonskoMapir a n je { p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] i n t v e l i c i n a = 1000;

a rg s )

/ / I l i i z a b e r i t e v e l i i n u na komandnoj l i n i j i : i f ( a r g s . l e n g t h > 0) v e l i c i n a = new l n t e g e r ( a r g s [ 0 ] ) ;

Poglavlje 17: Detaljno razmatranje kontejnera

713

K l j u c [ ] k l j u c e v i = new K l j u c [ v e l i c i n a ] ; WeakHashMap<Kljuc,Vrednost> mapa = new WeakHashMap<Kljuc,Vrednost>(); f o r ( i n t i = 0; i < v e l i c i n a ; i+ + ) { K l j u c k = new K l j u c ( I n t e g e r . t o S t r i n g ( i ) ) ; Vrednost v = new V r e d n o s t ( I n t e g e r . t o S t r i n g ( i ) ) ; i f ( i % 3 == 0) k l j u c e v i [ i ] = k; / / Sauvaj kao " p ra v e " r e f e r e n c e m ap a .p u t(k , v ) ;

)
Syste m .g c();

}
} /* ( P o k r e n it e da b i s t e v i d e l i re z u lta te ) * / / / : -

Klasa K ljuc nrora imati svoje m etode h a s h C o d e () i e q u a ls ( ), poto se upotrebljava kao klju u struktu ri podataka s transform isanjem kljueva. M etoda h a s h C o d e () opisana je u preth o d n o m delu poglavlja. Kada pokrenete program , videete da skuplja smea preskae svaki trei klju, zato to su u niz kljucevi smetene i obine reference tih kljueva, te se ti objekti ne mogu sakupljati kao smee.

Kontejneri Jave 1.0/1.1


Naalost, m nogo koda je napisano uz korienje kontejnera iz Jave 1.0/1.1, a ponekad se ak i nov kod pie uz korienje ovih klasa. Zbog toga m orate znati sve o starim kontejnerim a, m ada nikada ne bi trebalo da ih koristite dok piete nov kod. M eutim , stari kontejneri su bili prilino ogranieni, pa o njim a nem a m nogo toga da se kae. (Poto su oni sad prolost, uzdrau se od ismevanja nekih ishitrenih odluka prim enjenih pri njihovom projektovanju.)

Vector i Enumeration
Jedini niz koji je mogao da se proiruje u Javi 1.0/1.1 bio je Vector,pa se dosta koristio. Njegovi nedostaci su suvie brojni da bi se ovde naveli (potraite prvo izdanje ove knjige na lokaciji www.RruceEckel.coin). U osnovi, to je bio ArrayList s dugim , nezgodnim im enim a metoda. U biblioteci kontejnera nove Jave, Vector je prilagoen da bi mogao da radi i kao kolekcija i kao lista. To je pomalo udno zato to bi neki ljudi mogli pom isliti kako je klasa Vector poboljana, a ona u stvari postoji samo da bi podravala stari Java kod. Verzija iteratora u Javi 1.0/1.1 drugaije se zvala - Enum eration - tj. nije korien termin koji je svima poznat. Interfejs Enumeration je m anji od interfejsa Iterator i sadri sam o dve m etode s dugakim imenima: boolean hasMoreElements( ) koja vraa true ako kontejner sadri jo elemenata, i Object nextElement( ) koja vraa sledei element kolekcije ako on postoji (inae generie izuzetak). Enumeration je samo interfejs bez realizacije, pa ga ak i nove biblioteke ponekad koriste, to nije preporuljivo ali je barem bezopasno. Iako bi prilikom pisanja novih program a uvek trebalo koristiti Iterator, m orate znati da postoje biblioteke koje ele da vam proslede objekat tipa Enumeration.

714

Misliti na Javi

O sim toga, pom ou m etode C o Ile ctio n s.e n u m eratio n () moete da napravite objekat tipa E n u m e ra tio n za bilo koju kolekciju, kao u ovom prim eru:
/ / : k o n te jn e ri/N a b ra ja n je .ja v a / / Java 1 . 0 / 1 . 1 k la s e V e c t o r i Enumeration. im p o r t j a v a . u t i l . * ; im p o rtn e t.m in d v ie w .u til.*; p u b l i c c la s s N a b ra ja n je { p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] a rg s) { V e c t o r < S t r in g > v = new V e c t o r < S t r in g > ( C o u n t r ie s . n a m e s ( lO ) ) ; En u m e ra tio n < Stri ng > e = v . e l e m e n t s ( ) ; w h i1e ( e . hasMoreElements( ) ) S yste m .o u t.p rin t(e .n e x tE le m e n t() + ", " ) ; / / Pra vi Enumeration od k o l e k c i j e : e = C o lle c t io n s . e n u m e r a t io n ( n e w A r r a y L i s t < S t r i n g > ( ) ) ;

}
} / * Is p is : ALGERIA, ANGOLA, BENIN, B0TSWANA, BURKINA FASO, BURUNDI, CAMEROON, CAPE VERDE, CENTRAL AFRICAN REPUBLIC, CHAD

* ///:Da biste napravili objekat tipa Enumeration, pozovite m etodu elem en ts( ), koju potom m oete da upotrebite za prolazak kroz listu. U poslednjem redu pravi se objekat klase ArrayList i koristi m etoda en u m eration ( ) za dobijanje objekta tipa Enumeration na osnovu iteratora klase ArrayList. Dakle, m oete da koristite nove kontejnere i sa starim kodom u kom e se upotrebljava Enumeration.

Klasa Hashtable
Kao to ste videli u delovima ovog poglavlja posveenim uporeivanju perform ansi, klasa Hashtable je veoma slina klasi HashMap, ak su i imena m etoda ista. Zato nem a razloga da se u novim program im a um esto klase HashMap koristi Hashtable.

Klasa Stack
Pojam steka objanjen je ranije, u odeljku o klasi LinkedList. Klasa Stack iz Jave 1 .0 /1 .1 udna je: um esto da koristi klasu Vector i kom poziciju, ona je izvedena (nasleena) iz klase Vector. Zato im a sva obeleja i ponaanje kiase Vector, uz dodatne funkcije steka. Teko je razaznati da li su projektanti sm atrali da je to posebno korisno, ili se radi o neznanju; ipak, jasno je da projekat nije revidiran pre putanja u distribuciju, pa se i tako lo dizajn jo uvek sree (ali ga vi nem ojte koristiti). Evo jednostavnog prim era steka na koji se stavlja svaki red iz niza objekata tipa String. Pokazano je kako je kao stek jednako lako koristiti ulananu Iistu (objekat tipa LinkedList) ili stek napravljen u poglavlju uvcmje objekata:

Poglavfje 17: Detaljno razmatranje kontejnera

715

/ / : k o n t e j n e r i / S t e k o v i . ja v a / / P r i k a z i v a n j e k la s e Stack. im p o r t j a v a . u t i l . * ; im p o r t s t a t i c n e t . m i n d v i e w . u t i l . P r i n t . * ; enum Mesec { JANUAR, FEBRUAR, MART, APRIL, MAJ, JUN, JUL, AVGUST, SEPTEMBAR, OKTOBAR, NOVEMBAR}; p u b l i c c la s s S te ko vi { p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] a rg s) { S ta c k < S tr in g > s t k = new S ta c k < S trin g > ( ) ; for(M esec m : M e s e c .va lu e s O ) s tk .p u s h (m .to S trin g ()); p rin t("s te k = " + s tk ) ; / / Stek kao V e cto r: s tk . a d d E le m e n t ( " P o s le d n ji r e d " ) ; p rin t("e le m e n t 5 = " + s tk .e le m e n tA t(5 )); p r i n t ( " V a d i m e le m e n t e : " ) ; w h i1e ( ! s t k . e m p t y ( ) ) p rin tn b (s tk .p o p () + " " ) ; / / Ulanana l i s t a kao s te k : L i n k e d L i s t < S t r in g > l s t e k = new L i n k e d L i s t < S t r in g > ( ) ; fo r(M esec m : M ese c .va lu e s ( ) ) 1s te k .a d d F i r s t ( m . t o S t r i n g ( ) ) ; p r i n t ( " 1 s te k = " + l s t e k ) ; w h i1e ( ! 1s t e k . i sEmpty( ) ) p r i n t n b (1 s t e k . r e m o v e F ir s t ( ) + " " ) ; / / Klasa Stek i z p o g l a v l j a uvanje o b je k a ta n e t . m i n d v i e w . u t i 1. S t e k < S t r i ng> stek2 = new n e t . m i n d v i e w . u t i 1. S te k < S trin g > ( ) ; for(M ese c m : M e s e c.v a lu e s O ) s te k 2 .p u s h (m .to S trin g ()); p r in t ( " s t e k 2 = " + stek2); w h i1e ( ! s t e k 2 . e m p t y ( ) ) p rin tn b (stek2 .p o p ( ) + " " ) ;

}
} / * Is p is : s te k = [JANUAR, FEBRUAR, MART, APRIL, MAJ, JUN, JUL, AVGUST, SEPTEMBAR, OKTOBAR, NOVEMBAR] element 5 = JUN Vadim elemente: P o s le d n ji red NOVEMBAR OKTOBAR SEPTEMBAR AVGUST JUL JUN MAJ APRIL MART FEBRUAR JANUAR l s t e k = [NOVEMBAR OKTOBAR SEPTEMBAR AVGUST JUL JUN MAJ APRIL MART FEBRUAR JANUAR] NOVEMBAR OKTOBAR SEPTEMBAR AVGUST JUL JUN MAJ APRIL MART FEBRUAR JANUAR stek2 = [NOVEMBAR OKTOBAR SEPTEMBAR AVGUST JUL JUN MAJ APRIL MART FEBRUAR JANUAR] NOVEMBAR OKTOBAR SEPTEMBAR AVGUST JUL JUN MAJ APRIL MART FEBRUAR JANUAR

* ///:-

716

Misliti na Javi

O d klase Mesec definisane nabrajanjem konstanti generiu se odgovarajui znakovni nizovi (objekti tipa String), svaki se stavlja na stek m etodom p u sh ( ), a kasnije se skida s vrha steka m etodom p o p ( ). Radi prikaza, operacije ldase Vector takoe se izvode na objektu klase Stack. To je m ogue zato to je, zahvaljujui nasleivanju, stek jedna vrsta vektora. Stoga se sve operacije koje se m ogu obaviti s vektorom m ogu takoe obaviti i sa stekom, npr. elem entA t( ). Kao to je ranije pom enuto, kada elite ponaanje tipa steka, treba da koristite LinkedList ili klasu net.m indview.util.Stek izvedenu iz klase LinkedList.

Klasa BitSet
Klasa BitSet se koristi za efikasno uvanje velike koliine inform acija tipa u k lju e n o -iskljueno. O na je efikasna sam o s take gledita veliine; ako vam je potreban brz pristup, treba da znate da je ova klasa neto sporija od nizova prirodnog tipa. Osim toga, m inim alna veliina skupa bitova je long, tj. 64 bita. To znai da klasa BitSet nee biti efikasna za uvanje kraih podataka, npr. onih od 8 bitova. U tom sluaju, bolje je da napravite sopstvenu klasu, ili sam o niz u koji ete sm estiti podatke ako vam je bitna veliina. (To e biti sluaj sam o ako pravite m nogo objekata koji sadre liste inform acija tipa ukljueno-iskljueno, a odluku treba doneti tek na osnovu rezultata program a z.a optimizaciju i ostalih m erenja. Ukoliko ste to odluili sam o zato to ste sami neto proglasili za preveliko, imaete veom a sloen program i izgubiete m nogo vrem ena.) O bian kontejner se iri kada m u odajete nove elem ente, a to radi i BitSet. Sledei prim er pokaz.uje kako radi klasa BitSet:
/ / : k o n t e j n e r i / B i t o v i . ja v a / / P r i k a z i v a n j e k la s e B i t S e t . im p ort j a v a . u t i l im p o r t s t a t i c n e t . m i n d v i e w . u t i 1 . P r i n t . * ; p u b l i c c la s s B i t o v i { p u b l i c s t a t i c v o id i spi s i Bi t o v e ( B i t S e t b) { p rin t( " b ito v i: " + b ); S t r i n g B u i 1d e r b b i t o v i = new S t r i n g B u i 1d e r ( ) ; f o r ( i n t j = 0; j < b . s i z e ( ) ; j+ + ) b b i t o v i . a p p e n d ( b . g e t ( j ) ? "1" : " 0 " ) ; p rin t("b it-m a s k a : " + b b it o v i) ;

)
p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] a rg s ) { Random rand = new Random(47); / / Uzmi b a j t najmanje t e i n e od n e x t l n t ( ) : b yte b t = ( b y t e ) r a n d . n e x t l n t ( ) ; B it S e t bb = new B i t S e t ( ) ; f o r ( i n t i = 7; i >=0; i - - ) i f ( ( ( l i ) & b t ) != 0) b b .s e t(i); el se bb. c l e a r ( i );

Poglavlje 17: Detaljno razmatranje kontejnera

717

p r i n t ( " v r e d n o s t t i p a byte: " + b t ) ; is p is iB ito v e (b b ); short s t = ( s h o rt)r a n d .n e x tIn t(); B i t S e t bs = new B i t S e t ( ) ; f o r ( i n t i = 15; i >=0; i - - ) i f ( ( ( 1 i ) & s t ) != 0) b s .s e t(i); e ls e b s .c le a r(i); p rin t("v re d n o s t tip a short: " + s t ) ; is p is iB ito v e (b s ); in t i t = ra n d .n e x tln t(); B i t S e t bi = new B i t S e t ( ) ; f o r ( i n t i = 31; i >=0; i - - )

i f ( ((1

i ) & it)

!= 0)

b i . s e t( i ); el se b i . c le a r( i); p rin t("v re d n o s t tip a in t : is p is iB i to v e (b i);

" + it);

/ / T e s t i r a j skupove b i t a >= 64 b i t a : B i t S e t bl27 = new B i t S e t O ; b l 2 7 . s e t (127); p r i n t ( " b i t b r o j 127 j e u k l j u e n : " + b l 2 7 ) ; B i t S e t b255 = new B i t S e t ( 6 5 ) ; b255. s e t ( 25 5 ); p r i n t ( " b i t b r o j 255 j e u k lj u e n : " + b 2 5 5 ) ; B i t S e t b1023 = new B i t S e t ( 5 1 2 ) ; b 1023. s e t (1023); b l 0 2 3 . s e t ( 1024); p r i n t ( " b i t b r o j 1023 j e u k lj u e n : " + b l0 2 3 ) ; } / * Is p is : } v r e d n o s t t i p a b y t e : -107 b i t o v i : { 0 , 2, 4, 7} bi t - m a s k a : 1 0 101 0 010 0 000 0 000 0 000000000000 000000000000000000000000000000000000000000000 vr e d n o s t t i p a s h o r t : 1302 b i t o v i : { 1 , 2, 4, 8 , 10} b it - m a s k a :

101010010000000000000000000000000000000000000000000000000000000000000000000
v r e d n o s t t i p a i n t : -2014573909 b i t o v i : { 0 , 1, 3, 5, 7, 9, 11, 18, 19, 21, 22, 23, 24, 25, 26, 31} b it - m a s k a : 1010 1 001 0 000 0 000 0 0000000000000000000000000000000000000000000000000000000000 b i t b r o j 127 j e u k lj u e n : {127} b i t b r o j 255 j e u k l j u e n : {255} b i t b r o j 1023 j e u k lj u e n : {1023, 1024}

* ///= -

718

Misliti na Javi

G enerator sluajnih brojeva koristi se za generisanje sluajnih brojeva tipa byte, sh o rt i int, a svaki od njih se pretvara u odgovarajuu bit m asku u klasi BitSet. Ovo lepo radi zato to je BitSet 64-bitni, pa nijedan od ovih brojeva ne prouzrokuje poveavanje njegove duine. Potom se pravi skup bitova vee duine. Vidite da se BitSet iri po potrebi. Ukoliko imate neprom enljiv skup indikatora koje moete da imenujete, obino je bolje koristiti klasu EnumSet (videti poglavlje N abrojani tipovi ) nego klasu BitSet poto EnumSet omoguuje rukovanje im enim a, a ne lokajam a num erikih bitova, i tim e smanjuje koliinu greaka. EnumSet vas spreava i da nehotice dodate nove lokacije indikatora, to bi moglo da prouzrokuje ozbiljne greke koje se teko pronalaze. Koristite klasu BitSet um esto klase EnumSet samo u sluaju da sve do trenutka izvravanja program a ne znate broj indikatora ili ako nije praktino dodeliti im im ena ili vam treba neka od specijalnih operacija klase BitSet (proitajte JDK dokum entaciju klasa BitSet i EnumSet).

Saetak
Ima onih koji tvrde da je biblioteka kontejnera najvanija biblioteka objektno orijentisanog jezika. U veini programa kontejneri se upotrebljavaju vie od ijedne druge kom ponente biblioteke. Neki jezici (Python, na prim er) imaju ugraene ak i osnovne kontejnerske kom ponente (liste, mape i skupove). Kao to ste videli u poglavlju uvanje objekata, pom ou kontejnera se moe uraditi m nogo toga zanimljivog, a bez m nogo napora. M eutim , doi e trenutak kada ete m orati da znate vie o kontejnerim a kako biste mogli pravilno da ih upotrebljavate - konkretno, m orate znati dovoljno o operacijam a transform isanja kljueva (heiranja) da biste napisali sopstvenu m etodu h a s h C o d e () (a m orate znati i kada je to neophodno), i m orate znati ovoljno o raznim realizacijama kontejnera da biste izabrali onu koja vam treba. U ovom pogiavlju objasnili sm o te pojm ove i razm otrili jo neke korisne detalje o biblioteci kontejnera. U ovom tren utku trebalo bi da ste prilino dobro priprem ljeni za korienje Java kontejnera u svakodnevnim program erskim zadacima. Biblioteku kontejnera je teko projektovati (to vai za veinu dizajnerskih problem a s bibliotekama). U C ++-u, kontejnerske klase su obuhvatale m nogo razliitih klasa. I'o je bilo bolje od onoga to je postojalo pre kontejnerskih klasa jezika C ++ (a to je nita), ali se nije moglo lepo prevesti u Javu. Druga je krajnost kontejnerska biblioteka koja se sastoji od jedne jedine klase, container, koja istovrem eno radi kao linearna sekvenca i asocijativni niz. Javina biblioteka kontejnera odrava neku ravnoteu: ima svu funkcionalnost koju oekujete od zrele biblioteke kontejnera, ali ju je lake nauiti i lake se koristi od kontejnerskih klasa jezika C++ i ostalih slinih biblioteka kontejnera. Rezultat ponegde izgleda udno. Za razliku od nekih drugih odluka sprovedenih u starim Java bibliotekama, te udnovatosti nisu nastale sluajno, nego su posledica odluka paljivo odvaganih kako bi se postigao kom prom is po pitanju sloenosti.
R eenja o d a b ra n ih vebi d a ta su u e le k tro n sk o m d o k u m e n tu Thc Thinking in Jtiva Annotatcd Sohition Guide , koji sc m o e k u p iti na lok aciji wwiv.MindVicw.cinn.

Javin

ulazno-izlazni

sistem

Stvaranje dobrog ulaztio-izlaznog (U /I) sistem a jed a n je od teih zadataka za projektanta jezika. Posledica toga je veliki broj razliitih pristupa.
IZG LED A DA JE PROBLEM U ZE TI U O B ZIR SVE M O G U N O S T I. N E SA M O DA PO STO JE R A ZLlClTI

izvori ili ponori podataka U /I sistema s kojim a m oete da radite (datoteka, konzola, m rene veze itd.), ve im a i raznih naina za to (sekvencijalni, nasum ian pristup, baferi, binarni, znakovni, po redovim a, po reima itd.). Projektanti Javinih biblioteka pristupili su reavanju ovog problem a tako to su napravili velikog broja klasa. Zapravo, Javin U/I sistem im a toliko m nogo klasa da na prvi pogled moe da izgleda zastraujue (ironino, ali projekat Javinog U /I sistema zapravo spreava eksploziju broja klasa). N apravljena je i velika izm ena u U/I biblioteci nakon Jave 1.0, kada je prvobitna b in arno orijentisana biblioteka dopunjena znakovno orijentisanim, Unicode U/I klasama. Klase nio (akronim od ,,new 1 /0 i to emo im e koristiti jo godinam a iako su one bile uvedene u Javu 1.4 i stoga su ve ,,stare) dodate su radi poboljanja perform ansi i funkcionalnosti. Usled toga postoji prilino veliki broj klasa koje treba savladati da bi se stekla dovoljno dobra osnovna znanja o Javinom U/I sistemu. Prilino je vano poznavati istorijat razvoja U/I biblioteke, ak i ako je vaa prva reakcija: ,,Ne gnjavi me istorijom, ve mi pokai kako se koristi"! Problem je to to e vas bez predznanja iz istorije ubrzo zbuniti neke klase i neete znati ni kada da ih koristite, ni kada ne. Ovo poglavlje je uvod u razne U/I klase u stanardnoj Javinoj biblioteci i u nain njihovog korienja.

Klasa File
Pre nego to se pozabavim o klasama koje i itaju i upisuju podatke u tokove, prouiem o uslune klase koje postoje u biblioteci, a pom au u radu s datotekam a. Klasa File ima zbunjujue ime: moete se prevariti i pomisliti da se odnosi na datoteku. Bolje ime za klasu bilo bi FilePath. O na moe da predstavlja !;eodreenedatoteke ili im ena skupa datoteka u direktorijum u. Ako je re o skupu datoteka, moete da ga potraite metodom list( ) koja vraa niz podataka tipa String. Vraanje niza um esto neke prilagodljive kontejnerske klase opravdano je zato to je broj elemenata nepromenljiv, a ako elite drugaiji spisak saraja direktorijum a, treba da napravite drugaiji objekat klase File. U ovom odeljku prikazuje se korienje ove klase i njoj pridruenog interfejsa FilenameFilter.

Listanje direktorijuma
Pretpostaviem o kako hoete da vidite spisak sadraja direktorijum a. Objekat klase File moete upotrebiti na dva naina. Ako pozovete m etodu lis t ( ) bez argum enata, dobiete kom pletnu listu datoteka u direktorijum u koji opisuje taj objekat. Ukoliko elite ogranienu listu, npr. sve datoteke s nastavkom .java, upotrebite filtar direktorijum a, klasu koja pokazuje kako se biraju objekti klase File koji e se prikazivati.

720

Misliti na Javi

Evo kratkog prim era. O bratite panju na to da se rezultati veom a lako ureuju po abecednom redosledu pom ou m etode java.utii.Arrays.sort( ) i kom paratora String. CASE_INSENSITIVE_ORDER:
//: u i/L is ta n je D ire k to riju m a .ja v a

/ / P r ik a z u je s p is a k d i r e k t o r i j u m a pomou r e g u l a r n i h iz r a z a . / / { A rg s: " D . * \ . j a v a " } im p o r t j a v a . u t i l . r e g e x . * ; im p o r t j a v a . i o . * ; im p o r t j a v a . u t i l . * ; p u b l i c c la s s L i s t a n j e D i r e k t o r i j u m a { p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] a rg s) F i l e p u t a n ja = new F i l e ( " . ) ; S trin g [] lis t a ; i f ( a r g s . l e n g t h == 0) lis ta = p u ta n ja .lis t(); el se
lista = putanja.list(new Filt ar lm en a( ar gs ));

A r r a y s . s o r t ( l i s t a , String.CASE_INSENSITIVE_ORDER); f o r ( S t r i n g s t a v k a D ir : l i s t a )
System.out.pri ntln(stavkaDir);

} }
class Filtarlmena implements Fi1en am eF i1ter { private Pattern uzorak; public FiltarImena(String regiz) { uzorak = Pattern.compi1e ( r e g i z ) ;

}
public boolean accept(File dir, String ime) return uzorak .m at ch er (i me ).m at ch es( ); {

}
} /* Ispis: PrimerZaDi rektorijum.java Li stanjeDi rektorijuma.java ListanjeDi rektorijumaZ.java Li stanjeDi rektorijuma3.java

* ///:Klasa F iita rlm en a realizuje interfejs Filenam eFilter. Korisno je videti koliko je interfejs F ilenam eFilter jednostavan:
public interface FilenameFilter { boolean accept(File dir, String ime);

} Ova klasa postoji sam o da bi m etodi li s t ( ) pruila m etodu a c c e p tf), da bi list( ) mogla povratno da pozove a c c e p t() i tako utvrdi koja im ena datoteka bi trebalo da se nau u listi. Stoga se ovakva struktura esto naziva p o vra tn i poziv (engl. callback). Konkretnije, ovo je prim er projektnog obrasca Strategy (Strategija), poto li s t ( ) realizuje tek osnovnu funkcionalnost, a vi dajete Strategy u obliku interfejsa F ilenam eFilter koji dovrava algo-

Poglavlje 18: Javin ulazno-izlazni sistem

721

ritam potreban metodi lis t() za pruanje usluge. Poto je argument metode lis t() objekat klase FilenameFilter, to znai da joj moete proslediti objekat bilo koje klase koja realizuje interfejs FilenameFilter, i tako (ak i u vreme izvravanja) odrediti nain ponaanja metode lis t( ). Svrha Strategije je da se obezbedi prilagodljivost koda. Argument metode accept() m ora da bude objekat klase File koji predstavlja direktorijum, kao i String koji sadri ime datoteke. Zapamtite da metoda lis t( ) poziva metodu accep t() za svako ime datoteke u direktorijumu, da bi videla koje od njih treba ukljuiti u listu. Na to ukazuje rezultat metode accept() koji je tipa boolean. Metoda accept() koristi metodu m atcher(ime) da bi proverila da li se regularni izraz regiz podudara sa imenom datoteke. Rezultat metode lis t() je niz koji se postepeno pravi pomou metode accept(). A n o n im n e unutranje klase Ovaj primer je savren za prepravljanje pomou anonimne unutranje klase (takve klase su opisane u poglavlju Unutranje klase). Kao prva promena, uvodi se metoda filter( ) koja vraa referencu na FilenameFilter:
/ / : u i/L is ta n je D ire k to riju m a 2 .ja v a / / K o r i s t i anonimne u n u t r a n je k la s e . im p o r t j a v a . u t i l . r e g e x . * ; im p o r t j a v a . i o . * ; im p o rt j a v a . u t i 1 . * ; p u b l i c c la s s L i s t a n j e D i r e k t o r i j u m a 2 { p u b lic s t a t i c F ile n a m e F ilte r f i l t e r ( f i n a l S trin g re g iz ) / / P r a v l j e n j e anonimne u n u tr a n je k la s e : r e t u r n new F i 1enameFi1t e r () { p r i v a t e P a t t e r n uzorak = P a t t e r n . c o m p i l e ( r e g i z ) ; p u b l i c boolean a c c e p t ( F i l e d i r , S t r i n g ime) { r e t u r n u zo ra k .m a tc h e r (im e ) .m a tc h e s ( ) ;

}
}; / / Kra j anonimne u n u tr a n je kla se

}
p u b l i c s t a t i c v o id m ain ( S t r i ng [] a rg s) { F i l e p u t a n ja = new F i l e ( " . " ) ; S trin g [] lis t a ; i f ( a r g s . l e n g t h == 0) 1i s ta = p u t a n j a . 1i s t ( ) ; el se lis ta = p u ta n ja .lis t(filte r(a rg s )); A r r a y s . s o r t ( 1 i s t a , S t r i ng . CASE I NSENSI T I V E ORDER); fo r(S trin g s t a v k a D ir : lis ta ) System.out . p r i n t l n fs ta v k a D i r ) ,

}
i / * Is p is : PrimerZaDi r e k t o r i j u m . j a v a Li s t a n je D i r e k t o r i j u m a . j a v a Li s t a n j e D i r e k t o r i juma2. ja v a Li s t a n je D i r e k t o r i j u m a 3 . j a v a

* ///:-

722

Misliti na Javi

Obratite panju na to da argument metode filte r() mora da bude oznaen kao final. To je neophodno da bi anonimna unutranja klasa mogla da koristi objekat koji je izvan njene oblasti vaenja. Ovakav pristup je bolji zato to je klasa koja realizuje interfejs FilenameFilter sada vrsto povezana s klasom ListanjeDirektorijuma2. Meutim, ovaj pristup se moe dodatno poboljati kada se definie anonimna unutranja klasa kao argument metode Iist( ); u tom sluaju, program je jo krai:
//: ui/ListanjeDirektorijuma3.java // Pravljenje anonimne unutranje klase "na licu mesta". // { A r g s : "D.*\.java"} import java.util.regex.*; import java.io.*; import j a v a . u t i l .*; public class ListanjeDirektorijuma3 { public static void main(final String[] args) { File putanja = new File(".");

S trin g [] lis t a ; i f ( a r g s . 1 ength == 0) lis ta = p u ta n ja .lis t(); el se l i s t a = p u ta n ja .lis t( n e w F ile n a m e F ilte r() { p r i v a t e P a t t e r n uzorak = P a t t e r n . compi1e ( a r g s ) ; p u b l i c boolean a c c e p t ( F i l e d i r , S t r i n g ime) { r e t u r n u z o r a k . m a tc h e r ( i m e ) .m atches( ) ;

} });
A r r a y s . s o r t ( 1 i s t a , S t r i n g . CASE I NSENSITIVE_0RDER); fo r(S trin g s t a v k a D ir : lis ta ) S y s te m .o u t.p rin tln (s ta v k a D ir);

}
} / * Is p is : PrimerZaDi r e k t o r i j u m . j a v a Li s t a n je D i r e k t o r i j u m a . ja v a Li s t a n je D i r e k t o r i j u m a 2 . j a v a Li s t a n j e D i r e k t o r i j u m a 3 . j a v a

* ///:Argument metode m a in () sada je oznaen kao final, poto anonimna unutranja klasa direktno koristi args. Ovo pokazuje kako anonimne unutranje klase omoguuju pravljenje spefinih, unikatnih klasa za brzo reavanje problema. Prednost ovog pristupa jeste to to je kod koji reava odredeni problem izolovan na jednom mestu. S druge strane, kod nije uvek lako itljiv, pa morate mudro da ga koristite. Veba 1: (3) Izmenite program ListanjeDirektorijuma.java (ili neku od njegovih varijanata) tako da FilenameFilter otvara i ita svaku datoteku (uslunom metodom net.m indview.util.TextFile) i prihvata je na osnovu toga sadri li ona bilo koji od argumenata s komandne linije koji slede iza imena datoteke.

Poglavlje 18: Javin ulazno-izlazni sistem

723

Veba 2: (2) Napravite klasu ListanjeUreenogDirektorijuma s konstruktorom koji prima objekat tipa File i od njegovih datoteka pravi ureenu listu direktorijuma. Klasi dodajte dve preklopljene metode lis t(): prvu koja proizvodi celu listu i drugu koja proizvodi podskup te liste koji odgovara njenom argumentu (to je regularan izraz). Veba 3: (3) Izmenite program ListanjeDirektorijuma.java (ili neku od njegovih varijanata) tako da sabere veliine izabranih datoteka.

Uslune metode za direktorijume


U programiranju se esto obavljaju operacije nad skupovima datoteka, bilo u lokalnom direktorijumu bilo prolaskom kroz celo stablo direktorijuma. Dobro bi posluila alatka koja proizvodi taj skup datoteka. Sledea usluna ldasa proizvodi bilo niz File objekata u lokalnom direktorijumu (metodom lo k a l()), bilo List<File> celog stabla direktorijuma poev od datog direktorijuma (metodom p rolazak()). (File objekti su korisniji od imena datoteka zato to sadre vie informacija). Datoteke se biraju na osnovu regularnog izraza koji zadajete:
/ / : n e t / m i ndvi e w / u t i 1 / D i r e k t o r i j u m . j a v a / / P r o iz v o d i sekvencu F i l e o b je k a ta k o j i odgovaraju / / regularnom i z r a z u , b i l o u lo ka lnom d i r e k t o r i j u m u , / / b i l o prolasko m kroz s t a b lo d i r e k t o r i juma. package n e t . m i n d v i e w . u t i l ; im p o r t j a v a . u t i 1 . r e g e x . * ; im p o r t j a v a . i o . * ; im p o rt j a v a . u t i l p u b l i c f i n a l c la s s D i r e k t o r i j u m j p u b lic s t a t ic F ile [ ] lo k a l ( F i l e d i r , f i n a l S t r i n g r e g i z ) { r e t u r n d i r . 1i s t F i 1es(new F i 1enameFi1t e r ( ) { p r i v a t e P a t t e r n uzorak = P a t t e r n . c o m p i l e ( r e g i z ) ; p u b l i c boolean a c c e p t ( F i l e d i r , S t r i n g ime) { r e t u r n u z o ra k .m a tc h e rf new Fi 1e ( i m e ) . getNameO) .matches ( ) ;

} I); }
publ i c s t a t i c Fi 1e [ ] 1oka1( S t r i n g p u t a n ja , f i n a l S t r i n g r e g i z ) r e t u r n lo k a l( n e w F i l e ( p u t a n j a ) , r e g i z ) ; { // P r e k lo p lje n o

}
/ / Dvojka za v r a a n je para o b je k a ta : p u b l i c s t a t i c c la s s I n f o S t a b la implements I t e r a b l e < F i l e > { p u b l i c L i s t < F i l e > d a to te k e = new A r r a y L i s t < F i l e > ( ) ; p u b l i c L i s t < F i l e > d ir m i = new A r r a y L i s t < F i l e > ( ) ; / / Podrazumevani i t e r a b i l n i element j e l i s t a d a to te k a : p u b l i c I t e r a t o r < F i 1e> i t e r a t o r ( ) { retu rn d a t o t e k e . it e r a t o r ( ) ;

724

Misliti na Javi

void addAll(InfoStabla ostalo) dirmi.addAl 1 (ostalo.di r m i ) ;

d a to te ke .a dd Al1 ( o s t al o. da to te ke );

}
public String toString() "\n\ndatoteke: { return " d i r ek to ri ju mi: " + PPrint.pformat(dirmi) + " + P P ri nt .p fo rm at (d at ote ke );

} }
public static InfoStabla prolazak(String pocetak, String regiz) { // Poni rekurziju return rekurzDirme(new File(pocetak), regiz);

}
public static InfoStabla prolazak(File pocetak, String regiz) return rekurzDirme(pocetak, regiz); { // Preklopljeno

}
p u b l i c s t a t i c I n f o S t a b l a p r o l a z a k ( F i l e poceta k) r e t u r n re k u r z D ir m e ( p o c e ta k , { / / Sve

}
p u b l i c s t a t i c I n f o S t a b la p r o l a z a k ( S t r i n g poceta k) r e t u r n rekurzDirme(new F i l e ( p o c e t a k ) , " . * " ) ; {

}
s ta tic I n f o S t a b la r e k u r z D i r m e ( F i 1e p o c D ir , S t r i n g r e g i z ) {
: p o cD ir .1 i s tF i1e s ()) { { InfoStabla result = new InfoStabla(); for(File stavka if (s ta vk a. is Di re ct ory())

r e z u l t a t .d ir mi .a dd (st av ka ); r e z u l t a t .a d dA l 1 (rekurzDirme(stavka, r e gi z)); } e ls e / / Regularna d a to te k a if(s ta v k a .g e tN a m e ().m a tc h e s (re g iz )) re zu lta t.d a to te k e .a d d (s ta v k a );

}
retu rn r e z u lta t;

}
/ / Jednostavna p ro v e ra v a l i d n o s t i : p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] a rg s) { i f ( a r g s . l e n g t h == 0) S ystem .out.pri n t ln ( p r o la z a k ( . " ) ) ; el se f o r ( S t r i n g arg : a rg s) S y s te m .o u t.p rin tln (p ro la z a k (a rg ));

}
}

III---

Metoda lo k a l() koristi varijantu metode File.list( ) nazvanu IistFiles( ) koja proizvodi niz objekata tipa File. Vidite da koristi i FilenameFilter. Ukoliko umesto niza elite listu, sami pretvorite njen rezultat u niz metodom A rrays.asList(). Metoda p ro lazak () pretvara ime poetnog direktorijuma u objekat tipa File i poziva rek u rzD irm e() koja obavlja rekurzivni prolazak kroz direktorijume, prikupljajui sve

Poglavlje 18: Javin ulazno-izlazni sistem

725

vie informacija u svakoj rekurziji. Da bismo mogli da razlikujemo obine datoteke od direktorijuma, povratna vrednost je zapravo ,,n-torka objekata - jedna lista koja sadri obine datoteke i druga koja sadri direktorijume. Polja su namerno deklarisana kao javna (public), poto je svrha InfoStabla da prikupi objekte - da vraate samo jednu listu, ne biste je napravili privatnom, pa ni vraanje para objekata ne znai da ih morate napraviti privatnim. Obratite panju na to da InfoStabla realizuje Iterable<FUe> koji proizvodi datoteke, pa je podrazumevana iteracija" po listi datoteka, dok iteraciju po direktorijumima zadajete sa dirm i. Metoda InfoStabla.toString() upotrebljava klasu ,,pretty printer" da bi se ispis lake pregledao. Podrazumevane metode to S trin g () za kontejnere, tampaju sve elemente jednog kontejnera u istom redu. U velikim kolekcijama to postaje teko itljivo, pa bi se moglo poeleti i neko drugo formatiranje. Sledea alatka dodaje prelaske u novi red i uvlaenja za svaki element:
//: net/'mindview/util/PPrint.java // Pretty-printer za kolekcije package net.mindview.util; import java.uti1 public class PPrint { public static String pformat(Col 1ection<?> k) { if(k.size() == 0) return "[]"; StringBui1der rezultat = new StringBui1der("["); for(0bject elem : k) { if(k.size() != 1) rezultat.append("\n "); rezultat.append(elem);

}
if(k.size() != 1) rezultat.append("\n') ; rezultat.append("]"); return rezultat.toString();

}
public static void pprint(Col1ection<?> k) { System.out.println(pformat(k));

}
public static void pprint(0bject[] k) { System.out.println(pformat(Arrays.asList(k)));

} } ///:Metoda pfornnat() od kolekcije proizvodi formatiran znakovni niz, a p p r in t( ) poziva p fo rm a t() da to obavi. Imajte u vidu da se drugaije tretiraju posebni sluajevi kada nema elemenata, odnosno kada je samo jedan element. Postoji i verzija metode p p r in t( ) za nizove. Uslune metode klase D irektorijum deo su paketa net.m indview.util i stoga lako dostupne. Evo primera kako se upotrebljavaju:

726

Misliti na Javi

//: io/PrimerZaDirektorijum.java // Primer upotrebe uslunih metoda za Direktorijum. import java.io.*; import net.mindview.util.*; import static ne t. mi nd vi ew .u ti l.Print.*; public class PrimerZaDirektorijum { public static void main(String[] args) // Svi d i re kt or ij um i: PP ri nt .p pr in t( Di re cto ry .p ro la za k( ". ").di r e k t o r i j u m i ) ; // Sve datoteke koje poinju na 'T' for(File datoteka : D i re kt or ij um .l ok al(".", T.*")) print( da to te ka ); pr int(".................... .... ); // Sve Java datoteke koje poinju na 'T': for(Fi1e datoteka : Dire kt or ij um .p ro la zak (".", "T.*\\.java")) pr in t( da to te ka ); p r i n t ("======================"); // Class datoteke koje sadre "Z" ili "z": for(File datoteka : irektorijum.prolazak(".",".*[Zz],*\\.class")) pr in t( da to te ka ); {

}
} /* Ispis: [.\xfiles] .\TestEOF.class .\TestEOF.java .\TransferTo.class .\TransferTo.java .\TestE0F.java A T r a n s f e r T o . java .\ x f i1es\ThawAlien.java AF r e e z e A l i e n . c l a s s .\GZIPcompress.class .\Zi pC ompress.class (primer)

* ///:Moda je potrebno da osveite svoje znanje o regularnim izrazima, pa se vratite na poglavlje Znakovni nizovi da biste shvatili druge argumente u metodama lo k al() i p ro lazak (). Da bismo otili korak dalje, napraviemo alatku koja radi i jedno i drugo: prolazi kroz direktorijume i obraduje datoteke u njima u skladu s datim objektom tipa Strategy (ovo je jo jedan primer projektnog obrasca Strategy):
/ / : net/ mindview /u ti1/ObradaDatoteka.java package net.mindview.util; import java.io.*; public class ObradaDatoteka {

Poglavlje 18: Javin ulazno-izlazni sistem

727

p u b lic in te rfa c e Strategy { v o id p r o c e s s ( F i l e d a t o t e k a ) ;

}
p riv a te Strategy s t r a t e g ija ; p r iv a te S trin g s p o lj; p u b l i c O b ra d a D a to tek a (S tra teg y s t r a t e g i j a , S t r i n g s p o l j ) th is .s tra te g ija = s tra te g ija ; t h is .s p o lj = s p o lj;

}
p u b l i c v o id p o c e t a k ( S t r i n g [ ] a rg s) { try { i f ( a r g s . l e n g t h == 0) obradaStablaDirma(new F i l e ( " . " ) ) ; el se f o r ( S t r i n g arg ; a rg s) { F i l e f i l e A r g = new F i l e ( a r g ) ; if( file A rg .is D ire c to ry ()) obradaSta bla Di r m a ( f i l e A r g ) ; el se { / / Dozvoli k o r i s n i k u da i z o s t a v i nastavak imena d a t o t e k e : i f ( !arg.endsWi t h ( " . 1 1 + s p o lj)) arg += + s p o lj; s t r a t e g i j a . p ro c e s s ( new F i 1e ( a r g ) . g e t C a n o n ic a lF i1e ( ) ) ;

} }
} ca tc h (IO E x c e p tio n e) { throw new R u n tim e E x c e p tio n ( e ) ;

/ }
p u b l i c void o b ra d a S ta b la D ir in a ( F i l e koren) throws IO Exceptio n { f o r ( F i l e d a to te k a : D i r e k t o r i j u m . p r o l a z a k ( k o r e n . g e t A b s o lu t e P a t h ( ) , + s p o lj)) s t r a t e g i j a . p ro c e s s ( d a t o t e k a . g e t C a n o n ical Fi 1e ( ) ) ;

}
/ / Prim er upo tre b e : p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] a rg s) { new ObradaDatoteka(new O b ra d a D a t o t e k a . S tr a t e g y () p u b l i c v o id p r o c e s s ( F i l e d a to te k a ) { S y s te m .o u t.p rin tln (d a to te k a );

}
}, "ja v a ").p o c e ta k (a rg s ); re z u lta te ) *///:-

}
} /* ( P o k r e n it e da b i s t e v i d e l i

Interfejs Strategy ugneden je u klasu O bradaD atoteka. Da biste ga realizovali, morate da implementirate O bradaD atoteka.Strategy i time itaocu date vei kontekst. O bradaD atoteka pronalazi datoteke koje imaju odreeni nastavak (spolj kao argument konstruktora) i predaje ih objektu Strategy (koji je takoe argument konstruktora).

728

Misliti na Javi

Ako ne zadate argumente, u klasi O bradaD atoteka pretpostavlja se da elite da proete kroz sve direktorijume koji izlaze iz tekueg direktorijuma. S druge strane, moete zadati odreenu datoteku, s nastavkom ili bez njega(ona e dodati nastavak ako bude potrebno), odnosno jedan ili vie direktorijuma. U metodi m a in () vidite elementarni nain upotrebe ove alatke; ona tampa imena svih Java datoteka izvornog koda u skladu sa argumentima koje zadate na komandnoj linijii. Veba 4: (2) Upotrebite D irektorijum .prolazak() za sabiranje veliina svih datoteka u stablu direktorijuma ija imena odgovaraju odreenom regularnom izrazu. Veba 5: (1) Izmenite program O bradaD atoteka.java tako da ispituje podudaranje s regularnim izrazom, a ne s nepromenljivim nastavkom imena datoteke.

Provera postojanja i pravljenje direktorijuma


Klasa File je vie od pukog prikaza postojee datoteke ili direktorijuma. Objekat klase File moete da koristite i za pravljenje novog direktorijuma ili celokupne putanje direktorijuma ako ona ne postoji. Moete da ispitujete i karakteristike datoteka (veliinu, datum poslednje promene, mogunost za itanje/upisivanje), utvrujete da li objekat klase File predstavlja datoteku ili direktorijum i briete datoteke. Slcdei program prikazuje neke od ostalih metoda klase File (potpun skup metoda potraite u MTML dokumentaciji na lokaciji java.sun.com ):
/ / : u i/N a p ra v iD ir e k to riju m e .ja v a / / P r i k a z u j e k o r i e n j e k la s e F i l e / / za p r a v l j e n j e d i r e k t o r i j u m a i rad s datotekama. / / { A r g s : N a p r a v iD ir e k t o r i ju m e P r o b a ) im p o r t j a v a . i o . * ; p u b l i c c la s s N a p r a v i D i r e k t o r i j u m e { p r i v a t e s t a t i c v o id u p o tr e b a ( ) { S y s te m .e rr.p rin tln ( "K o ri e n je :N a p ra v iD ire k to riju m e p u ta n ja l . . . \ n " + " P r a v i sve p u t a n je \ n " + " K o r i e n j e : N a p r a v i D i r e k t o r i jume -d p u t a n j a l . . . \ n " + " B r i e sve p u t a n je \ n " + " K o r i e n j e : N a p r a v i D i r e k t o r i j u m e - r p u t a n j a l p u t a n ja 2 \ n " + "Pre im e n u je p u t a n j u l u p u t a n j u 2 " ; S y s te m .e x it(l);

}
p r i v a t e s t a t i c v o id p o d a c i D a t o t e k e ( F i l e f ) { S y s te m .o u t.p rin tln ( " A p s o lu tn a p u t a n j a : " + f . g e t A b s o l u t e P a t h ( ) + " \ n moe da se i t a : " + f .c a n R e a d () + " \ n moe da se u p i s u j e : " + f . c a n W r i t e ( ) + " \ n ime: " + f.g e tN a m e () + \n r o d i t e l j : " + f . g e t P a r e n t ( ) + "\n putan ja : " + f.g e tP a th ( ) + " \ n d u in a : " + f . l e n g t h ( ) + \ n datum p o s le d n j e promene: " + f . 1a s t M o d i f i e d ( ) ) ;

Poglavlje 18: Javin ulazno-lzlazni sistem

729

if(f.is F ile ()) S y s te m .o u t.p rin tln (" to j e datoteka"); e lse i f ( f . i s D i r e c t o r y ( ) ) S y s te m .o u t.p rin tln ("to je d ir e k to r iju m " ) ;

}
p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] args) { i f ( a r g s . l e n g t h < 1) u p o t r e b a ( ) ; if(a rg s .e q u a ls (" -r" )) { i f ( a r g s . l e n g t h != 3) u p o t r e b a ( ) ; F ile s t a r i = new F i l e ( a r g s ) , novolme = new F i l e ( a r g s ) ; s t a ri. r e n a m e T o ( n o v o I m e ) ; p o d a c iD a to te k e (s ta ri); p o d ac iD a to te k e ( n o v o Im e ) ; r e t u r n ; / / I z la z a k i z f u n k c i j e main

}
i n t b r o j = 0; boolean b r i s a n j e = f a l s e ; if(a rg s .e q u a ls ("-d ")) { b r o j+ + ; b ris a n je = tru e ;

}
b ro j- - ; w h ile (+ + b ro j < a rg s .le n g th ) { F i l e f = new Fi 1e ( a r g s [ b r o j ] ) ; i f ( f . exi st s ( ) ) { System.out . p r i n t l n ( f + 1 1 p o s to ji"); if(b ris a n je ) { S y s te m .o u t. p r i n t l n ( " b r i e m . . . " + f ) ; f .d e le te ();

} }
e ls e { / / Ne p o s t o j i i f ( ! b ris a n je ) { f .mkdi rs 0 ; S y s te m .o u t. p r i n t l n ( " n a p r a v lj e n a " + f ) ;

} }
p o d a c iD a to te k e (f);

} }
} / * I s p i s : (80% podudaranja) n a p r a v lje n o NapraviD i re k to r iju m e P ro b a A p s o lu tn a p u t a n ja : d : \ a a a - T I J 4 \ c o d e \ io \ M a k e D ir e c t o r ie s T e s t moe da se i t a : t r u e moe da se u p i s u j e : t r u e ime: N a p ra v iD i r e k to r iju m e P ro b a r o d i t e l j : n u ll p u t a n j a : N a p ra v iD i r e k to r iju m e P ro b a d u in a : 0

730

Misliti na Javi

datum p o s le d n je promene: 1101690308831 to j e d ir e k to r iju m

* ///= U metodi podaciD atoteke() moete da vidite razne metode za ispitivanje datoteka koje se koriste za prikazivanje informacija o datoteci ili putanji direktorijuma. Prva metoda koja se koristi je renam eTo(); ona preimenuje (ili premeta) datoteke na potpuno novu putanju predstavljenu argumentom tipa File. Ovaj pristup se moe koristiti i za direktorijume proizvoljne duine. Ako budete eksperimentisali s gornjim programom, ustanoviete da moete napraviti putanju direktorijuma proizvoljne sloenosti zato to e m kdirs( ) obaviti sav posao umesto vas. Veba 6: (5) Iskoristite program O bradaD atoteka i pronaite u nekom podstablu direktorijuma sve datoteke Java izvornog koda koje su promenjene nakon odreenog datuma.

Ulaz i izlaz
U U/I bibliotekama esto se koristi apstraktan pojam - tok podataka (engl. stream). Tok predstavlja neki izvor ili ponor podataka u obliku objekta koji je u stanju da daje ili prima delove podataka. Tok skriva detalje o tome ta se deava s podacima unutar stvarnog U/I ureaja. Klase Javine biblioteke za U/I podeljene su na ulazne i izlazne, to moete videti u hijerarhiji Javinih klasa u dokumentaciji na Webu. Zbog nasleivanja, sve klase izvedene iz InputStream ili Reader imaju osnovnu metodu re a d () za itanje jednogbajta ili niza bajtova. Slino, sve klase izvedene iz O utputStream ili W riter imaju osnovnu metodu write( ) za upisivanje jednog bajta ili niza bajtova. Meutim, obino neete koristiti te metode, jer one postoje da bi ih koristile druge klase koje obezbeuju korisnije metode. Retko ete praviti objekat toka korienjem samo jedne klase; umesto toga, grupisaete vie objekata da biste postigli eljenu funkcionalnost. Mogunost da se napravi vie objekata radi dobijanja jednog toka, glavni je krivac za nerazumljivost Javine biblioteke tokova. Korisno je kategorizovati klase prema njihovoj funkcionalnosti. U Javi 1.0 projektanti biblioteka su se vodili idejom da se sve klase koje imaju veze sa ulazom, izvoe iz klase InputStream , a da sve klase povezane sa izlazom nasleuju klasu OutputStream . Kao i dosad u knjizi, pokuau da prikaem klase, ali u podrazumevati da ete sve detalje, npr. kompletne spiskove metoda, traiti u dokumentaciji na Webu.

Vrste ulaznih tokova


Klasa InputStream treba da predstavlja ulazne tokove iz razliitih izvora. Ti izvori mogu da budu: 1. niz bajtova 2. objekat klase String 3. datoteka

Poglavlje 18: Javin ulazno-izlazni sistem

731

4. cev (engl. pipe) koja radi kao prava cev: podaci se stavljaju na jednom kraju, a izlaze na drugom 5. niz drugih tokova koji se mogu objediniti u jedan tok 6. drugi izvori, npr. veza sa Internetom (o ovome se govori u knjizi Thinking in Enterprise Java, dostupnoj na adresi www.MindView.net). Svaki od ovih tipova ulaza povezan je sa odreenom potklasom klase InputStream. Pored toga, FilterlnputStream takoe je vrsta ulaznog toka koji slui kao osnova za dopunske klase, tj. za dodeljivanje atributa ili korisnih interfejsa ulaznim tokovima. O ovome e biti rei u nastavku.

Tabela U/l-1 Tipovi ulaznih tokova


Klasa Funkcija Argum enti konstruktora Kako se koristi ByteArraylnputStream

Omoguuje da se memorijski blok koristi kao ulazni tok

Bafer iz kojeg se izvlae bajtovi. Kao Izvor podataka. Poveite je sa objektom klase FilterlnputStream da biste dcbili koristan interfejs.
String. Stvarna realizacija zapravo koristi StringBuffer

StringBufferlnputStream

Konvertuje String u InputStream

Kao izvor podataka. Poveiteje sa objektom klase FilterlnputStream da biste dobili koristan interfejs.
FileInputStream

itanje informacija iz datoteke

String koji predstavlja ime datoteke, ili objekti klase File ili FileDescriptor

Kao izvor podataka. Poveite je sa objektom klase FilterlnputStream da biste dobili koristan interfejs.
Pipedlnput Stream

Daje podatke koji se upisuju u pridrueni PipedOutputStream. Realizuje slanje kroz cevovod. Konvertuje dva ili vie objekata klase InputStream ujedan ulazm tok.

PipedOutputStream

Kao izvor podataka u vienitnom radu. Poveiteje sa objektom klase FilterlnputStream da biste dobili koristan interfejs. Dva objekta klase InputStream ili Enumeration za kontejner objekata klase InputStream

SequencelnputStream

Kao izvor podataka. Poveite je sa objektom klase FilterlnputStream da biste dobili koristan interfejs.
Filterlnput Stream

Apstraktna klasa, interfejs za dopunske klase koje obezbeduju korisne funkcije za ulazne tokove. Pogledajte tabelu U/l -3.

Pogledajte tabelu U/l -3. Pogledajte tabelu U/l -3.

732

Misliti na Javi

Vrste izlaznih tokova (OutputStream)


Ova kategorija obuhvata klase koje odluuju gde e se usmeriti izlaz: u niz bajtova (ali ne i String; znakovni niz moete da napravite korienjem niza bajtova), datoteku ili cev. Pored toga, FilterO utputStream slui kao osnovna klasa za dopunske klase koje dodaju atribute ili korisne interfejse u izlazne tokove. O ovome e biti rei u nastavku.

Tabela U/i-2 Vrste izlaznih tokova


Klasa Funkcija A rg u m e n ti konstruktora Kako se koristi ByteArrayO utputStream

Pravi bafer u memoriji. Svi podaci koje aljete u tok smetaju se u taj bafer.

Opciona poetna veliina bafera. Za zadavanje odredita podataka. Poveite je sa objektom klase FilterO utputStream da biste dobili koristan interfejs. Znakovni niz koji predstavlja ime datoteke, ili objekte klase File ili FileDescriptor

File O utputStream

Slanje informacija u datoteku.

Za zadavanje odredita podataka. Poveite je sa objektom klase FilterOutputStream da biste dobili korr stan interfejs.
PipedO utputStream

Sve informacije koje upisujete u ovaj tok automatski zavravaju kao ulaz za odgovarajui PipedInputStream Realizuje prijem iz cevovoda. Apstraktna klasa. osnova za dopunske klase koje obezbeduju korisne funkcije drugim izlaznim tokovima. Pogledajte tabelu U/l -4.

PipedlnputStream

Za zadavanje odredita podataka u vienitnom radu. Poveiteje sa objektom klase FilterOutputStream da biste dobili koristan interfejs. Pogledajte tabelu U/l -4 Pogledajte tabelu U/l -4.

FilterO utputStream

Dodavanje atributa i korisnih interfejsa


Dekoratori su predstavljeni u poglavlju Generiki tipovi , na stranici 484. Javina U/I biblioteka zahteva veliki broj kombinacija funkcija i zato se koristi projektni obrazac Decorator (Dekorator).1 Zbog toga u Javinoj U/I biblioteci postoje filtarske klase: apstraktna filtarska klasa je osnovna klasa za sve dekoratore. Decorator mora da ima isti interfejs kao olijekat koji omotava, ali moe i da ga proiri, to se deava u nekim filtarskim klasama. Ovakav obrazac, ipak, ima i nedostatak. Projektni obrasci Decorator omoguuju mnogo veu prilagodljivost programa (poto se atributi lako kombinuju), ali poveavaju slo/enost koda. Javinu U/I biblioteku nije pogodno koristiti zato to morate da pravite veliki broi klasa, tj. jezgro ulaza/izlaza i razne dekoratore da biste dobili jedan U/I objekat koji elite.
N ije ja s n o d a li jc to b ila d o b r a p ro je k ta n ts k a o lu k a , n a ro ito k a d a se u z m e u o b z ir je d n o s ta v n o s t U/1 b ib lio te k a u d r u g im je z icim a. Ali o d lu k a se tim e o p ra v d a v a .

Poglavlje 18: Javin ulazno-izlazni sistem

733

Klase FilterlnputStream i FilterO utputStream koje nemaju preterano intuitivna imena, obezbeuju dekoratorski interfejs za kontrolisanje odreenog ulaznog ili izlaznog toka. FilterlnputStream i FilterO utputStream su apstraktne klase izvedene iz osnovnih klasa U/I biblioteke, InputStream i O utputStream , to je i kljuni zahtev dekoratora (da bi obezbedio zajedniki interfejs za sve omotane objekte).

Filtriranje ulaznog toka


Klase koje realizuju FilterlnputStream izvravaju dva prilino razliita zadatka. D atalnputStream omoguuje itanje razliitih prostih tipova, kao i objekata klase String - za to slue metode koje poinju reju read (itanje), npr. readB yte(), readFIoat() itd. Ova klasa, uz srodnu klasu D ataO utputStream , omoguuje premetanje prostih tipova podataka s jednog mesta na drugo pomou toka. Ta mesta odreuju klase iz tabele U /I-l. Preostale klase menjaju nain internog ponaanja ulaznog toka: odreuju da li je on baferisan ili nije, da li prati broj redova koje ita (i omoguuje da zatraite red po broju ili da zadate broj reda) i da li se jedan znak moe vratiti u bafer. Poslednje dve klase prilino lie na podrku za pravljenje prevodioca (verovatno su dodate da bi se podrao eksperimentalni Javin prevodilac pisan na Javi), pa ih verovatno neete koristiti u svakodnevnom programiranju. Gotovo uvek morate da baferiete ulaz, bez obzira na to s kojim U/I ureajem ste povezani, pa bi bilo korisnije da U/I biblioteka ima specijalan sluaj (ili prosto poziv metode) za nebaferisani ulaz umesto za baferisan ulaz. Tabela U / I - 3 V rste f iltr ir a n ih u la z n ih to k o v a
Klasa Funkcija A rgu m enti konstruktora Kako se koristi DatalnputStream

Konsti se u kombinaciji s klasom DataOutputStream , da bi se iz toka na prenosiv nain itali prosti tipovi (int, char, long itd.). Koristite je za smanjenje broja fizikih operacija itanja svaki put kada vam zatreba jo podataka. Ovom klasom kaete toku: Upotrebi bafer". Prati brojeve redova u ulaznom toku: moete da pozovete getLineNum ber( ) i setLinelMumber(int)

InputStream

Sadri kompletan interfejs koji omoguuje itanje prostih tipova.


InputStream, uz opcionu veliinu bafe-

BufferedlnputStream

ra. Ne obezbeduje interfejs sama po sebi, ve samo procesu dodaje bafer. Pridruite jo j objekat koji realizuje interfejs.
InputStream

LineNumberInputStream

Samo dodaje numerisanje redova. pa ete jo j verovatno pridruiti objekat koji realizuje interfejs.
InputStream

PushbacklnputStream

Ima skladite za vraanje jednog bajta u koji moete da smestite poslenji proitani znak.

Obino se koristi u analizatoru za prevodioca. Verovatno je neete koristiti.

734

Misliti na Javi

Filtriranje izlaznog toka


Odgovarajua klasa za D atalnputStream je D ataO utputStream ; ona formatira sve proste tipove i objekte klase String u tok na takav nain da ih moe itati svaki tok tipa DatalnputStream na bilo kojoj platformi. Sve njene metode poinju reju write (upis), npr. w riteB yte(), w riteF loat() itd. Prvobitna svrha klase PrintStream bila je ispis svih prostih tipova podataka i objekata klase String u formatu koji se moe prikazivati ljudima. To se razlikuje od D ataO utputStream, iji je cilj da elemente podataka postavi u binarni niz na takav nain da ih DataInputStream moe rekonstruisati nakon prenosa. Dve vane metode klase PrintStream su p r in t( ) i p r in tln () koje se redefiniu za ispis svih tipova. Razlika izmeu ove dve metode je to to p rin tln ( ) prelazi u novi red po zavretku tampanja. Klasa PrintStream moe da bude problematina zato to hvata sve izuzetke tipa IOException (metodom checkE rror() morate eksplicitno da proverite da li je nastala do greka). Klasa PrintStream nije primenljiva za neke jezike i ne radi s prelomima redova na nain nezavisan od platforme (ti problemi su reeni pomou klase PrintW riter o kojoj e biti rei kasnije. BufferedOutputStream je opunska klasa koja nalae toku da koristi baferisanje da se podaci ne bi fiziki upisivali kad god se upisuje u tok. Verovatno ete ovu klasu uvek koristiti za ispisivanje rezultata. Tabela U/l-4 Vrste filtriranih izlaznih tokova
Klasa Funkcija A rgum enti konstruktora Kako se koristi DataO utputStream U kombinaciji s klasom DatalnputStre- O utputStream am koristi se za upis.vanje prostih tipova (int, char, long it.| u tok na prenosiv nain. Koristi se za formatiranje izlaza. Za razliku od klase D ataO u tp u tStream koja skladiti podatke, PrintStream ih ispisuje. Sadr, k0mpletan mterfejs koji omoguuje uplsivanje prostlh t(pova OutputStream, uz opcioni argument tipa boolean koji odreuje da li se bafer prazni prilikom prelaska u sleei red. Trebalo bi da bude konano" omotavanje za objekat kiase OutputStream Verovatno eteje dosta koristiti. BufferedO utputStream Koristite je da biste izbegli flziko upisivanje pri svakom slanju podataka. O utputStream, uz opcionu veliinu bafera.

PrintStream

Ovom klasom kaete toku da korist, ba- Ne obezbeduje mterfejs sama po seb, fer Za pranjenje sadraja bafera mo- V samo proceS(J dodaje bafer Dode|, ete a pozovete metodu flush( ) teJQ J objeka( kQj( rea|jz(Jje in(erfejs

Klase za itanje i upisivanje


U Javi 1.1 znaajno su izmenjene osnovne biblioteke U/I tokova. Kada vidite klase Reader i Writer, prvo ete pomisliti (kao i ja) da zamenjuju klase InputStream i O utputStream .

Poglavlje 18: Javin ulazno-izlazni sistem

735

Ipak, nije tako. Iako su neki elementi prvobitne biblioteke tokova zastareii (ako ih koristite, dobiete upozorenje od prevodioca), klase InputStream i O utputStream i dalje obezbeduju vane funkcije binarno orijentisanog ulaza/izlaza, dok klase Reader i W riter obezbeduju znakovno orijentisani Unicode ulaz/izlaz. Pored toga: 1. U lavu 1.1 dodate su nove klase u hijerarhiji ulaznih i izlaznih tokova, pa je oigledno da klase InputStream i O utputStream nisu zamenjene. 2. Postoje prilike kada morate da koristite klase iz binarne hijerarhije u kombinaciji s klasama u znakovnoj hijerarhiji. Da bi se to postiglo, postoje posrednike klase: InputStreamReader konvertuje InputStream u Reader, a O utputStream W riter konvertuje O utputStream u Writer. Hijerarhije klasa Reader i W riter postoje prvenstveno zbog internacionalizacije. Stara U/I hijerarhija podrava samo osmobitne binarne tokove, odnosno ne radi dobro sa esnaestobitnim Unicode znakovima. Poto se Unicode koristi za internacionalizaciju (a Javin izvorni tip char je esnaestobitni Unicode), hijerarhije klasa Reader i W riter su dodate da bi se podrao standard Unicode u svim U/I operacijama. Pored toga, projektovane su nove biblioteke koje rade bre nego stare.

Izvori i ponori podataka


Za gotovo sve originalne ulazno/izlazne Javine klase postoje ekvivalentne potklase Reader i W riter koje obezbeuju osnovni rad sa Unicode znakovima. Meutim, postoje prilike kada su binarni ulazni i izlazni tokovi ispravno reenje. To se naroito odnosi na biblioteke java.util.zip koje su binarno, a ne znakovno orijentisane. Zbog toga je najmudrije pokuati koristiti klase Reader i W riter kad god je to mogue, a situacije kada morate da koristite binarno orijentisane biblioteke otkriete sami zato to kod nee moi da se prevede. Evo tabele koja prikazuje ve/.u izmeu izvora i ponora informacija (tj. mesta s kojih podaci fiziki stiu i kuda odlaze) u dve hijerarhije.
Izvori i ponori: klasa Jave 1.0 O dgovarajua klasa Jave 1. 1

InputStream OutputStream FilelnputStream FileOutputStream


StringBufferlnputStream

Reader Konvertor InputStream Reader Wnter Konvertor OutputStream\X/riter FileReader FileU/riter


StringReader StringW riter CharArrayReader CharArrayW riter PipedReader PipedW riter

(zastarelo) |nema odgovarajue klase) ByteArraylnputStream ByteArrayOutputStream PipedlnputStream PipedOutputStream

U veini sluajeva otkriete da su interfejsi dve razliite hijerarhije slini, ako ne i istovetni.

736

Misliti na Javi

Menjanje ponaanja toka


U klasama InputStream i O utputStream , tokovi su prilagoeni specifinim potrebama pomou dopunskih potklasa klasa FilterlnputStream i FilterO utputStream . I u hijerarhiji klasa Reader i W riter koristi se ovaj pristup, ali ne na potpuno isti nain. U sledeoj tabeli veza nije toliko direktna kao u prethodnoj tabeli. To je posledica organizacije klasa: za razliku od toka BufferedO utputStream koji je potklasa klase FilterOutputStream , BufferedW riter nije potklasa klase FilterW riter (koja, iako je apstraktna, nema potklase, pa izgleda da je uvedena ili zbog nekih kasnijih proirenja, ili samo da se ne biste pitali zato je nema). Meutim, interfejsi klasa su veoma slini.
Filtri: klasa Jave 1.0 O dgovarajua klasa Jave 1.1 FilterReader F ilterW riter (apstraktna klasa bez potklasa] BufferedReader (ima i metodu re a d L in e f)) Buffered\X/riter

FilterlnputStream FilterOutputStream BufferedlnputStream BufferedOutputStream


DatalnputStream

Koristite DatalnputStream . osirn kada morate da koristite metodu re a d L in e f) U tom sluaju bi trebalo da koristite klasu BufferedReader PrintW riter LineNum berReader StreamTokenizer (koristite konstruktor koji prihvata argument

PrintStream
LineNum berlnputStream

(zastarelo)
StreamTokenizer

tipa Reader) PushBacklnputStream


PushBackReader

Jedno pravilo je sasvim jasno: kad god elite da koristite readL ine(), to vie ne bi trebalo da radite pomou klase D atalnputStream (o tome ete dobiti upozorenje tokom prevoenja), ve pomou klase BufferedReader. U svim ostalim sluajevima, D atalnputStream se i dalje preporuuje. Da bi se olakao prelazak na korienje klase PrintW riter, njeni konstruktori prihvataju sve objekte tipa O utputStream , ali i Writer. Medutim, PrintW riter ne omoguuje nita bolje formatiranje od klase PrintStream ; njihovi interfejsi su doslovno isti. Konstruktor klase PrintW riter takode ima opciju za automatsko pranjenje internih bafera, to se deava nakon svakog poziva metode p rin tln () ako je indikator postavijen u konstruktoru.

Klase koje nisu promenjene


Neke klase su ostale nepromenjene i u Javi 1.1: Naroito se bez ikakvih izmena koristi klasa D ataO utputStream , pa za uvanje i pronalaenje podataka u prenosivom formatu moete da primenite hijerarhije InputStream i O utputStream .

Poglavlje 18: Javin ulazno-izlazni sistem

737

Klase Jave 1.0 bez odgovarajuih klasa u Javi 1.1 DataO utputS tream File RandomAccessFile SeguencelnputStream

Poseban sluaj: klasa RandomAccessFile


Klasa RandomAccessFiie koristi se za datoteke sa zapisima poznate veliine, pa s jednog zapisa na drugi moete da se pomerate metodom seek(), a zatim da itate ili menjate zapise. Zapisi ne moraju da budu iste veliine; treba samo da bude mogue odrediti njihovu veliinu i mesto u datoteci u kojoj se nalaze. Isprva je pomalo teko poverovati da RandomAccessFile nije deo hijerarhija klasa InputStream ili O utputStream . Meutim, ona nema nikakve veze s tim hijerarhijama osim to realizuje interfejse D atalnput i D ataO utput (koje realizuju i D atalnputStream i DataO utputStream ). Ona ak ne koristi ni funkcije postojeih klasa InputStream ili OutputStream - to je sasvim posebna klasa, napisana kompletno od poetka, sa sopstvenim, veinom osnovnim, metodama. RandomAccessFile nije deo hijerarhije zato to se ponaa sasvim drugaije od ostalih U/I tipova: omoguuje kretanje unapred i unaza po datoteci. U svakom sluaju, ona postoji kao samostalan, direktan naslednik klase Object. U osnovi, RandomAccessFile radi kao D atalnputStream u kombinaciji s klasom DataO utputStream , uz metode getFilePointer() za pronalaenje pozicije unutar datoteke, seek () za prelazak na novu poziciju unutar datoteke i le n g th () za odreivanje maksimalne veliine datoteke. Osim toga, njeni konstruktori zahtevaju jo jedan argument (isti kao za funkciju fo p e n () u jeziku C) koji ukazuje na to da li se radi samo o nasuminom itanju (r) ili o itanju i upisivanju (rw). Nema podrke za datoteke u koje moe samo da se upisuje, to ukazuje na to da bi klasa RandomAccessFile mogla da radi dobro i da je izvedena iz klase D atalnputStream . Metode za pretraivanje dostupne su samo unutar klase RanomAccessFile koja radi iskljuivo s datotekama. Klasa BufferedlnputStream omoguuje oznaavanje pozicije (ija se vrednost uva u internoj promenljivoj) metodom m a rk () i vraanje na tu poziciju metodom re se t(), ali su te metode ograniene i nisu naroito korisne. Od Jave 1.4, umesto veine, ako ne i svih funkcija klase RandomAccessFile koriste se nio datoteke preslikane u memoriju (engl. m em ory-mappedfiles), koje ebiti opisane u nastavku poglavlja.

Tipine primene U/l tokova


lako klase U/I tokova moete da kombinujete na razne naine, verovatno ete upotrebljavati samo nekoliko kombinacija. Sledei primer se moe koristiti kao osnovna referenca; on prikazuje pravljenje i korienje tipinih U/I konfiguracija. U ovim primerima, obradu izuzetaka pojednostaviemo tako to emo prosleiti izuzetke na konzolu, ali to je prikladno samo u malim primerima i uslunim klasama. Vi ete u kodu morati da primenite bolju obradu izuzetaka.

738

Misliti na Javi

Baferisana ulazna datoteka


Kako biste otvorili datoteku za unos znakova, upotrebite FilelnputReader sa objektima klasa String ili File kao imenima datoteka. Da bi se poveala brzina, ta datoteka bi trebalo da bude baferisana, pa dobijenu referencu prosledite konstruktoru klase BufferedReader. Poto ta klasa ima i metodu readL ine(), to je konaan objekat i interfejs iz koga ete itati. Kada stignete do kraja datoteke, metoda read L in e() vraa null, to se koristi kao uslov izlaska iz petlje while.
//: io/BaferisanaUlaznaDatoteka.java import java.io.*; public class BaferisanaUlaznaDatoteka { // Baci izuzetke na konzolu: public static String read(String imedatoteke) throws IOException { // itanje ulaza red po red: BufferedReader in = new BufferedReader( new FileReader(imedatoteke)); String s; StringBuilder sb = new StringBui1der(); while((s = in.readLine())!= null) sb.append(s + "\n"); in.c1ose(); return sb.toString();

}
public static void main(String[] args) throws IOException { System.out.print(read("BaferisanaUlaznaDatoteka.java"));

}
} /* (Pokrenite da biste videli rezultat) *///:-

Objekat sb tipa StringBuilder koristi se za sakupljanje celokupnog sadraja datoteke (ukljuujui i znakove za prelazak u novi red koji se moraju dodati poto ih readL ine() odseca). Na kraju se poziva metoda c lo se() za zatvaranje atoteke. Veba 7: (2) Otvorite tekstualnu datoteku tako da je moete itati red po red. Proitajte svaki red kao znakovni niz i stavite ga u listu tipa LinkedList. Ispiite sve redove povezane (ulanane) liste obrnutim redosledom. Veba 8: ( 0 Promenite vebu 7 tako da se ime datoteke koju itate prosleuje kao argument s komandne linije. Veba 9: ( 1) Promenite vebu 8 tako da pretvara sva slova redova teksta iz liste ArrayList u velika i alje rezultate u tok System.out.
M ctoda close( ) bie pozvana au to m atsk i to k o m izvravanja m etode finalize( ), to bi trebalo da se desi pri izlasku iz pro g ram a (b e z o b z ira na to da li e se sm e e sk u p iti). M e u tim .n a dru g im m estim a u knjizi objan jen o je da to ne rad i kako su pro jek tan ti Jave oekivali (tj. prosto ne radi), pa je jedini bezbedan p ristu p da se eksplicitno pozove m eto d a c lo s e ( ) za datoteke.

Poglavfje 18: Javin ulazno-izlazni sistem

739

Veba 10: (2) Promenite vebu 8 tako da kao dodatne argumente s komandne linije prihvata rei koje e traiti u datoteci. Ispiite sve redove u kojima se rei pronau. Veba 11: (2) U primeru unutrasnjeklase/UpravljanjeStaklenomBastom.java, KontrolerStakleneBaste sadri fiksiran skup dogaaja. Promenite program tako da ita dogaaje i njihova relativna vremena iz tekstualne datoteke. (nivo teine 8): Za pravljenje dogaaja upotrebite projektni obrazac Factory M ethod (Proizvodna metoda) pronai ete ga u knjizi Thinking in Patterns (with Java) koja se moe preuzeti s lokacije www.MindView.net.

itanje iz memorije
U ovom odeljku se na osnovu String rezultata metode BufferedInputFile.read() pravi objekat klase StringReader. Potom se poziva metoda re a d () koja ita jedan po jedan znak i alje ga na konzolu.
/ / : io /C ita n je lz M e m o rije .ja v a im p o r t j a v a . i o . * ; p u b l i c c la s s C i t a n je lz M e m o r i je { p u b l i c s t a t i c v o i m ain ( S t r i n g [] args) throws I 0 E x c e p tio n { S trin g R e a d e r i n = new S trin g R e a d e r ( B u f f e r e d l n p u t F i 1e . r e a d ( " C i t a n j e l z M e m o r i j e . j a v a " ) ) ; i n t c; w h i l e ( ( c = i n . r e a d ( ) ) != -1) S y s te m .o u t.p rin t((c h a r)c );

}
} /* ( P o k r e n it e da b i s t e v i d e l i re z u lta t) * / / / : -

Obratite panju na to da metoda re a d () vraa sledei bajt kao ceo broj (int) i stoga se on mora konvertovati u tip char da bi se pravilno ispisivao.

Formatiran ulaz iz memorije


Da biste proitali formatirane podatke, upotrebite binarno orijentisanu U/1 klasu DatalnputStreani (a ne neku znakovno orijentisanu). To znai da morate koristiti klase hijerarhije InputStream , a ne klase hijerarhije Reader. Naravno, pomou klasa tipa InputStream moete da proitate bilo ta (pa i datoteku) kao niz bajtova, ali se ovde koristi String.
//: i o / F o r m a t ir a n U la z I z M e m o r ije . ja v a im p o r t j a v a . i o . * ; p u b l i c c la s s F o r m a t ir a n llla z I z M e m o rije { p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] arg s) throws I0 E x c e p tio n { try { D a taln putS tre am u la z = new D a ta InputStream ( new B yte A rr a y In p u tS tr e a m ( B a f e r i sa n a U la zn aD a to te ka .re a d (

74 0

Misliti na Javi

"FormatiranUlazIzMemorije.java"),getBytes())); while(true) System.out.print((char)ulaz.readByte()); } catch(EOFException e) { System.err.println("Kraj toka");

} }
} /* (Pokrenite da biste videli rezultat) *///:~

Da bi se objekat klase String pretvorio u niz bajtova, to je potrebno za klasu ByteArrayInputStream , koristi se metoda getB ytes() klase String. Nakon toga imate odgovarajui ulazni tok koji ete proslediti klasi D atalnputStream . Ako itate znakove iz toka tipa D atalnputStream bajt po bajt pomou metode readB y te(), svaka vrednost moe biti vaei rezultat, pa se povratna vrednost ne moe koristiti za otkrivanje kraja ulaznih podataka. Umesto toga moete da upotrebite metodu available() da biste saznali koliko jo znakova ima. Evo primera koji pokazuje kako se ita datoteka bajt po bajt:
/ / : u i/P ro v e ra K ra ja .ja v a / / Provera k r a j a d a to te k e tokom njenog i t a n j a b a j t po b a j t . im p o r t j a v a . i o . * ; p u b l i c c la s s Prov e ra K ra ja { p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] args) throws IOExce ption { Data ln putStre am u la z = new Data InputStream( new B u ffe r e d In p u tS tr e a m ( new F i l e I n p u t S t r e a m ( " P r o v e r a K r a j a . j a v a " ) ) ) ; w h i1e ( u l a z . a v a i 1a b l e ( ) != 0) S y s te m .o u t. p r i n t ( ( c h a r ) u l a z . r e a d B y t e ( ) ) ;

}
} /* ( P o k r e n it e da b i s t e v i d e l i r e z u l t a t ) * / / / : -

Obratite panju na to da metoda available() radi razliito, u zavisnosti od toga koja se vrsta medija ita; u optem sluaju, ona vraa broj bajtova koji se mogu proitati bez blokiranja. Ako se radi o datoteci, to podrazumeva celu datoteku, ali za neku drugu vrstu toka moda ima drugaije znaenje, pa ovu metodu paljivo koristite. Kraj ulaznih podataka u sluajevima poput ovog mogli biste da otkrijete i hvatanjem izuzetka. Meutim, korienje izuzetaka za kontrolu toka smatra se njihovom zloupotrebom.

Osnove pisanja u datoteku


Objekat klase FileW riter upisuje podatke u datoteku. Gotovo uvek e biti potrebno da se izlaz baferie omotavanjem u BufferedW riter (pokuajte da izostavite omotavanje da biste videli kakav e to uticaj imati na performanse: baferisanje neverovatno poboljava performanse U/I operacija). U ovom primeru, izlazni tok se radi formatiranja dekorie kao PrintW riter. Datoteka koja je napravljena na ovaj nain moe se itati kao obina tekstualna datoteka:

Poglavlje 18: Javin ulazno-izlazni sistem

741

/ / : io /O s n o v e P is a n ja U D a to te k u .ja v a im p o r t j a v a . i o . * ; p u b l i c c la s s OsnovePisanjaUDatoteku { s t a t i c S t r i n g d a to te k a = "O sno v e P is a n ja U D a to te k u .o u t"; p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] args) throvvs IOException { BufferedRe ader u la z = new B u fferedReader( new S trin g R e a d e r( B a f e r is a n a U la z n a D a t o t e k a . r e a d (" O s n o v e P is a n ja U D a to t e k u . ja v a ") ) ) ; P r i n t W r i t e r out = new P r i n t W r i t e r ( new B u ffe r e d W r ite r( n e w F i l e W r i t e r ( d a t o t e k a ) ) ) ; i n t brojRedova = 1; S t r i n g s; w h i l e ( ( s = u l a z . r e a d L i n e O ) != nul 1 ) ou t.prin tln (b ro jR e d o va+ + + " + s); o u t.c lo s e O ; / / P r ik a i u s k la d i t e n u d a to te k u : S y s t e m . o u t . p r i n t l n ( B a f e r i sanaUlaznaDatoteka. r e a d ( d a t o t e k a ) ) ;

}
} /* ( P o k r e n it e da b i s t e v i d e l i re z u lta t) *///:-

Dok se redovi upisuju u datoteku, uveava se ukupan broj redova. Obratite panju na to da nisam upotrebio klasu LineN um berlnputStream , poto je sasvim beskorisna. Kao to je ovde pokazano, veoma je lako pratiti ukupan broj redova. Kada se ulazni tok iscrpe, metoda readLine( ) vraa null. Videete eksplicitan poziv metode cIose( ) za datoteku out. Ako ne pozovete metodu close( ) za izlaznu datoteku, moe se desiti da bateri ne budu ispranjeni i da se zato izgube podaci. Preica za pisanje u tekstualnu datoteku U Javi SE5, klasi PrintW riter dodat je pomoni konstruktor, da ne biste morali runo da radite ekoraju svaki put kada hoete da napravite tekstualnu datoteku i neto u nju upiete. Izmenio sam program OsnovePisanjaUDatoteku.java tako da koristi ovu preicu:
/ / : i o / P r e c i caZaPi s a n je u D a to te k u . ja v a im p o r t j a v a . i o . * ; p u b l i c c la s s P re c ic aZaP is anjeuDatoteku { s t a t i c S t r i n g d a to te k a = " P r e c ic a Z a P is a n je u D a t o t e k u . o u t " ; p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] args) throws IOException { BufferedReader u la z = new BufferedReader( new S trin g R e a de r( B a fe r is a n a U la z n a D a to te k a .r e a d ( "PrecicaZaPi s a n je u D a to t e k u . j a v a " ) ) ) ; / / Evo p r e ic e : P r i n t W r i t e r o u t = new P r i n t W r i t e r ( d a t o t e k a ) ; i n t brojRedova = 1; S t r i n g s; w h i l e ( ( s = u l a z . r e a d L i n e O ) != n u l l )

742

Misliti na Javi

o u t.p rin tln (b ro jR e d o v a + + + " + s); o u t.c lo s e (); / / P r i k a i u s k la d i t e n u d a t o t e k u : S yste m .o u t.p rin tln (B a fe ris a n a U la z n a D a to te k a .re a d (d a to te k a ));

}
} /* ( P o k r e n it e da b i s t e v i d e l i re z u lta t) * / / / : -

Baferisanje je i dalje tu, samo ne morate sami da ga radite. Naalost, za ostale uobiajene programerske zadatke nisam napisao preice, pa tipian U/I i dalje zahteva mnogo redundantnog teksta. Meutim, u ovoj knjizi se upotrebljava usluna metoda TextFile koja e biti definisana malo kasnije u ovom poglavlju - ona pojednostavljuje te uobiajene programerske zadatke. Veba 12: (3) Promenite vebu 8 tako da se otvara tekstualna datoteka u koju tekst moe i da se upisuje. Upiite u datoteku redove koji se nalaze u ulananoj listi (LinkedList) i njihove brojeve. (Nemojte koristiti klase LineNumber.) Veba 13: (3) Promenite vebu OsnovePisanjaUDatoteku.java tako da za praenje broja redova upotrebljava LineNumberReader. Obratite panju na to koliko je lake pratiti taj broj programski. Veba 14: (2) Na osnovu programa OsnovePisanjaUDatoteku.java, napiite program koji poredi performanse upisivanja u datoteku pri korienju baferisanog i nebaferisanog ulaza/izlaza.

uvanje i rekonstruisanje podataka


Klasa PrintVVriter formatira podatke u oblik koji ovek moe da ita. Meutim, da bi se podaci iskazali u obliku koji drugi tok moe da prepozna, koristi se D ataO utputStream za upisivanje, odnosno D atalnputStream za rekonstruisanje podataka. Naravno, ovi tokovi bi mogli da sadre bilo koje podatke, ali se ovde koristi datoteka baferisana i za upis i za itanje. Klase D ataO utputStream i D atalnputStream binarno su orijentisane, pa je potrebno koristiti klase tipa InputStream i OutputStream.
//: io/CuvanjelRekonstruisanjePodataka.java import java.io.*; public class CuvanjelRekonstruisanjePodataka { public static void m a i n ( S t r i n g [] args) throvvs IOException { Da ta Ou tp ut St re am out = new DataOutputStream( new BufferedOutputStream( new Fi1eO ut pu tS t r e a m ( " P o d a c i .tx t"))); o u t . w r i t e D o u b l e ( 3 . 14159); o u t. writeUTF("To je broj pi"); o u t . w r i t e D o u b l e ( 1 . 41 41 3); o u t .writeUTF("Kvadratni out.closeO; Dataln pu tS tr ea m ulaz = new DataInputStream( new BufferedInputStream( new F i l e I n p u tS tr ea m( "P oda ci.t x t" ) ) ) ; koren od 2");

Poglavlje 18: Javin ulazno-izlazni sistem

743

S y s te m .o u t.p rin tln (u la z .re a d D o u b le ()); / / Samo readUTF() p r a v i l n o p ro n a la z i / / Java-UTF S t r i n g : S y s te m .o u t.p rin tln (u la z .re a d U T F ()); S y s te m .o u t. p r i n t l n ( u l a z . readDouble( ) ) ; S y s te m .o u t.p rin tln (u la z .re a d U T F ());

}
} / * Is p is : 3.14159 To j e b r o j pi 1.41413 K v a d ra tn i koren od 2

* ///:Ako koristite D ataO utputStream za upisivanje podataka, Java garantuje da ete podatke moi tano da rekonstruiete pomou klase D atalnputStream , bez obzira na to na kojoj platformi se podaci upisuju i itaju. To je izuz.etno korisno, to e biti jasno svima koji su mnogo vremena proveli brinui o prilagoavanju programa razliitim platformama. Takav problem nestaje ako lava postoji na obe platforme.3 Kada koristite DataO utputStream , jedini nain pisanja znakovnog niza (ol^jekta tipa String) koji osigurava njegovu pouzdanu rekonstrukuju pomou ulaznog toka D atalnputStream jeste kodiranje UTF-8 koje se u ovom primeru obavlja metodama writeUTF( ) i readU T F (). UTF-8 je viebajtni format ija se duina kodiranih znakova menja u zavisnosti od upotrebljenog skupa znakova. Ako radite (iskljuivo ili preteno) sa ASCII znakovima (koji zauzimaju samo sedam bitova), korienjem Unicode zauzima se ogroman prostor i/ili propusni opseg; pa UTF-8 kodira ASCII znakove u jednom bajtu, a neASCII znakove u dva ili tri bajta. Sem toga, duina znakovnog niza se smeta u prva dva bajta UTF-8 znakovnog niza. Meutim, w riteU T F() i readU TF() upotrebljavaju posebnu varijantu kodiranja UTF-8 za Javu (detaljno opisanu u HTML dokumentaciji tih metoda na Webu), pa ako znakovni niz zapisan metodom writeUTF( ) itate ne-Java programom, morate sami za to pisati poseban kod, inae itanje nee biti ispravno. Kada koristite D ataO utputStream i metode w riteU T F() i readU TF(), moete meati znakovne nizove i ostale tipove podataka, jer e znakovni nizovi biti ispravno usldaditeni kao Unicode i Iako e se rekonstruisati pomou ulaznog toka D atalnputStream . Metoda w riteD ouble() stavlja broj tipa double u tok, a rekonstruie ga odgovarajua metoda readD ouble() - sline metode za itanje i upisivanje postoje i za druge tipove. Da bi metode za itanje radile kako treba, morate znati tanu poziciju podatka u toku, poto bi sauvan podatak tipa double mogao da se proita i kao jednostavan niz bajtova, kao tip char i sl. Zbog toga morate da imate fiksan format za podatke u atoteci ili se u njoj moraju uvati dodatne informacije koje ete anaiizirati da biste otkrili gde se nalaze podaci. Imajte u vidu da serijalizacija objekata ili XML (koji e biti opisani u nastavku poglavlja) mogu omoguiti laki nain skladitenja i rekonstrukcije sloenih struktura podataka.

Jezik X M L ta k o c o m o g u u je p re n o s in fo rm a c ija n e z a v is n o o d p la tfo r m e . D a b i se m o g a o k o ris titi, n a s v im p la tfo r m a m a n e m o ra p o s to ja ti Javina v irtu e ln a m a in a . X M L e b iti p re d s ta v lje n u n a sta v k u p o g la v lja .

744

Misliti na Javi

Veba 15: (4) Proitajte HTML okum entaciju klasa D ataO utputStream i D atalnputStream na Webu. Na osnovu programa CuvanjelPronalazenjePodataka.java, napiite program koji skladiti i zatim rekonstruie sve mogue tipove s kojima rade klase DataO utputStream i D atalnputStream . Dokaite da se svi tipovi ispravno skladite i rekonstruiu.

itanje i upisivanje datoteka s nasuminim pristupom


Korienje klase RandomAccessFile je kao kombinovanje tokova D atalnputStream i D ataO utputStream poto realizuje jednake interfejse. Osim toga, za nesekvencijalno kretanje po datoteci i promenu nasumino rasporeenih vrednosti na raspolaganju je metoda seek (). Preduslovza korienje klase RandomAccessFile jeste da znate (moete da izraunate) poloaj svih entiteta u datoteci, jer ete jedino tako moi ispravno s njima da radite. RandomAccessFile ima specifne metode za itanje i pisanje prostih tipova i UTF-8 znakovnih nizova. Evo jednog primera:
//: io /KoriscenjeRandomAccessFi 1e . j a v a im p o r t j a v a . i o . * ;

p u b l i c c la s s KoriscenjeRa ndomAccessFi1e { s t a t i c S t r i n g d a to te k a = " r t e s t . d a t " ; s t a t i c v o id d i s p l a y ( ) throws IO Exce ption { RandomAccessFi1e r f = new RandomAccessFile (datoteka, f o r ( i n t i = 0; i < 7; i+ + ) S y s t e m . o u t . p r i n t l n( "Vre dn o st " + i + " : " + r f . re a d D o u b le () ) ; S y s t e m . o u t . p r i n t l n ( r f . rea d U T F()) ; rf,c lo s e ();

"r");

}
p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] args) throws I 0 E x c e p t io n { RandomAccessFile r f = new RandomAccessFi1e ( d a t o t e k a , " r w " ) ; f o r ( i n t i = 0 ; i < 7 ; i+ + ) r f .w r it e D o u b le ( i*1.4 1 4 ); rf.w rite U T F ("K ra j d a to te k e "); r f ,c lo s e (); d is p la y (); r f = new R a ndom A ccessFile (datoteka, " r w " ) ; r f . se e k ( 5 * 8 ) ; rf.w rite D o u b le (4 7 .0 0 0 1 ) ; rf.c lo s e (); di s p la y ( ) ;

}
} / * Is p is : Vrednost 0: 0 .0 Vrednost 1: 1.414 Vrednost 2: 2.828 Vrednost 3: 4.242

Poglavlje 18: Javin ulazno-izlazni sistem

745

Vrednost 4: 5.656 Vrednost 5: 7.069999999999999 Vrednost 6: 8.484 Kra j d a to te k e Vrednost 0: 0.0 Vre d n ost 1: 1.414 Vrednost 2: 2.828 Vrednost 3 : 4.242 V re d n ost 4: 5.656 V re d n ost 5: 47.0001 V rednost 6: 8.484 Kra j d a to te k e

* ///:Metoda d isplay() otvara datoteku i prikazuje njenih sedam elemenata kao vrednosti tipa double. U metodi m ain( ), datoteka se pravi, otvara i menja. Poto svaki broj tipa double (uvek) zauzima osam bajtova, da biste metodom seek() pronali estu vrednost po redu, morate izraunati 5*8 i dobiete poziciju njenog poetnog bajta u datoteci. Kao to je ranije pomenuto, klasa RandomAccessFile je potpuno izdvojena od ostatka hijerahije U/I tokova, osim to realizuje interfejse D atalnput i DataOutput. Ona ne podrava dekorisanje, pa je zbog toga ne moete kombinovati ni s jednim aspektom potklasa InputStream i O utputStream . Morate pretpostaviti da je klasa RandomAccessFile ispravno baferisana, poto tu funkciju ne moete da joi dodate. ledina opcija koja vam stoji na raspolaganju jeste drugi argument konstruktora; datoteku s nasuminim pristupom moete da otvorite radi itanja (r) ili itanja i upisivanja (rw). Imajte u vidu da umesto klase RandomAccessFile moete koristiti nio datoteke preslikane u memoriju. Veba 16: (2) Proitajte HTM LdokumentacijuklaseRandomAccessFilenaW ebu. Na osnovu programa KoriscenjeRandomAccessFile.java, napiite program koji smeta i zatim rekonstruie sve mogue tipove s kojima radi klasa RandomAccessFile. Dokaite da se svi tipovi ispravno skladite i rekonstruiu.

Cevovodi
Klase PipedlnputStream , PipedO utputStream , PipedReader i PipedW riter pomenute su u ovom poglavlju samo ukratko. Nemojte na osnovu toga zakljuiti da nisu korisne. Njihova korisnost nije oigledna dok ne ponete da isprobavate vienitni rad, jer se cevovodi upotrebljavaju za komunikaciju izmeu niti. O ovome e, uz primer, biti rei u poglavlju Paralelno izvravanje.

Uslune klase za itanje i pisanje


U programiranju veoma esto treba uitati datoteku u memoriju, izmeniti je i zatim je ponovo iz memorije upisati u datoteku. Jedan od nedostataka Javine biblioteke za U/I jeste to to se mora pisati znatna koliina koda da bi se obavile te uobiajene operacije - u njoj nema jednostavnih pomonih funkcija koje bi to radiie umesto nas. Da stvari budu

746

Misliti na Javi

jo gore, zbog dekoratora je prilino teko zapamtiti kako se otvaraju datoteke. Dakle, bilo bi dobro dodati biblioteci pomone klase koje e te elementarne operacije raditi umesto nas. Java SE5 je klasi PrintW riter dodala prigodni konstruktor koji olakava otvaranje tekstualne datoteke za upisivanje. Meutim, ostalo je jo mnogo drugih poslova koji se esto ponavljaju, pa bi valjalo napisati kod za njih jednom zauvek. Sledi klasa TextFile koju smo ve u prethodnim primerima iz ove knjige koristili da pojednostavimo upisivanje i itanje datoteka. Ona sadri statine metode za itanje i upisivanje tekstualnih datoteka u obliku jednog znakovnog niza, a moete napraviti i TextFUe objekat koji redove te datoteke uva u listi ArrayList (pa tokom rada sa sadrajem datoteke imate na raspolaganju svu njenu funkcionalnost):
/ / : n e t / m i n d v i e w / u t i l / T e x t F i 1e . ja v a / / S t a t i n e f u n k c i j e za u p i s i v a n j e i i t a n j e t e k s t u a l n i h d a t o t e k a u o b l i k u / / jednog znakovnog n i z a , i t r e t i r a n j e d a t o t e k e kao je d n e A r r a y L i s t l i s t e . / / x je d n e A r r a y L i s t l i s t e . package n e t . m i n d v i e w . u t i l ; im p o rt j a v a . i o . * ; im p ort j a v a . u t i l . * ; p u b l i c c la s s T e x t F i l e extends A r r a y L i s t < S t r i n g > { / / U i t a j d a to te k u kao jedan znakovni n i z : p u b l i c s t a t i c S t r i n g r e a d ( S t r i n g im eDa toteke) { S t r i n g B u i l d e r sb = new S t r i n g B u i 1d e r ( ) ; try { B ufferedReader u la z = new Bu ffe red R e a d e r( new F ile R e ad e r( new F i l e ( i m e D a t o t e k e ) . g e t A b s o l u t e F i 1e ( ) ) ) ; try { S t r i n g s; w h i l e ( ( s = u l a z . r e a d L i n e ( ) ) != n u l l ) { sb.append(s); sb .a pp e n d ("\n ");

}
} fin a lly { u la z .c lo s e O ;

}
} c a tc h (IO E x c e p tio n e) { th ro w new R u n t im e E x c e p t io n ( e ) ;

}
retu rn s b . t o S t r in g ( ) ;

}
/ / U p i i c e lu d a to te k u je d n im pozivom metode: p u b l i c s t a t i c v o id w r i t e ( S t r i n g imeDato teke, S t r i n g t e k s t ) try { P r i n t W r i t e r o u t = new P r i n t W r i t e r ( new F ile ( im e D a t o te k e ) . g e t A b s o l u t e F i l e O ) ; try { o u t.p rin t(te k s t); } fin a lly { {

Poglavlje 18: Javin ulazno-izlazni sistem

747

o u t.c1o se ();

}
} c a tc h (IO E x c e p tio n e) { throw new R u n t im e E x c e p t io n ( e ) ;

} }
/ / U i t a j d a t o t e k u , i z d e l j e n u na r e i pomou b i l o koje g reg u la rn o g i z r a z a : p u b l i c T e x t F i l e ( S t r i n g imeDa toteke , S t r i n g podela) { s uper(A rrays.asLi s t( r e a d ( im e D a t o t e k e ) . s p lit ( p o d e la ) ) ) ; / / Zbog podele metodom s p lit ( ) p o m o u r e g u la rn o g i z r a z a , na / / p r v o j p o z i c i j i e sto o s t a j e prazan znakovni n i z : i f ( g e t (0) . e p u a l s C " 1) ) remove(O);

}
/ / Obino se i t a red po re d : p u b l i c T e x t F i l e ( S t r i n g imeDatoteke) t h is ( i m e D a t o t e k e , " \ n " ) ; {

}
p u b l i c v o id w r i t e ( S t r i n g im eDatoteke) { try { P r i n t W r i t e r o u t = new P r i n t W r i t e r ( new F i 1e ( i m e D a t o t e k e ) . g e t A b s o lu t e F i1e ( ) ) ; try { f o r ( S t r i n g stavka : t h i s ) o u t.p rin tln (s ta v k a ); ) fin a lly { o u t.c lo s e ();

}
} c a t c h (IO E xce p ti on e) { th ro w new R u n tim e E x c e p tio n ( e ) ;

} }
/ / Jednostavna p ro v e r a : p u b l i c s t a t i c v o id m ain ( S t r i n g [ ] args) { S t r i n g d a to te k a = r e a d ( " T e x t F i 1e . j a v a " ) ; w r i t e ( " t e s t . t x t " , da tote ka ); T e x t F i l e t e k s t = new T e x t F i l e C t e s t . t x t " ) ; te k s t.w rite C te s t2 .tx t"); / / Podela na je d i n s t v e n e r e i u ureenoj l i s t i : T re e S e t< S trin g > r e c c i = new T re e S e t< S tr in g > ( new T e x t F i 1e ( " T e x t F i 1e . j a v a " , " \ \ W + " ) ) ; / / P r i k a i r e i s v e l i k i m poetnim slovom: S y s t e m . o u t . p r i n t l n ( r e c c i . headSet( " a " ) ) ;

}
} / * Is p is : [0 , A r r a y L i s t , A r r a y s , Break, Buffe red Re a d e r, B u f f e r e d W r i t e r , Clean, F i l e , F ile R e a d e r, F i l e W r i t e r , IO Exce p tio n , Jednostavn a, Obino, O u tp ut, P r i k a i , P r i n t W r i t e r , Read, R e g u la r, Ru ntim eExce ptio n, S t a t i c , S t r i n g , S t r i n g B u i l d e r , System, T e x t F i l e , T o o ls , Tre eSet, W , W r it e ]

* ///:-

748

Misliti na Javi

Metoda re a d () objektu tipa StringBuilder dodaje svaki red i zatim znak za prelazak u novi red, jer se oni uklanjaju tokom itanja. Potom ona kao svoj rezultat vraa znakovni niz koji sadri celu datoteku. Metoda w rite () otvara tekstualni znakovni niz i upisuje ga u datoteku. Vodite rauna o tome da svaki program koji otvara neku datoteku uva njen poziv metode clo se() unutar bloka finaUy, da bi datoteka bila zajemeno zatvorena. Konstruktor pretvara datoteku u znakovni niz pomou metode re a d (), zatim poziva S tring.split() da bi taj rezultat podelio na redove (uz znakove za prelazak u novi red kao graninike; ako ovu klasu esto upotrebljavate, mogli biste da prepravite taj konstruktor tako da bude delotvorniji). Naalost, ne postoji odgovarajua metoda ,,spajanja redova u znakovni niz, pa nestatinom m etodom w rite () moramo da ispisujemo red po red runo. Poto je ova klasa napisana zato da bi do krajnosti pojednostavila postupak itanja i pisanja datoteke, svi IOException izuzeci pretvoreni su u RuntimeException izuzetke, kako korisnik ne bi morao da upotrebljava try-catch blokove. Meutim, moda ete morati da napiete drugu verziju koja IOException izuzetke prosleuje pozivaocu. U metodi m a in () obavlja se elementarna provera ispravnosti rada spomenutih metoda. Nije trebalo mnogo koda da bi se napisala ova usluna klasa, a ona e nam utedeti mnogo vremena i olakati ivot, u ta ete se uveriti u nekima od narednih primera u ovom poglavlju. Problem s itanjem tekstualnih datoteka moe se reiti i na drugi nain, klasom java.util.Scanner uvedenom u Javu SE5. Medutim, ona slui samo za itanje datoteka, ne i za pisanje u njih. Ta alatka (koja nije stavljena u paket java.io) prvenstveno je namenjena za pravljenje analizatora programskih jezika (engl. scanners) ili malih jezika. Veba 17: (4) Pomou klase TextFile i mape M ap<Character,Integer> napiite program koji prebrojava znakove u datoteci. (Dakle, ako u nekoj datoteci ima 12 primeraka slova a, 12 treba da sadri objekat tipa Integer pridruen objektu tipa Character koji sari a u mapi). Veba 18: (1) Izmenite program TextFile.java tako da izuzetke IOExceptions prosleduje pozivaocu.

itanje binarnih datoteka


Slino klasi TextFile.java, i ova klasa pojednostavljuje postupak itanja binarne datoteke:
/ / : n e t/m in d v ie w /u til/B in a rn a D a to te k a .ja v a / / Usluna k la s a za i t a n j e d a t o t e k e u binarnom o b l i k u . package n e t . m i n d v i e w . u t i l ; im p o r t j a v a . i o . * ; p u b l i c c la s s Bin a rn a D a to te ka { p u b l i c s t a t i c b y t e [ ] r e a d ( F i l e bDatoteka) throws IO Exceptio n{ B u f f e r e d ln p u t S t r e a m bD = new B u ffe r e d In p u tS tr e a m ( new F i 1e ln p u tS t r e a m ( b D a t o t e k a ) ) ; try {

Poglavlje 18: Javin ulazno-izlazni sistem

749

b y t e [ ] data = new b y t e [ b D . a v a i l a b l e ( ) ] ; b D .re a d (d ata ); r e t u r n d a ta ; } fin a lly { b D .c lo s e ();

} }
p u b lic s t a t ic b yte [ ] r e a d ( S t r i n g bDa toteka) throws IOExce ptio n { r e t u r n read(new F ile ( b D a t o t e k a ) . g e t A b s o l u t e F i l e O ) ;

} III-Jedna preldopljena metoda prima argument tipa File; druga prima argument tipa String, to je ime te datoteke. Obe vraaju rezultujui niz tipa byte. Metodom available() pravi se niz odgovarajue veliine, a ba ova verzija preklopljene metode re a d () popunjava taj niz. Veba 19: (2) Pomou klase BinarnaDatoteka i mape Map<Byte,Integer> napiite program koji prebrojava razliite bajtove u datoteci. Veba 20: (4) Pomou metode Direktorijum .prolazak( ) i klase BinarnaDatoteka, dokaite da sve .class datoteke u stablu direktorijuma poinju heksadecimalnim kodovima znakova CAFEBABF.

Standardni U/l tokovi


Pojam standardni U/I to k odnosi se na Unixov koncept koji je u ovom ili onom obliku reprodukovan u Windowsu i mnogim drugim operativnim sistemima, a oznaava jedinst\'en tok informacija koji koristi program. Svi ulazni podaci programa stiu iz standardnog ulaznog toka, svi izlazni podaci alju se na standardni izlazni tok, a sve poruke 0 grekama alju se na standardni tokzagre'ske. Standardni ulazno/izlazni tokovi su znaajni zato to omoguuju lako povezivanje programa, a standardni izlazni tok jednog programa moe da postane standardni ulazni tok drugog programa. To je snana alatka.

itanje standardnog uiaznog toka


Oslanjajui se na standardan U/I model, Java obezbeduje tokove System.in, System.out 1 System.err. U celoj knjizi smo upisivali u standardan izlazni tok pomou objekta System .out koji je ve omotan kao objekat klase PrintStream. System.err na slian nain biva objekat klase PrintStream , a System.in je tipa InputStream , bez omotavanja. To znai sledee: za razlikti od objekata System.out i System.err koje moete da koristite odmah, objekat System.in mora da se omota da biste neto itali iz njega. Ulaz, po pravilu, itate red po red, koristei metodu readL ine(), pa System.in treba da omotate u BufferedReader. Da biste to uradili, morate da konvertujete System.in u Reader pomou klase InputStreamReader. Evo primera koji samo ponavlja svaki recl koji unesete:

750

Misliti na Javi

/ / : u i/E h o .ja v a / / Kako se i t a sta n d a rd n i u la z n i t o k . / / {RunByHand} im p o r t j a v a . i o . * ; p u b l i c c la s s Eho { p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] args) throws IO Exce ptio n { Buffe redReader s t d i n = new B u fferedReader{ new InputStre am Reader(S ystem.i n ) ) ; S t r i n g s; w h i l e ( ( s = s t d i n . r e a d L i n e ( ) ) != n u l l && s . 1e n g th ( ) S y s te m .o u t.p rin tln (s ); / / Program zavra va prazan red i l i C t r l - Z

!= 0)

> III-Razlog za specifikaciju izuzetka u metodi m a in () jeste to to metoda readL ine() moe da generie izuzetak IOException. Vodite rauna o tome da System.in obino treba da bude baferisan, kao i veina tokova. Veba 21: (1) Napiite program koji prima standardan ulaz, pretvara sva primljena slova u velika, a zatim rezultate alje na standardan izlaz. Preusmerite u ovaj program sadraj neke datoteke. (Postupak preusmeravanja se menja u zavisnosti od operativnog sistema.)

Omotavanje toka System.out u PrintWriter


Tok System.out je tipa PrintStream koji nasleduje klasu OutputStream . Klasa PrintW riter ima konstruktor iji je argument objekat klase OutputStream . Stoga tim konstruktorom moete da pretvorite System.out u PrintW riter:
/ / : u i/O m otavanje System O ut.ja va / / Omotavanje System.out u P r i n t W r i t e r . im p o rt j a v a . i o . * ; p u b l i c c la s s OmotavanjeSystemOut { p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] args) { P r i n t W r i t e r o u t = new P r i n t W r i t e r ( S y s t e m . o u t , t r u e ) ; o u t.p rin tln ("Z d ra v o , s v e te ");

}
} / * Is p is : Zdravo , sv ete

* ///-.-

Vano je da se koristi verzija konstruktora 7,a klasu PrintW riter koja ima dva argumenta i da drugi argument bude true, da bi se omoguilo automatsko pranjenje izlaznih bafera, inae se izlaz nee prikazivati red po red.

Poglavlje 18: Javin ulazno-izlazni sistem

751

Preusmeravanje standardnog ulaza/izlaza


Javina klasa System omoguuje preusmeravanje standardnih ulaznih tokova, izlaznih tokova i tokova za greke, pozivanjem jednostavnih statikih metoda: setln(InputStream ) setO ut(PrintStream ) setErr(PrintStream ) Preusmeravanje izlaza je naroito korisno ako odjednom ponete da ispisujete veliku koliinu izlaznih podataka na konzolu, pa se oni kreu bre nego to moete da ih proitate.4 Preusmeravanje ulaza je korisno za konzolne programe pom ou kojih elite vie puta da testirate odreenu sekvencu ulaznih podataka. Evo jednostavnog prim era koji prikazuje korienje ovih metoda:
/ / : u i/ P r e u s m e r a v a n je . ja v a / / P r ik a z u je standardno U / I preusmeravanje. im p o r t j a v a . i o . * ; c l a s s Preusmeravanje { p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] args) thro ws IO Exception { P r in t S t r e a m ko nzola = Syste m .o ut; B u ff e r e d ln p u t S t r e a m i n = new B u ffe r e d In p u tS tr e a m ( new F i 1e I n p u tS tre a m (" P re u s m e ra v a n je .j a v a " ) ) ; P r in t S t r e a m o u t = new P r in tS tr e a m ( new B u fferedO utputStre am ( new F i 1e O u t p u t S t r e a m ( " t e s t . o u t " ) ) ) ; S y s te m .s e tln (in ); S y s te m .s e tO u t( o u t ) ; S y s te m .s e tE rr(o u t); Bu fferedReader b r = new BufferedReader( new I n p u tS tre a m R e a d e r(S y s te m .in )) ; S t r i n g s; w h i l e ( ( s = b r . r e a d L i n e ( ) ) != n u l l ) S y s te m .o u t.p rin tln (s ); o u t . c l o s e ( ) ; / / Ne z a b o r a v it e ovo! S y s t e m . s e tO u t ( k o n z o la ) ;

} ///:-

Ovaj program povezuje datoteku na standardni ulaz i preusmerava standardni izlaz i standardni tok za greke u drugu datoteku. Obratite panju na to da se na poetku programa skladiti referenca prvobitnog System.out objekta i da se na kraju standarni izlaz vraa tom objektu. U/I preusmeravanje radi s tokovima bajtova, a ne s tokovima znakova, pa se koriste klase InputStream i O utputStream umesto klasa Reader i VVriter.
1 II p o g la v lju Grafika korisnika okruenja p re d s ta v lje n o je b o lje re e n je to g p ro b le m a - g ra fi k i p ro g ra m s p o lje m za te k st.

752

Misliti na Javi

Upravljanje procesima
esto ete biti u prilici da iz Jave pokreete druge programe operativnog sistema i da upravljate ulazom i izlazom tih programa. Javina biblioteka ima klase za takve operacije. Uobiajen zadatak je pokretanje programa i slanje rezultujueg izlaza na konzolu. U ovom odeljku napisaemo uslunu klasu koja e pojednostaviti taj zadatak. U ovoj uslunoj klasi mogu nastati dve vrste greaka: uobiajene greke koje prouzrokuju izuzetke - za njih emo samo ponovo generisati RuntimeException - i greke pri izvravanju samog procesa. Te greke emo prijavljivati posebnim izuzetkom:
/ / : n e t/m in d v ie w /u til/Iz u z e ta k O S Iz v rs e n ja .ja v a package n e t . m i n d v i e w . u t i l ; p u b l i c c la s s I z u z e ta k O S Iz v rs e n ja extend s Runtim eException { p u b l i c I z u z e t a k O S I z v r s e n ja ( S t r in g r a z l o g ) { s u p e r ( r a z l o g ) ; }

1 ///= Da biste pokrenuli program, metodi OSIzvrsenje.komanda( ) prosledite komandni znakovni niz to jest komandu koju biste upisali da pokreete program s konzole. Ta komanda se prosleduje konstruktoru klase java.lang.ProcessBuilder (kojoj je potrebno da ga dobije u obliku sekvence String objekata), i tako nastaje rezultujui objekat tipa ProcessBuilder:
/ / : n e t / m i n a v i e w / u t i 1/ O S I z v r s e n j e . j a v a / / Iz v r a v a komandu o p e ra t iv n o g s is te m a i i z l a z a l j e na k onzolu . package n e t . m i n d v i e w . u t i l ; im p o rt j a v a . i o . * ; p u b l i c c la s s O SIzvrsenje { p u b l i c s t a t i c v o id komanda(String komanda) { boolean greska = f a l s e ; try { Process proces = new Pr'ocessBui 1der(komanda. spl i t (" " ) ) . p o c e t a k ( ) ; BufferedReader r e z u l t a t i = new Buffe red Re a d e r( new I n p u t S t r e a m R e a d e r ( p r o c e s , g e t ln p u t s t r e a m ( ) ) ) ; S t r i n g s; w h i l e ( ( s = r e z u l t a t i . r e a d L i n e ( ) ) != n u l l ) S y s te m .o u t.p rin tln (s ); BufferedReader greske = new Buffe red Re a d e r( new I n p u t S t r e a m R e a d e r ( p r o c e s . g e t E r r o r S t r e a m ( ) ) ) ; / / Ako ima problema, p r i j a v i g re ke i pozivajue m / / procesu v r a t i v re d n o s t r a z l i i t u od n u le : w h i l e ( ( s = g r e s k e . r e a d L i n e O ) != nul 1) { S y s te m .g re s k a .p r i n t l n ( s ) ; greska = t r u e ;

}
} c a t c h (E x c e p t io n e) { / / Zbog Windowsa 2000, k o j i g e n e r i e / / iz u z e t a k za podrazumevani s a d r a j komandne l i n i j e :

Poglavlje 18: Javin ulazno-izlazni sistem

753

i f ( ! komanda. s t a rt s W i t h ( "CMD / C " ) ) komanda("CMD /C " + komanda); el se throw new R u n t im e E x c e p t io n ( e ) ;

}
i f (greska) th ro w new I z u z e ta k O S Iz v rs e n ja (" G r e k e tokom i z v r a v a n ja 1 1+ komanda);

} III-Metodu getIn p u tS tream () pozivate da bi hvatala standardni izlaz programa tokom izvravanja. To se ini zato to objekat tipa InputStream moemo itati. Rezultati programa stiu red po red, pa ih itamo metodom readLine(). Kod nas se redovi samo ispisuju, ali biste mogli da ih hvatate i vraate iz metode k o m an d a(). Greke u programu alju se na standardni tok za greke i hvataju pozivanjem metode getErrorStream ( ). Ako ima greaka, one se ispisuju i generie se izuzetak IzuzetakOSIzvrsenja da bi pozivajui program reio problem. Lvo primera kako se koristi OSIzvrsenje:
/ / ; u i/ P r i m e r O S I z v r s e n j a . ja v a / / P r ik a z u je standardno preusmeravanje U / I . im p o r t n e t . m i n d v i e w . u t i l . * ; p u b l i c c la s s Prim e rO S Izv rse n ja { p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] a rg s ) { O SIz vrs e n je .ko m a n da ("ja v ap P r im e r O S I z v r s e n ja " ) ;

}
} / * Is p is : Compiled from " P r im e r O S I z v r s e n j a . ja v a " p u b l i c c la s s Prim e rO S Iz vrs e n ja extends j a v a . l a n g . O b j e c t f p u b l i c P r im e r O S I z v r s e n ja ( ) ; p u b l i c s t a t i c v o id m a i n ( j a v a . 1a n g . S t r i n g [ ] ) ;

} * ///:Ovde je za povratno prevodenje programa upotrebljen dekompilator javap (koji se isporuuje uz JDK). Veba 22: (5) Izmenite program OSIzvrsenje.java tako da rezultate izvravanja programa vraa u obliku liste znakovnih nizova, umesto da ih ispisuje u standardni izlazni tok. Pokaite kako se koristi nova verzija te uslune klase.

Nove U/l klase


Javina ,,nova U/I biblioteka, uvedena u JDK 1.4 u paketima java.nio.*, ima samo jedan cilj: brzinu. U stvari, ,,stari U/I paketi su ponovo realizovani pomou nio klasa da bi se iskoristilo to ubrzanje, pa od njega imate koristi ak i kada u svojim programima ne koristite eksplicitno nio klase. Ubrzanje se osea i pri U/I operacijama sa datotekama, to je tema ovog poglavlja, i pri mrenom ulazu/izlazu, obradenom u knjizi Thinking in Enterprise Java.

754

Misliti na Javi

Ubrzanje je posledica korienja kanala i bfl/era-struktura koje su blie nainu na koji sam operativni sistem obavlja U/I operacije. Po analogiji s rudnikom uglja, kanal bi bio rudnik koji sadri ilu uglja (podatke), a bafer su kola koja se alju u rudnik. Kola se vraaju puna uglja, i on se vadi iz njih, a ne neposredno iz rudnika. Dakle, s kanalom nemate posla neposredno; radite s baferom i njega aljete u kanal. Kanal uzima podatke iz bafera ili ih stavlja u bafer. Jedina vrsta bafera za komunikaciju neposredno s kanalom jeste ByteBuffer - tj. bafer koji uva sirove bajtove. Ako proitate HTML dokumentaciju ldase java.nio.ByteBuffer, videete da je prilino elementarna: njenom konstruktoru treba saoptiti koliko memorijskog prostora da rezervie, i postoje metode za umetanje i vaenje podataka, bilo u sirovom obliku bajtova bilo u obliku prostih tipova. Ali nema naina da umetnete ili izvadite objekat, pa ak ni znakovni niz. Sve je zadrano na prilino niskom nivou, ba zato to se u veini operativnih sistema time dobija delotvornije preslikavanje. Tri klase ,,starog U/I izmenjene su tako da proizvode FileChannel: FilelnputStream, FileOutputStream, i, kako za itanje tako i za pisanje, RandomAccessFile. Obratite panju na to da se radi o tokovima za rukovanje bajtovima, to je u skladu s niskim nivoom nio klasa. Znakovno orijentisane klase Reader i W riter ne proizvode kanale, ali klasa java.nio.channels.Channels ima uslune metode koje od kanala proizvode objekte tipova Reader i Writer. Evo jednostavnog primera u kojem sve tri vrste tokova proizvode kanale u koje se moe pisati, itati/pisati odnosno itati:
/ ' / : u i/ D a j K a n a l . ja v a / / P r a v l j e n j e kanala od tokova im p o rt j a v a . n i o . * ; im p o rt j a v a . n i o . c h a n n e l s . * ; im p o r t j a v a . i o . * ; p u b l i c c la s s DajKanal { p r i v a t e s t a t i c f i n a l i n t VELBAF = 1024; p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] a rg s) throws Exce p tio n { / / I s p i i datotekU: FileChannel f c = new F i 1e O u tp u tS tre a m ("p o d a c i. t x t " ) .getChannel ( ) ; f c . w r i t e ( B y t e B u f f e r . w r a p ( Nekakav t e k s t " . g e t B y t e s ( ) ) ) ; fc .c lo s e (); / / Dodaj na k r a j d a to te k e : fc = new RandomAccessFi1e ( " p o d a c i . t x t " , " r w " ) .getChannel ( ) ; f c . p o s i t i o n ( f c . s i z e ( ) ) ; / / Prei na k r a j f c . w r i te (B yte B u ffer.w ra p("Jo m alo",g e tB yte s( ) ) ) ; fc .c lo s e (); / / i t a j d a to te k u : f c = new F i l e I n p u t S t r e a m ( " p o d a c i . t x t " ) . g e t C h a n n e l ( ) ; B y t e B u f f e r b a f = B y t e B u f f e r . a l 1ocate(VELBAF); fc .re a d (b a f); baf . f l i p ( ) ; w h ile (b a f.h a s R e m a in i n g ( ) )

Poglavlje 18: Javin ulazno-izlazni sistem

755

S y s te m .o u t.p rin t((c h a r)b a f.g e t());

}
} / * Is p is : Nekakav t e k s t Jo malo

* ///:Metoda getC hannel() za svaku prikazanu klasu tokova pravi objekat tipa FileChannel. Kanal je prilino jednostavan: moe m u se predati objekat tipa ByteBuffer radi itanja ili pisanja, i delove datoteke moete zakljuati, odnosno ostvariti iskljuivo pravo pristupanja (to e biti opisano u nastavku). Jedan od naina smetanja bajtova u ByteBuffer jeste da ih neposredno umetnete u njega nekom od put metoda, i tako upiete jedan ili vie bajtova ili vrednosti prostih tipova. Meutim, iz programa vidite da metodom w ra p () moete postojei niz tipa byte da omotate objektom tipa ByteBuffer. Kada to uradite, pripadajui niz se ne kopira, nego koristi kao skladite za generisani ByteBuffer. Kaemo da je taj ByteBuffer poduprt nizom. Datoteku podaci.txt ponovo otvara klasa RandomAccessFile. Vodite rauna o tome da FileChannel moete premetati po datoteci; ovde sam ga premestio na kraj, tako da se novi upisi dodaju na kraj postojeih (engl. append). Kada primenjujete pristup samo za itanje, memorijski prostor za ByteBuffer morate eksplicitno dodeliti statinom metodom allo cate(). Svrha nio klasa je brzo premetanje velikih koliina podataka, pa nije nevano koju veliinu bafera ByteBuffer zadate - zapravo, ovde e vam 1 K verovatno mnogo manje znaiti nego inae (moraete da eksperimentiete s tekuom aplikacijom da biste odredili najbolju veliinu bafera). U potrazi za jo veom brzinom, mogli biste metodom allocateD irect() umesto metodom allocate() napraviti direktan bafer koji je jo tenje spregnut sa operativnim sistemom. Meutim, reijski trokovi takve dodele su vei, a njena realizacija se menja u zavisnosti od operativnog sistema, pa ete opet morati da eksperimentiete s tekuom aplikacijom da biste odredili donose li vam direktni baferi ikakvo ubrzanje. Nakon to pozovete read( ) da biste kanalu FileChannel saoptili da upie bajtove u ByteBuffer, morate pozvati metodu flip( ) za taj bafer kako biste mu saoptili da se pripremi za vaenje bajtova iz njega. (To jeste malo nezgrapno, ali ne zaboravite da se radi o veoma niskom nivou i da se tako postie maksimalna brzina). A kada bismo taj bafer upotrebili za jo neku operaciju re a d (), pre svakog uitavanja saraja u bafer morali bismo da pozovemo metodu clear( ) kao pripremu. To vidite u ovom jednostavnom programu za kopiranje datoteka:
/ / : u i/ K o p ir a n je K a n a lo m . ja v a / / K o p ir a n je d a to te k e pomou kanala i b a f e r a / / { A rg s: K o p ir a n je K a n alom .ja v a t e s t . t x t } im p o r t j a v a . n i o . * ; im p o r t j a v a . n i o . c h a n n e l s . * ; im p o rt j a v a . i o . * ; p u b l i c c la s s KopiranjeKanalom { p r i v a t e s t a t i c f i n a l i n t VELBAF = 1024; p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] a rg s) throws Exce p tio n {

756

Misliti na Javi

if(a rg s .le n g th

!= 2) {

S y s te m .o u t.p rin tln ("a rg u m e n ti: d a tiz v o r d a to d re d is te "); S y s te m .e x it(l);

}
FileCh annel u la z = new F i l e I n p u t S t r e a m ( a r g s [ 0 ] ) . g e t C h a n n e l ( ) , i z l a z = new F i l e O u t p u t S t r e a m ( a r g s [ l ] ) . g e t C h a n n e l ( ) ; B y t e B u f f e r b a f e r = B y t e B u f f e r . a l lo c a t e ( V E L B A F ) ; w h i l e ( u l a z . r e a d ( b a f e r ) != -1) { b a f e r . f l i p ( ) ; / / P rip re m i za p is a n je iz la z .w r ite (b a fe r) ; b a f e r . c l e a r ( ) ; / / Prip rem i za i t a n j e

} } } ///= Jedan FileChannel otvorili smo za itanje, a drugi za pisanje. Objektu tipa ByteBuffer dodeljuje se memorija, i kada metoda FileChannel.read( ) vrati -1 (nema sumnje, ta vrednost je zaostatak iz Unixa i C-a), to znai da smo oli do kraja ulaza. Nakon svakog poziva metode re a d () koja u bafer smeta podatke, flip () priprema bafer da bi metoda w rite () mogla da vadi podatke iz njega. Podaci ostaju u baferu i nakon itanja metodom w rite (), i tek metoda c le a r() resetuje sve interne pokazivae tako da bafer postaje spreman da od metode re a d () primi nove podatke. Prethodni program nije idealan za obavljanje te vrste operacija. Postoje posebne metode transferT o() i tran sferF ro m () za neposredno povezivanje jednog kanala s drugim:
/ / : u i / T r a n s f e r T o . ja v a / / P o ve z iv a n je kanala metodom t r a n s f e r T o ( ) / / { A r g s : T r a n s f e r T o . ja v a T r a n s f e r T o . t x t } im p o r t j a v a . n i o . c h a n n e l s . * ; im p o r t j a v a . i o . * ; p u b l i c c la s s T ra n s fe rT o { p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] a rg s ) throws Exce p tio n { i f ( a r g s . l e n g t h != 2) { S y s te m .o u t.p rin tln ("a rg u m e n ti: d a tiz v o r d a to d re d is te "); S y s te m .e x it(l);

}
F i 1eChannel u la z = new F ile l n p u t S t r e a m ( a r g s ) . g e t C h a n n e l ( ) , i z l a z = new F i 1e O u t p u t S tr e a m ( a rg s ) .g e tC h a n n e l( ) ; u la z .tra n s fe rT o (0 , u l a z . s i z e ( ) , i z l a z ) ;

//III:
// i z l a z . t r a n s f e r F r o m ( u l a z , 0, u l a z . s i z e O ) ;

} } ///:Ovako neto retko e vam trebati, ali dobro je to znati.

Poglavlje 18: Javin ulazno-izlazni sistem

757

Konverzija podataka
Ako se osvrnete na program DajKanal.java, videete da prilikom ispisivanja datoteke podatke vadimo bajt po bajt, pa svaki byte zasebno pretvaramo u char. To je primitivno - proitajte dokumentaciju klase java.nio.CharBuffer i videete da se u njoj za metodu to S trin g () kae: Vraa znakovni niz koji sadri znakove bafera. Poto se ByteBuffer nakon obrade metodom asC harB uffer() moe smatrati baferom CharBuffer, zato ne bismo upotrebili njega? Kao to vidite iz prvog reda ispisa rezultata sledeeg programa, to ipak ne radi kako smo oekivali:
/ / : u i/B a fe rU T e kst.ja va / / K o n v e r z ija t e k s t a u B y t e B u f f e r i i z njega im p o r t j a v a . n i o . * ; im p o r t j a v a . n i o . c h a n n e l s . * ; im p o r t j a v a . n i o . c h a r s e t . * ; im p o r t j a v a . i o . * ; p u b l i c c la s s BaferLITekst { p r i v a t e s t a t i c f i n a l i n t VELBAF = 1024; p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] args) throws Exce p tio n ( File Channel f c = new Fi 1e 0 u tp u t S t r e a m ( " p o d a c i2 . t x t " ) . g e t C h a n n e l ( ) ; fc .w rite (B y te B u ffe r.w ra p ("N e k i te k s t".g e tB y te s ( ) ) ) ; fc .c lo s e O ; f c = new Fi l e I n p u t S t r e a m C p o d a c i 2 . t x t " ) .getChannel ( ) ; B y t e B u f f e r b a f = B y t e B u f f e r . a l lo c a t e ( V E L B A F ) ; fc .re a d (b a f); b a f. f l ip ( ) ; / / Ne r a d i : S ystem .out. p ri n tln ( b a f.a s C h a rB u ffe r ( ) ) ; / / D e k o d ira j po podrazumevanoj emi k o d ir a n ja ovog sis tem a: b a f.re w in d (); S t r i n g k o d i r a n j e = S y s t e m . g e t P r o p e r t y ( " f i 1e . e n c o d i n g " ) ; S y s t e m . o u t . p r i n t l n ( " D e k o d i r a n o po emi " + k o d i r a n j e + : " + C h a r s e t . forName(kodi r a n j e ) . d e c o d e ( b a f ) ) ; / / I I i da kodiramo po emi k o ja se moe o d ta m p a ti: f c = new F i 1e 0 u tp u tS tr e a m ( " p o d a c i2 . t x t " ) . g e t C h a n n e l ( ) ; f c . w r i t e ( B y t e B u f f e r . w r a p ( " N e k i t e k s t " . g e tB y te s ("U T F -1 6 B E ")) ) ; fc .c lo s e O ; / / Sada ponovo p o k u a jte da i t a t e : f c = new Fi 1e I n p u t S t r e a m C p o d a c i 2 . t x t " ) .getChannel ( ) ; b a f.c le a r(); fc .re a d (b a f); b a f. f l ip ( ) ; S y s te m .o u t.p rin tln (b a f.a s C h a rB u ffe r()); / / Pisaemo kroz C h a rB u ffe r : f c = new F ile 0 u t p u t S t r e a m C p o d a c i 2 . t x t " ) .getChannel ( ) ; b a f = B y t e B u f f e r . a l l o c a t e ( 2 4 ) ; / / Vie nego t o j e potrebno b a f . asCharB uff e r ( ) . p u t ( " Neki t e k s t " ) ; f c . w r ite (b a f);

758

Misliti na Javi

fc .c lo s e (); / / ita n je i p rik a z iv a n je : f c = new F i l e I n p u t S t r e a m ( " p o d a c i 2 . t x t ) . g e t C h a n n e l( ) ; b a f.c le a rO ; fc .re a d (b a f); b a f.fl ip ( ) ; S y s te m .o u t.p rin tln (b a f .a s C h a rB u ffe rO );

)
} /* Is p is :

????
Dekodirano po emi Cpl252: Neki t e k s t Neki t e k s t Neki t e k s t

* ///:Bafer sadri sirove bajtove, i da bismo njih pretvorili u znakove, moramo ih kodirati prilikom stavljanja unutra (da bi neto znaili kada ih izvadimo iz bafera) ili dekodirati prilikom vaenja iz bafera. To se moe postii klasom java.nio.charset.Charset koja obuhvata alatke za kodiranje po raznim emama:
/ / : u i/D o s tu pn e S em e K o d ir a nja.ja v a / / P r ik a z u je eme k o d i r a n j a i n ji h o v e a l i j a s e im p o r t j a v a . n i o . c h a r s e t . * ; im p o r t j a v a . u t i l . * ; im p o r t s t a t i c n e t . m i n d v i e w . u t i l . P r i n t . * ; p u b l i c c la s s DostupneSemeKodir a n ja { p u b l i c s t a t i c v o id m a in ( S t r i n g [] a r g s ) { S o r te dM a p < S tring ,C h a rs e t> semeKodiranja = Ch arset . a v a i l a b l e C h a r s e t s O ; I t e r a t o r < S t r i n g > i t = semeKodir a n j a . ke y S e t( ) . i t e r a t o r ( ) ; w h i1e ( i t ,h a s N e x t( ) ) { S t r i n g imeSemeKodiranja = i t . n e x t ( ) ; p r i ntnb(imeSemeKodi r a n j a ) ; Ite ra to r a lija s i = semeKodi r a n j a . g e t (imeSemeKodi r a n j a ) . a l i a s e s ( ) . i t e r a t o r ( ) ; i f ( a l i j a s i .h a s N e x t( ) ) p rin tn b (": " ); w h i1e ( a l i j a s i . hasN ext( ) ) { p rin tn b (a lija s i,n e x t()); i f ( a l i j a s i .h a s N e x t( ) ) p rin tn b C ', ");

}
pri nt ();

} }
} / * Is p is : B ig 5 : csBig5 Big5-HKSCS: b i g 5 - h k s c s , b ig 5 h k , b i g 5 - h k s c s : u n ic o d e 3 .0 , big 5 h ks cs , Big5_HKSCS

Poglavfje 18: Javin ulazno-izlazni sistem

759

EUC-JP: e u is , x-e u p , csEUCPkdFmtjapanese, eup, Extended_UNIX_Code_Packed_Format_for_Japanese, x - e u c - j p , euc_jp EUC-KR: ksc5601, 5601, ksc5601_1987, ksc_5601, ksc5601-1987, euc_kr, ks_c_5601-1987, e u c k r, csEUCKR GB18030: gbl8030-2000 GB2312: gb2312-1980, gb2312, EUC_CN, gb2312-80, e u c -c n , euccn, x-EUC-CN GBK: windows-936, CP936

* ///:Vratimo se na BaferUTekst.java. Ako bafer ,,premotate metodom rew in d () (da biste se vratili na poetak podataka) i zatim za dekodiranje podataka metodom decode( ) upotrebite podrazumevanu emu kodiranja te platforme, dobiete CharBuffer koji se lepo ispisuje na konzoli. Za pronalaenje podrazumevane eme kodiranja pozovite metodu System.getProperty("file.encoding") koja proizvodi znakovni niz s imenom te eme kodiranja. Prosledite to metodi Charset.forN am e() i dobiete objekat tipa Charset kojim se taj znakovni niz moe dekodirati. Druga mogunost je da (metodom encode()) kodirate po emi kodiranja koja e dati rezultat koji se moe tampati nakon itanja datoteke, kao to vidite u treem delu programa BaferUTekst.java. Tu je za pisanje teksta u datoteku upotrebljena ema kodirania UTF-16BE, pa nakon itanja samo morate da je konvertujete u CharBuffer koji e dati oekivani tekst. Najzad, vidite ta se deava ako piete u ByteBuffer preko CharBuffer bafera (o tome e jo biti rei). Baferu ByteBuffer dodelili smo 24 bajta. Poto svaki znak (char) zahteva dva bajta, to je dovoljno za 12 znakova, ali Neki tekst ih ima samo 10. Preostali bajtovi s nulama ipak se vide u prikazu CharBuffer bafera koji proizvodi njegova metoda to S trin g (), kao to vidite iz ispisa rezultata. Veba 23: (6) Napravite i ispitajte uslunu metodu koja tampa sadraj CharBufferbafera sve do mesta od kojega se njegovi znaci vie ne mogu odtampati.

Pribavljanje prostih tipova


Iako ByteBuffer samo uva bajtove, on obuhvata metode koje od tih bajtova proizvode sve proste tipove. U narednom primeru prikazano je umetanje i vaenje razliitih vrednosti pomou tih metoda:
/ / : u i / D a jP o d a t k e . ja v a / / Tumaenje b a jt o v a i z B y te B u ffe ra na r a z l i i t e naine im p o r t j a v a . n i o . * ; im p o r t s t a t i c n e t . m i n d v i e w . u t i l . P r i n t . * ; p u b l i c c la s s DajPodatke { p r i v a t e s t a t i c f i n a l i n t VELBAF = 1024; p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] args) { B y t e B u f f e r bb = B y t e B u f f e r . a l 1ocate(VELBAF); B y t e B u f f e r se automatski puni nulama im mu i n t i = 0; w h i 1e ( i + + < b b . 1 i m i t ())

//

se

d o d e li memorija:

760

Misliti na Javi

i f ( b b . g e t ( ) != 0) p r in t( " n ije n u la "); p rin t("i = " + i) ; b b .re w in d (); / / U p i i i p r o i t a j n i z t i p a ch a r: b b .a s C h a rB u ffe r().p u t("Z d ra v o !" ) ; c h a r c; w h i ! e ( ( c = b b . g e t C h a r ( ) ) != 0) p rin tn b (c + " " ); p rin t(); b b .re w in d (); / / U p i i i p r o i t a j b r o j t i p a s h o r t : b b .asS h o rtB u ffe r(),p ut((sho rt)4 7 1 1 4 2 ); p rin t(b b .g e tS h o rt()); b b .re w in d (); / / U p i i i p r o i t a j b r o j t i p a i n t : b b .asIn tB u ffe r().p u t(9 9 4 7 1 1 4 2 ); p rin t(b b .g e tln t()); b b .re w in d (); / / U p i i i p r o i t a j b r o j t i p a lo n g : bb.asLo n g B u ffe r().pu t(9 9 47 1 142); p rin t(b b .g e tL o n g ()); b b .re w in d (); / / U p i i i p r o i t a j b r o j t i p a f l o a t : b b .a s F lo a tB u ffe r().p u t(9 9 4 7 1 1 4 2 ); p rin t(b b .g e tF lo a t()); b b .re w in d (); / / U p i i i p r o i t a j b r o j t i p a do u ble: b b . a s D o u b le B u f f e r ( ) . p u t (99471142) ; p r i n t ( b b . g e t D o u b le ( ) ) ; b b . rewi n d ( ) ;

}
} / * Is p is : i = 1025 Z d r a v o ! 12390 99471142 99471142 9 . 9471144E7 9 . 9471142 E7

* ///.Nakon doele memorije objektu tipa ByteBuffer, proverili smo da li su njegove vrednosti automatski postale nula - i jesu. Proitane su sve 1024 vrednosti (do kraja bafera koji daje metoda lim it()), i sve su bile nula. Vrednosti prostih tipova najlake je umetnuti u ByteBuffer pomou odgovarajuih ,,prikaza tog bafera koje daju metode asC harB uffer(), asShorlB uffer() itd., i zatim treba upotrebiti metodu p u t( ) tog prikaza. Upravo taj postupak smo primenili za sve proste tipove podataka. Meu svim tim metodama, jedino je udna metoda p u t( ) za ShortBuffer koja zahteva eksplicitnu konverziju tipa (a ona odseca i menja rezultujuu vrednost). Nijedan od ostalih prikaza bafera u svojoj metodi p u t ( ) ne zahteva konverziju tipa.

Poglavlje 18: Javin ulazno-izlazni sistem

761

Baferi prikaza
Bafer prikaza omoguuje da ByteBuffer u pozadini posmatrate kroz prozor odreenog prostog tipa. ByteBuffer je i dalje jedino skladite koje ,,podupire taj prikaz, pa se sve izmene koje napravite u prikazu odraavaju u izmenama podataka u ByteBuffer baferu. Kao to ste videli u prethodnom primeru, na raspolaganju su vam pomone metode za umetanje prostih tipova u ByteBuffer. Prikaz omoguuje i itanje vrednosti prostih tipova iz ByteBuffer bafera, bilo jednu po jednu (to ByteBuffer dozvoljava) bilo u grupama (kao nizove). Evo primera kako se pomou klase IntBuffer radi s celim brojevima (int) u ByteBuffer baferu:
/ / : u i/In tB u ffe rP rim e r.ja v a / / Rad s c e lim b ro je vim a u B y t e B u ffe r u pomou I n t B u f f e r a im p o r t j a v a . n i o . * ; p u b l i c c la s s I n t B u f f e r P r i m e r { p r i v a t e s t a t i c f i n a l i n t VELBAF = 1024; p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] a rg s) { B y t e B u f f e r bb = B y t e B u f f e r . a l 1ocate(VELBAF); In tB u ffe r ib = b b .a s In tB u ffe r ( ) ; / / U s k l a d i t i n iz c e l i h b r o je v a : i b . p u t ( n e w i n t [ ] { 11, 42, 47, 99, 143, 811, 1016 } ) ; / / i t a n j e i p is a n je sa /u a p s o lu tn e l o k a c i j e : S y s te m .o u t.p rin tln (ib .g e t(3 )); i b . p u t (3, 1811); / / Zadavanje novog k r a j a b a fe ra pre " p r e m o t a v a n ja " . ib .fl ip ( ) ; w h i1e (ib . h a s R e m a in in g ( ) ) { in t i = ib .g e t(); S y s te m .o u t. p r i n t l n ( i ) ;

} }
} /* 99 Is p is :

11
42 47 1811 143 811 1016

* ///:Preklopljena metoda p u t ( ) prvo se koristi za skladitenje niza celih brojeva. Naredni pozivi metoda g e t() i put( ) neposredno pristupaju lokacijama celih brojeva u pripadajuem ByteBufferu. Imajte u vidu da je pristupanje apsolutnim lokacijama dostupno i za proste tipove, preko neposrednog obraanja ByteBufferu. Kada se pripadajui ByteBuffer preko bafera prikaza popuni ceiim brojevima ili nekim drugim prostim tipom vrednosti, onda se ByteBuffer moe upisati neposredno u kanal. lednako lako je i itanje iz kanala i upotreba bafera prikaza za konverziju svega u

762

Misliti na Javi

odreeni tip proste vrednosti. U narednom prim eru se ista sekvenca bajtova redom tumai kao broj tipa short, int, float, long i double, tako to se proizvode odgovarajui baferi prikaza za isti ByteBuffer:
/ / : u i/B a fe riP rik a z a .ja v a ir r p o r t j a v a . n i o . * ; im p o r t s t a t i c n e t . m i n d v i e w . u t i 1 . P r i n t . * ; p u b l i c c la s s B a f e r i P r i k a z a { p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] a rg s) { B y t e B u f f e r bb = B y t e B u f f e r . w r a p ( new b y t e [ ] { 0, 0, 0, 0, 0, 0, 0, ' a ' } ) ; b b .re w in d (); p rintn b ("B yte B u ffer " ); w h ile ( b b . h a s R e m a in in g ( )) p r i n t n b ( b b . p o s i t i o n ( ) + " -> " + b b . g e t ( ) + " , " ) ; p rin t(); C h a rB u ffe r cb = ( (B y te B u ffe r)b b .re w in d ()). a s C h a rB u ffe r(); p rintn b ("C h a rB u ffe r " ) ; w h i1e(cb.hasRemai ni n g ( ) ) p r i n t n b ( c b . p o s i t i o n ( ) + " -> " + c b . g e t ( ) + " , " ) ; p rin t(); F lo a tB u ffe r fb = ( ( B v t e B u f f e r ) b b . rewi n d ( ) ) . a s F l o a t B u f f e r ( ) ; p rin tn b ("F lo a tB u ffe r " ) ; w h i1e(fb .h a sR e m a in i n g ( ) ) p r i n t n b ( f b . p o s i t i o n ( ) + " -> " + f b . g e t ( ) + " , " ) ; p rin t(); In tB u ffe r ib = ( (B y te B u ffe r)b b .re w in d ()) , a s I n tB u f fe r ( ) ; p rin tn b ( " In tB u ffe r "); w h i l e ( ib . h a s R e m a in in g ( ) ) p r i n t n b ( i b . p o s i t i o n ( ) + " -> " + i b . g e t ( ) + " , " ) ; p rin t(); L o n g B u ffe r l b = ( ( B y t e B u f f e r ) b b . r e w i nd( ) ) . asLongBuff e r ( ) ; p rin tn b ("L o n g B u ffe r " ) ; w h i1e ( l b . hasRemaining ( ) ) p r i n t n b (1b . p o s i t i o n ( ) + " -> " + l b . g e t ( ) + " , ) ; p rin t(); S h o r t B u f f e r sb = ((B y te B u ffe r)b b .re w in d ()).a s S h o rtB u ffe r(); p rintn b ("S h ortB u ffe r " ) ; w h i1e(sb.hasRemai ni n g ( ) ) p r i n t n b ( s b . p o s i t i o n () + " -> " + s b . g e t ( ) + " , ) ; p rin t(); D o u b le B u ffe r db = ((B y te B u ffe r)b b .re w in d ()) .a s D o u b le B u ffe r(); p rin tn b ("D o u b le B u ffe r " ) ;

Poglavlje 18: Javin ulazno-izlazni sistem

763

w h ile ( d b .h a s R e m a in in g ( )) p rin tn b (d b .p o s itio n ()+ 1 1 -> " + d b . g e t ( ) + " ,

");

}
} /* Is p is : 2 -> 0, 3 -> 0, 4 -> 0, 5 -> 0, 6 -> 0 , 7 -> 97, 2 -> , 3 -> a, 1.36E-43, B y t e B u f f e r 0 -> 0, 1 -> 0, C h a rB u ffe r 0 -> , 1 -> , F l o a t B u f f e r 0 -> 0 . 0 , 1 -> I n t B u f f e r 0 -> 0, 1 -> 97, L o n g B u ffe r 0 -> 97, S h o r t B u f f e r 0 -> 0, 1 -> 0, D o u b le B u ffe r 0 -> 4.8E-322,

2 -> 0 , 3 - > 97,

* ///:ByteBuffer je proizveen omotavanjem osmobajtnog niza tipa byte koji se zatim prikazuje putem bafera prikaza svih prostih tipova. U narednom dijagramu prikazana su razliita tumaenja istih bitova kada se itaju iz razliitih vrsta bafera: Ovo odgovara ispisu rezultata programa.
0 0 0 0 0 0 0 a 0 0 0.0 97 4.8E-3 22 0 0 97 1.3 6E-43 97 97 bytes chars shorts ints floats longs d oubles

Veba 24: (1) Izmenite program IntBufferPrimer.java tako da se koriste brojevi tipa double. Endiani Podaci se na raznim raunarima skladite na razliite naine. Na big endian platformama, najznaajniji bajt se smeta na najniu memorijsku adresu, dok se na little endian platformama najznaajniji bajt smeta na najviu memorijsku adresu. Kada skladitite vrednost veu od jednog bajta, kao to su int, float itd., morate uzeti u obzir redosled bajtova. ByteBuffer skladiti podatke u big endian obliku, a tako se podaci uvek alju i preko mree. Redosled smetanja bajtova podataka u ByteBufferu moete promeniti metodom order( ), uz argument ByteOrder.BIG_ENDIAN ili ByteOrder.LITTLE_ENDIAN.

764

Misliti na Javi

Pogledajmo ByteBuffer koji sadri ova dva bajta:

bl

b2

Ako ovebajtove proitate (protumaite) kao broj tipa short (ByteBuffer.asShortBuffe r()), dobiete broj 97 (00000000 01100001), ali ukoliko preete na redosled bajtova little endian, dobiete broj 24832 (01100001 00000000). U narednom prim eru pokazano je kako se redosled bajtova u znakovima menja u zavisnosti od param etra endian:
/ / : u i/E n d ia n i.ja v a / / R a z lik e izmeu endiana i s k l a d i t e n j e podataka. im p o r t j a v a . n i o . * ; im p o r t j a v a . u t i l . * ; im p o r t s t a t i c n e t . m i n d v i e w . u t i l . P r i n t . * ; p u b l i c c la s s Endiani ( p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] a rg s) ( B y t e B u f f e r bb = B y t e B u ffe r .w ra p ( n e w b y te [ 1 2 ] ) ; bb .asC ha rB u ffe r().pu t("ab cd e f"); p rin t(A rra y s .to S trin g (b b .a rra y ()) ) ; b b .re w i n d ( ) ; bb.ord e r(B y te O rd er.B IG _ E N D IA N ); b b .a sC ha rB u ffe r().pu t("ab cd e f" ) ; p rin t(A rra y s . to S trin g (b b .a rra y ( ) ) ) ; b b .re w in d (); bb.ord e r(B y te O rd er.L ITT L E _ E N D IA N ); b b .a s C h a rB u ffe r().p u t("a b c d e f" ) ; p rin t(A rra y s .to S trin g (b b .a rra y ()));

}
} / * Is p is : [ 0 , 97, 0, 98, 0, 99, 0, 100, 0 , 101, 0 , 102] [ 0 , 97, 0, 98, 0, 99, 0, 100, 0, 101, 0, 102] [9 7, 0, 98, 0, 99, 0, 100, 0, 101, 0, 102, 0]

* ///:ByteBuffer je dobio dovoljno prostora da sve bajtove uva u ni/.u znakova (charArray) kao spoljnom baferu, tako da se moe pozvati metoda array( ) da bi prikazala sve bajtove. Metoda a rra y () je neobavezna i moete je pozvati za bafere poduprte nizom; u protivnom, dobiete U nsupportedO perationException. Pomou prikaza CharBuffer umee se charA rray u ByteBuffer. Nakon prikazivanja svih bajtova uveriete se da je podrazumevani redosled jednak kasnijem eksplicitno pozvanom poretku big endian, dok redosled little endian zamenjuje bajtove.

Poglavlje 18: Javin ulazno-izlazni sistem

765

Rad s podacima pomou bafera


U narednom dijagramu ilustrovani su odnosi izmeu nio klasa, pa moete videti kako da premetate i konvertujete podatke. Na primer, ako niz tipa byte elite da upiete u datoteku, onda treba da ga omotate metodom ByteBuffer.w rap(), metodom getC hanneI() otvorite kanal u toku FileO utputStream , a zatim iz tog ByteBuffera podatke upiete u FileChannel. Podupirua mrea ili sistem datoteka
'' F ile lnp ut? jtre a m F ile O u tp u tStream R a nd om A c cessFile ' Socket D a ta g ra m S o cke t ServerSocket Uslune klase

K anali

g e tC h a n n e l() F ile C h a n n e l

<-

,w rite (B yte B u ffe r) B yteB u ffer rea d (By te B u ffe r)

~K\

m ap(F ileC hannel. M a p M o d e , position, size)

M a p p e d B y te B u ffe r

----------------------- 1
b yte [] ch ar(| d o u b lc 'd
flo a tfj

pojavljuje se u adresnorc prostoru procesa

^ a rra y ()/q e t(b y te f[) w ra p (b y te (j) ^ a rra y ()/q e tfc h a rf|) w ra p (c h a r[() ^ a rra y ()/q e t(d o u b le f|) w ra p (d o u b le [j) ^ a rra y f|/q e t(flo a tffl w ra p (flo a t[j) ^ a rra y f)/q e tfin t C h a rB u ffe r a sD o u b le B u ffe r() D o u b le B u fte r asFloatB uffer() F lo a tB u ffe r aslntB uffer() In tB u ffe r asLongB uffer() L o n g B u ffe r asS hortBuffer() asC harBuffer()

in t j j

w ra p (in t [)) ^ a rra y f)/q e t(lo n q [|)


w r a p ( lo n g f j) a rr a y ( )/q e t(s h o r t(|)

lo n g f j

short((
w r a p ( S h o r tfj)

S h o rtB u ffe r

K odiranje/D ekodiranje pom ou ByteBaffera


ka kodiranom toku podataka
e n c o d e (C h a rB u ffe r)______

Uitajte emu kodiranja pozivom! Charset-forNarnef"8859 I ema kodiranja

n e w E n c o d e rf)

C h arse tE n cod er

C h arse tD e co d e r n e w D e c o d e r() ecoe(ByteBuffer) ^


iz kodiranog toka podataka

766

Misliti na Javi

Vodite rauna o tome da je ByteBuffer jedini nain za umetanje podataka u kanal i vaenje podataka iz kanala, i da od ByteBufera jedino moete napraviti samostalni bafer nekog od prostih tipova, sami ili nekom od ,,as metoda. Dakle, bafer nekog od prostih tipova ne moete pretvoriti u ByteBuffer. Meutim, poto podatke prostih tipova moete umetati u ByteBuffer i vaditi ih iz njega preko bafera prikaza, to i nije neko ogranienje.

Detaljno o baferima
Bafer se sastoji od podataka i etiri indeksa za pristupanje tim podacima i delotvoran rad s njima: marker, poloaj, kraj i kapacitet. Postoje metode za postavljanje vrednosti tih indeksa, za njihovo resetovanje i za ispitivanje njihovih vrednosti.

capacity()
c le a r() flipl ) lim it| ) lim it(int kraj) mark( ) position( ) positionfint pol) rem ainingl ) hasRemaining( )

Vraa kapacitet bafera. Brie sadraj bafera, indeksu potoaja zadaje vrednost nula, a indeksu kraja vrednost indeksa kapacitet. Ovom metodom istite postojei bafer. Indeksu kraja zadaje vrednost indeksa poloaja. a indeksu poloaja vrednost nula. Ovom metodom se bafer priprema za itanje nakon upisivanja podataka. Vraa vrednost indeksa kraj. Zadaje vrednost indeksa kraj. Indeksu markerzaaje vrednost indeksa poloaja. Vraa vrednost indeksa poloaja. Zadaje vrednost indeksa poloaja. Vraa (kraj - poloaj). Vraa true ako u baferu ima elemenata izmedu mesta na koja pokazuju indeksi po loaja i kraja

Ove indekse auriraju metode koje umeu i vade podatke iz batera. Tako indeksi odraavaju izmene sprovedene u baferu. U narednom primeru upotrebljen je veoma jednostavan algoritam (zamena susednih znakova) da bi se ispreturali i ponovo vratili na mesta znakovi u CharBufferu:
//: u i/ U p o t r e b a B a f e r a . ja v a im p ort j a v a . n i o . * ; im p o r t s t a t i c n e t . m i n d v i e w . u t i l . P r i n t . * ; p u b l i c c la s s UpotrebaBafera { p r i v a t e s t a t i c v o id s i m e t r i c n o P r e t u r a n j e ( C h a r B u f f e r b a f e r ) { w hile (b afe r.h asR e m a in in g ()) { b a fe r.m a rk(); ch ar c l = b a f e r . g e t ( ) ; ch a r c2 = b a f e r . g e t ( ) ; b a fe r.re s e tO ; b a fe r.p u t(c 2 ).p u t(c l);

Poglavlje 18: Javin ulazno-izlazni sistem

767

}
p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] a rg s) { c h a r [ ] podaci = " U p o t r e b a B a f e r a " . t o C h a r A r r a y ( ) ; B y t e B u f f e r bb = B y t e B u f f e r . a l 1o c a t e ( p o d a c i .1 ength * 2 ) ; C h a rB u ffe r cb = b b . a s C h a r B u f f e r ( ) ; cb .p u t(p o d a c i); p rin t(c b .re w in d ()); s im e tric n o P re tu ra n je (c b ); p rin t(c b .re w in d ()); s im e tri cn o P re tu ra n je (c b ); p rin t(c b .re w in d ( ) ) ;

}
} / * Is p is : UpotrebaBafera pUtoera baBefa r UpotrebaBafera

* ///:Iako se CharBuffer moe napraviti neposredno metodom w ra p () od niza tipa char, umesto toga smo memoriju dodelili ByteBufferu, a CharBuffer je napravljen kao prikaz toga ByteBuffera. Time se istie da je cilj uvek koristiti ByteBuffer, poto on radi neposredno s kanalom. Evo kako izgleda bafer na ulazu u metodu sim etricnoP returanje():

rwi

a i -i i

1ndeks poloaja pokazuje na prvi element bafera, a indeksi kapacitet i kraj na posiednji. U metodi simetricnoPreturanjef ), petlja while iterira dok indeks poloaja ne doe do vrednosti kraj. Indeks (tekueg) poloaja bafera se menja kada se pozovu relativne funkcije g e t( ) ili p u t( ) (pozivi bez argumenata). Moete pozivati i apsolutne metode g e t() i p u t( ), sa indeksom poloaja elementa kao argumentom koji odreduje mesto na kojem se odigrava vadenje (metodom g e t()) odnosno umetanje (metodom p u t()). Te (apsolutne) metode ne menjaju vrenost indeksa poloaja bafera. Kada kontrola ude u petlju while, vrednost indeksa m arker biva zadata pozivom metode m a rk (). Tada je stanje bafera:
I m ,!r |

1 M -) 1

/
U

s/ a I ferai I

768

Misliti na Javi

Dva poziva relativne metode g e t() smetaju vrednost prva dva znaka u promenljive cl i c2. Nakon ta dva poziva, bafer izgleda ovako:
I mar I I kap |

\/ u

O 1gg I

r I

\/ a

/
krai

Da bismo zamenili mesta ta dva znaka, vrednost promenljive c2 moramo upisati na mesto poloaj = 0 , a vrednost promenljive c l na mesto poloaj = 1. Za to moemo upotrebiti apsolutnu metodu p u t ( ) ili indeksu poloaja zadati vrednost indeksa marker, to upravo i radi metoda re se t():
I mar I j kap |

N ,/

s p o t r e b a

I Pol

I k ra j |

Dva poziva metode p u t( ) upisuju c2 i zatim cl:

\/ p

T
loaja:

a \

Tokom sledee iteracije petlje, indeksu marker se zadaje tekua vrednost indeksa po-

\/ P U o t r e b a B a f e r

\/ a \

Postupak se nastavlja do prolaska kroz ceo bafer. Na kraju petlje vvhile, indeks poloaja pokazuje na kraj bafera. Kada ispiete sadraj bafera, ispisuju se samo znakovi izmeu poloaja i kraja. Dakle, da biste ispisali ceo sadraj bafera, indeksu poloaja morate metodom rew in d () zadati vrednost poetka bafera. Evo stanja bafera nakon poziva metode rew in d () (vrednost indeksa marker postaje nedefinisana):

Poglavlje 18: Javin ulazno-izlazni sistem

769

I tap I
\/ p u t o e r a b a B e f a
/

r \

I pol I

I kraj |

Kada se ponovo pozove metoda sim etricn o P retu ranje(), CharBuffer se podvrgava istom postupku i vraa u svoje prvobitno stanje.

Datoteke preslikane u memoriju


Datoteke preslikane u memoriju omoguuju pravljenje i menjanje datoteka koje su prevelike da bi cele bile smetene u memoriju. Kada je datoteka delom preslikana u memoriju, moete se pretvarati kako je cela u memoriji i da joj moete pristupati tretirajui je kao obian veliki niz. Time se znatno pojednostavljuje kod koji se mora napisati za menjanje takve datoteke. Sledi jednostavan primer:
/ / : u i / V e l i k e P r e s lik a v a n e D a to te k e .ja v a / / P r a v l j e n j e veoma v e l i k e d a tote ke pomou p r e s l i k a v a n j a . / / {RunByHand} im p o rt j a v a . n i o . * ; im p o r t j a v a . n i o . c h a n n e l s . * ; im p o r t j a v a . i o . * ; im p o rt s t a t i c n e t . m i n d v i ew.u t i 1 . P r i n t . * ; p u b l i c c la s s V e lik e P re s lik a v a n e D a t o te k e { s t a t i c i n t duzina = 0x8FFFFFF; / / 128 M B p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] a rg s) throws Exce p tio n { MappedByteBuffer i z l a z = new R a n d o m A c c e s s F ile C t e s t . d a t " , " r w " ) .getChannel () ,map(FileChannel.MapMode.READ_WRITE, 0, d u z in a ) ; f o r ( i n t i = 0; i < d u z in a ; i++) iz la z .p u t((b y te )' x ' ); p r i n t ( " P is a n je zavreno"); f o r ( i n t i = d u z in a /2 ; i < d u z in a /2 + 6; i+ + ) p rin tn b ((c h a r)iz la z .g e t(i) );

} } III-Da bismo obraili i itanje i pisanjc, poinjemo tako to pravimo (objekat tipa) RandomAccessFile, pribavimo kanal za tu datoteku, i zatim pozovemo metodu n ia p () koja proizvodi objekat tipa MappedByteBuffer, to je posebna vrsta direktnog bafera. Obratite panju na to da moete zaati poetnu taku i duinu oblasti u datoteci koju elite da preslikate; to znai da imate mogunost da preslikavate manje oblasti velikih datoteka. MappedByteBuffer je izvedena iz klase ByteBuffer, pa ima sve njene metode. Ovde su prikazane samo veoma jednostavne primene metoda p u t( ) i g e t(), ali na raspolaganju su vam i metode kao asC harB uffer() itd.

770

Misliti na Javi

Datoteka napravljena u prethodnom programu dugaka je 128 MB, pa verovatno prevazilazi maksimalnu veliinu pojedinane datoteke u memoriji koju OS vaeg raunara dozvoljava. Izgleda kao da je cela datoteka odjedanput dostupna zato to se u memoriju smeta tek jedan njen deo, a ostatak se izmeta napolje. Na taj nain moete menjati sadraj veoma velikih datoteka (do 2 GB). Imajte u vidu da su, radi optimizovanja performansi, za preslikavanje datoteka u memoriju upotrebljene uslune metode pripadnog operativnog sistema. Performanse Iako su performanse ,,starog U/I sistema zasnovanog na tokovima poboljane realizacijom pomou nio klasa, obino se jo mnogo bre pristupa datotekama preslikanim u memoriju. U narednom programu uporeene su njihove performanse (na veoma jednostavan nain):
/ / : u i/U IP re s lik a v a n ih .ja v a im p o r t j a v a . n i o . * ; im p o r t j a v a . n i o . c h a n n e l s . * ; im p o r t j a v a . i o . * ; p u b l i c c la s s U l P r e s l i k a n i h { p r i v a t e s t a t i c i n t b r o jU p i s a = 4000000; p r i v a t e s t a t i c i n t b ro jU p is a U B a fe r = 200000; p r i v a t e a b s t r a c t s t a t i c c la s s T e s t e r { p r i v a t e S t r i n g ime; p u b l i c T e s t e r ( S t r i n g ime) { t h i s . i m e = ime; } p u b l i c v o id r u n T e s t () { S yste m .o ut.p rin t(im e + ": " ) ; try { lo ng pocetak = System .nanoTim e(); te s t(); double t r a j a n j e = System.nanoTime() - pocetak; System .out.form at("% .2f\n", tr a ja n je /1 .0 e 9 ) ; ) c a t c h ( I 0 E x c e p t io n e) { throw'new R u n t im e E x c e p t io n ( e ) ;

} }
p u b l i c a b s t r a c t vo id t e s t () throws I0 E x c e p tio n ;

}
p riv a te s t a tic T este r[] is p itiv a n ja = { new T e s t e r ( " U p i s i v a n j e u t o k " ) { p u b l i c v o id t e s t ( ) throws I 0 E x c e p tio n { DataOutputStream dos = new Data0utputStream( new Bu ffe red O u tp u tS tre a m ( new F i 1e0utputStream(new F i 1e ( " t e m p . t m p " ) ) ) ) ; f o r ( i n t i = 0; i < b r o jU p i s a ; i+ + ) d o s .w r ite In t( i); d o s .c lo s e O ;

Poglavjje 18: Javin ulazno-izlazni sistem

771

new T e s t e r ( " U p i s i v a n j e u p r e s l i k a n u " ) { p u b l i c v o id t e s t ( ) throws IOException { FileChannel f c = new RandomAccessFile("temp.tnip", " rw " ) .g e tC h a n n e l( ) ; I n t B u f f e r i b = fc.map( FileCh annel .HapMode.READ_WRITE, 0, f c . s i z e O ) .a s In tB u ffe r(); f o r ( i n t i = 0; i < b r o jU p i s a ; i+ + ) ib .p u t(i); fc .c lo s e O ;

} },
new T e s t e r ( " i t a n j e t o k a " ) { p u b l i c v o id t e s t ( ) throws I0 E x ce p tio n { Da ta ln putStre am d is = new DataInputStream( new B u ffe r e d In p u tS tr e a m ( new F i le I n p u t S t r e a m ( " t e m p . t m p " ) ) ) ; f o r ( i n t i = 0; i < b r o jU p i s a ; i+ + ) d is .re a d ln tO ; d is .c lo s e O ;

} },
new T e s t e r ( " i t a n j e p r e s l i k a n e " ) { p u b l i c v o id t e s t ( ) throws I0 E x c e p tio n { FileChannel f c = new F i 1eInputStre am( new F i1 e ( " t e m p . t m p " ) ) . g e t C h a n n e l( ) ; I n t B u f f e r i b = fc.map( Fi 1eChannel .MapMode.READ_0NLY, 0, f c . s i z e O ) .a s In tB u ffe r(); w h i1e ( i b.hasRemai ni n g ( ) ) ib .g e t(); fc .c lo s e O ;

new T e s t e r ( " i t a n j e i z t o k a / p i s a n j e u t o k " ) { p u b l i c v o id t e s t ( ) throws I0 E x ce p tio n { RandomAccessFi1e r a f = new RandomAccessFi1e( new F i 1e ( t e m p . t m p " ) , " r w " ) ; ra f.w ri t e l n t ( l ) ; f o r ( i n t i = 0; i < b ro jU p is a U B a fe r; i+ + ) { r a f . s e e k ( r a f . le n g th () - 4); r a f . w r it e l n t ( r a f . re a d ln t( ) ) ;

}
r a f .c lo s e ();

} i,
new T e s t e r ( " i t a n j e i z p r e s l i k a n e / p i s a n j e u p r e s l i k a n u " ) p u b l i c v o id t e s t ( ) throws I0 E x ce p tio n { FileChannel f c = new RandomAccessFi1e( new F i l e ( " t e m p . t m p " ) , " rw " ).g e tC h a n n e l ( ) ; {

772

Misliti na Javi

I n t B u f f e r i b = fc .m a p ( FileChannel ,MapMode.READ_WRITE, 0, f c . s i z e O ) .a s In tB u ffe r(); ib .p u t(O ); f o r ( i n t 1 = 1; i < b r o j l lp is a U B a f e r ; i+ +) ib .p u t( ib .g e t(i - 1 )); fc .c lo s e ();

} } };
p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] fo r(T e ste r te s t : is p itiv a n ja ) te s t.ru n T e s t(); a rg s ) {

}
} / * I s p i s : (90% podudaranja) U p is iv a n je u t o k : 0.56 U p is iv a n je u p r e s l i k a n u : 0.12 i t a n j e t o k a : 0.80 i t a n j e p r e s l i k a n e : 0.07 i t a n j e i z t o k a / p i s a n j e u t o k : 5.32 i t a n j e i z p r e s l i k a n e / p i s a n j e u p r e s l i k a n u : 0.0 2

* ///:Kao to ste videli u prethodnim primerim a iz ove knjige, metoda runTest( ) je upotrebljena u projektnom obrascu Template M ethod (ablonska metoda) za pravljenje strukture za ispitivanje raznih realizacija metode te s t( ) defmisanih u anonimnim unutranjim potklasama. Svaka od tih potklasa obavlja jednu vrstu ispitivanja, pa te metode te s t() predstavljaju i prototip za obavljanje raznih U/I operacija. Premda izgleda kao da se za pisanje u preslikanu datoteku upotrebljava tok FileOutputStream, sav izlaz preslikanih datoteka mora upotrebljavati klasu RandomAccessFile, ba kao itanje/pisanje u prethodnom kodu. Obratite panju na to da te s t() metode obuhvataju i vreme za inicijalizaciju raznih U/I objekata. Priprema preslikanih datoteka ume da bude skupa, ali je sveukupni dobitak znaajan kada se uporedi sa stanjem pri korienju U/I sistema zasnovanih na toku. Veba 25: (6) Napravite eksperiment tako to ete u primerima iz ovog poglavlja naredbu ByteBuffer.aIlocate( ) promeniti u ByteBuffer.aIlocateDirect( ). Pokaite razlike u performansama, i proverite da li se prim etno menja trajanje pokretanja programa. Veba 26: (3) Izmenite program strings/JGrep.java tako da koristi Java nio datoteke preslikane u memoriju po elovima.

Zakljuavanje datoteka
Zakljuavanje datoteka omoguuje sinhronizovanje pristupa datoteci kao deljenom resursu. Medutim, dve niti koje se takmie za istu datoteku mogu biti u razliitim JVM-ovima ili jedna moe biti Java nit, a druga nit operativnog sistema. Zakljuavanje datoteka vide ostali procesi operativnog sistema zato to se zakljuavanje Java datoteka direktno preslikava u uslunu metodu operativnog sistema za zakljuavanje.

Poglavlje 18: Javin ulazno-izlazni sistem

773

Sledi jednostavan prim er zakljuavanja datoteka.


/ / : u i/ Z a k l j u c a v a n j e D a t o t e k a . j a v a im p o r t j a v a . n i o . c h a n n e l s . * ; im p o r t j a v a . u t i l . c o n c u r r e n t . * ; im p o r t j a v a . i o . * ; p u b l i c c la s s Z a k lju c a v a n je D a t o t e k a { p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] a rg s) throws Exceptio n { F ile O u tp u tS tre a m fos= new F i l e O u t p u t S t r e a m ( " d a t o t e k a . t x t " ) ; F ile L o c k f l = f o s . g e t C h a n n e l( ) . t r y L o c k ( ) ; i f ( f l != n u l 1) { S yste m .o u t.p rin tln ("Z a k lju a n a d a to te k a "); Timellnit.MILLISECONDS.sl e e p (lO O ); f l ,re le a s e (); S y s t e m . o u t . p r i n t l n ( " O t k l ju a n a d a t o t e k a " ) ;

}
fo s .c lo s e O ;

}
} / * Is p is : Z a k lju a n a d a tote k a O t k lju a n a d a tote k a

* ///:Bravu (objekat tipa FileLock) cele datoteke dobijate pozivom metoda tryLock( ) ili lock( ) za objekat tipa FileChannel. (SocketChannel, D atagram Channel i ServerSocketC hannel ne treba zakljuavati, poto su to po svojoj prirodi entiteti jednog procesa; po pravilu se mrena utinica ne deli izmeu dva procesa.) tryL ock() je neblokirajua metoda. Ona e pokuati da prigrabi bravu za sebe, ali ako u tome ne uspe (kada neki drugi proces ve ima tu bravu, a ona nije deljena), vratie se bez rezultata. Metoda lo c k () blokira sve dok ne pribavi bravu (engl. lock), ili dok nit koja je pozvala lo c k () ne bude prekinuta, ili dok se ne zatvori kanal za koji je metoda lock( ) pozvana. Brava se oslobaa (preputa drugima) metodom FileLock.release(). Moe se zakljuati i deo (oblast) datoteke, i to pozivom:
try Lo c k (lo n g p o lo a j, long v e l i i n a , boolean d e lje n a )

ili
l o c k ( l o n g p o l o a j , long v e l i i n a , boolean d e lje n a )

koji zakljuava oblast (veliina - poloaj). Treim argumentom se zadaje da li je brava deljena (engl. shared lock). Ako se metoda za zakljuavanje pozove bez argumenata, njen uinak se ne menja s promenom veliine datoteke. Ukoliko se brava pribavi za oblast od indeksa poloaj do indeksa poloaj+veliina, a datoteka se povea preko granice poloaj+veliina, onda taj poveani deo datoteke nee biti zakljuan. Pozivi metoda za zakljuavanje bez argumenata, zakljuavaju celu datoteku, ak i kada se ona povea.

774

Misliti na Javi

Podrku za iskljuive (engl. exclusive lock) ili deljene brave mora dati operativni sistem. Ukoliko operativni sistem ne podrava deljene brave, a primi zahtev da napravi jednu takvu, bie upotrebljena iskljuiva brava umesto deljene. Vrstu brave (deljena ili iskljuiva) moete saznati kad pozovete metodu FileLock.isShared(). Zakljuavanje delova preslikane datoteke Kao to je ve bilo reeno, obino se preslikavaju veoma velike datoteke. Ponekad treba zakljuati delove takve velike datoteke, kako bi ostali procesi mogli da menjaju njene nezakljuane delove. Primera radi, tako neto inimo s bazom podataka da bi bila dostupna veem broju korisnika istovremeno. Evo primera s dve niti, od kojih svaka zakljuava odreeni deo datoteke:
/ / : u i/Z a k lju c a v a n je P re s lik a n ih D a to te k a .ja v a / / Z a k lj u a v a n je delova p r e s l i k a n i h d a t o t e k a . / / {RunByHand} im p o rt j a v a . n i o . * ; im p o rt j a v a . n i o . c h a n n e l s . * ; im p o rt j a v a . i o . * ; p u b l i c c la s s Z a k lj u c a v a n je P r e s lik a n ih D a t o t e k a { s t a t i c f i n a l i n t DUZINA = 0x8FFFFFF; / / 128 M B s t a t i c FileChannel f c ; p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] a rg s) throws E x ce p tio n { fc = new R a n d o m A c c e s s F ile ( " t e s t . d a t , " r w " ) .g e t C h a n n e l( ) ; MappedByteBuffer i z l a z = fc.map(FileChannel.MapMode.READ_WRITE, 0 , DUZINA); f o r ( i n t i = 0; i < DUZINA; i+ + ) i z l a z . p u t ( ( b y t e ) ' x 1) ; new Z a k l j u c a j l P r o m e n i ( i z l a z , 0, 0 + DUZINA/3); new Z a k l j u c a j l P r o m e n i ( i z l a z , DUZINA/2, DUZINA/2 + DUZINA/4);

}
p r i v a t e s t a t i c c la s s Z a k lj u c a jl P r o m e n i extends Thread { p r iv a te B yteB uffer baf; p r i v a t e i n t po ce ta k, k r a j ; Z a k l j u c a j l P r o m e n i ( B y t e B u f f e r mbb, i n t p o c e ta k , i n t k r a j ) t h i s . p o c e t a k = poceta k; th is .k ra j = k ra j; mbb.1i m i t ( k r a j ) ; m b b .p o s itio n (p o c e ta k ); b a f = m b b . s li c e ( ) ; p o c e ta k(); {

}
publ i c v o id r u n ( ) try { {

/ / I s k l j u i v a brava bez p r e k la p a n ja : F ile L o c k f l = f c . l o c k ( p o c e t a k , k r a j , f a l s e ) ; S y s t e m . o u t . p r i n t l n ( " Z a k l ju c a n o : "+ pocetak +" do "+ k r a j ) ; / / Obavi izmene: w h i l e ( b a f . p o s i t i o n ( ) < b a f . l i m i t ( ) - 1)

Poglav[je 18: Javin ulazno-izlazni sistem

775

b a f . p u t ( (by t e ) ( b a f . g e t ( ) + i ) ) ; f l .re le a s e (); S y s te m .o u t.p rin tln ("O tk lju c a n o : } c a t c h ( I 0 E x c e p t io n e) ( th ro w new R u n tim e E x c e p tio n ( e ) ; "+ pocetak +" do + k r a j ) ;

} } }
} lll--~

Klasa niti ZakljucajlProm eni priprema oblast bafera i metodom slice() pravi odseak (engl. slice) koji e biti izmenjen, a u metodi r u n ( ) pribavlja se brava za kanal datoteke (ne moete pribaviti bravu bafera, nego samo kanala). Metoda lo ck () poziva se veoma slino pribavljanju zakljuane niti objekta - sada imate kritinu sekciju sa iskljuivim pristupom tom delu datoteke.5 Brave se otkljuavaju automatski kada JVM izae iii kada se zatvori kanai za koji je brava pribavljena, ali za otkljuavanje objekta tipa FileLock moete i eksplicitno da pozovete metodu release() kao to je uraeno u prethodnom programu.

Komprimovanje
Javina U/I biblioteka sadri klase koje podravaju itanje i upisivanje tokova u komprimovanom formatu. Te klase treba omotati oko postojeih U/I klasa. Ove klase nisu izvedene iz klasa Reader i Writer, ve su deo hijerarhija klasa InputStreain i O utputStream . Razlog je to to biblioteka za komprimovanje radi s bajtovima, a ne sa znacima. Ipak, ponekad ete biti prim orani da kombinujete ove dve vrste tokova. (Zapamtite da za jednostavnu konverziju izmeu ova dva tipa moete koristiti klase InputStream Reader i OutputStreamReader.)
Klasa za kom prim ovanje CheckedlnputStream CheckedOutputStream DeflaterO utputStream ZipOutputStream GZIPOutputStream InflaterlnputStream ZiplnputStream GZIPInputStream Funkcija

Metoda G etCheckSum f ) vraa kontrolm zbir za bilo koji objekat klase InputStream (nesamo dekomprimovanje). Metoda G etCheckSum f ) vraa kontrolni zbirza bilo koji objekat klase O utputStream (ne samo komprimovanje). Osnovna klasa za kompresiju. Potklasa klase DeflaterO utputStream koja komprimuje podatke u formatu Zip. Potklasa klase DeflaterOutputStream koja komprimuie podatke u formatu GZIP Osnovna klasa za dekomprimovanje. Potklasa klase InflaterlnputStream koja dekomprimuie podatke snimijene u formatu Zip Potklasa klase InflaterlnputStream koja dekomprimuje podatke snimljene u formatu GZIP

Vic in fo rm a c ija o n itim a n a c i ete u p o g la v lju Paralelno izvravanje.

776

Misliti na Javi

Iako postoje mnogi algoritmi za komprimovanje, Zip i GZIP se verovatno najee koriste. Zbog toga s komprimovanim podacima moete lako da radite pom ou brojnih alatki za itanje i upisivanje u pomenutim formatima.

Jednostavno komprimovanje u formatu GZIP


Interfejs GZIP je jednostavan i stoga je verovatno podesniji za sluajeve kada treba komprimovati jedan tok podataka (a ne kontejner meusobno neslinih podataka). Evo primera komprimovanja jedne datoteke:
/ / : ui/G ZIP Ko m p rim ova n je .ja va // { A r g s : GZIPKom primovanje.java} im p o r t j a v a . i o . * ; im p o r t j a v a . u t i l . z i p . * ; p u b l i c c la s s GZIPKomprimovanje { p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] args) throvvs IO Exception { i f ( a r g s . l e n g t h == 0) { S y s t e m . o u t . p r i n t l n( "U potreba: \nGZIPKomprimovanje d a t o t e k a \ n " + " \ t U p o t r e b l ja v a GZIP komprimcvanje da bi komprimovao " + " d a to te k u u t e s t . g z " ) ; S y s te m .e x it(l);

}
Buffe redReader i n = new BufferedReaderf new F i l e R e a d e r ( a r g s [ 0 ] ) ) ; Bu ffe red O u tp u tS tre a m o u t = new B u fferedO utputStre am ( new GZIPOutputStream( new F ile O u t p u t S t r e a m ( t e s t . g z " ) ) ) ; S y s te m .o u t.p rin tln ("U p is u d a to te k u "); i n t c; w h i l e ( ( c = i n . r e a d ( ) ) != -1) o u t.w ri-te (c ); in .c lo s e O ; o u t.c lo s e O ; S y s te m .o u t.p rin tln ("C ita n je d a to te k e "); B u fferedReader in 2 = new Buffe red Reader( new InputStreamReader(new GZIPInputStream( new F i l e I n p u t S t r e a m ( " t e s t . g z " ) ) ) ) ; S t r i n g s; w h i l e ( ( s = i n2 . r e a d L i n e ( ) ) != n u l l ) S y s te m .o u t.p rin tln (s );

}
} /* ( P o k r e n it e da b i s t e v i d e l i re z u lta te ) *///:-

Klase za komprimovanje prilino se jednostavno koriste treba samo da omotate izlazni tok u klase G ZIPO utputStream ili ZipO utputStream , a ulazni tok u klase GZIPInputStream ili ZipInputStream . Sve ostalo svodi se na uobiajeno itanje i upisivanje.

Poglavlje 18: Javin ulazno-izlazni sistem

777

Ovo je prim er kombinovanja znakovno orijentisanih tokova s binarno orijentisanim tokovima: objekat in koristi klase iz hijerarhije Reader, dok konstruktor klase GZIPOutputStream prihvata samo objekat klase O utputStream , ali ne i klase Writer. Kada se datoteka otvori, G ZIPInputStream se pretvara u tip Reader.

Komprimovanje veeg broja datoteka u formatu Zip


Biblioteka koja podrava format Zip mnogo je obimnija. Pomou nje moete Iako da komprimujete vie datoteka, a postoji ak i posebna klasa koja olakava postupak itanja Zip datoteke. Ova biblioteka koristi standardni format Zip, pa bez problema radi sa svim alatkama koje se mogu preuzeti sa Interneta. Sledei prim er lii na prethodni, ali moe da radi s proizvoljnim brojem argumenata s komandne linije. Osim toga, prikazuje i korienje Checksum klasa za izraunavanje i proveru kontrolnog zbira datoteke. Postoje dve vrste kontrolnih zbirova: Adler32 (bri) i CRC32 (sporiji, ali neto taniji):
/ / : u i/ Z ip K o m p r im o v a n je . ja v a / / K o r i s t i fo rm a t Z ip za komprimovanje p r o i z v o l j n o g / / b r o j a d a to te k a k o j i se z adaje na komandnoj l i n i j i . / / ( A r g s : Zip K o m p rim ova n je .ja va } im p o r t j a v a . i o . * ; im p o r t j a v a . u t i l im p o r t j a v a . u t i l . z i p . * ; im p o r t s t a t i c n e t . m i n d v i e w . u t i l . P r i n t . * ; p u b l i c c la s s ZipKomprimovanje { p u b l i c s t a t i c vo id m a i n ( S t r i n g [ ] thro ws IOExce ption {

args)

Fi 1eOutputStream f = new F i l e O u t p u t S t r e a m C t e s t . z i p " ) ; CheckedOutputStream csum = new Ch eckedOutp utStream (f, new A d l e r 3 2 ( ) ) ; Z i pOutputStream zos = new Z ipO utputStream (csum ); Buffe red O u tp u tStre a m o u t = new B u ff e r e d O u t p u t S t re a m (z o s ) ; zos.setComment("Proba pakovanja u fo rm a tu Z i p " ) ; / / Ip a k , nema odgovaraju e metode getComment(). f o r ( S t r i n g arg : args) { p r i n t ( " U p i s u d a to te k u " + a r g ) ; BufferedReader in = new Buffe red Reader( new F i 1e R e a d e r(a rg )) ; z o s . p u t N e x t E n t r y (new Zi p E n t r y ( a r g ) ) ; in t c; w h i l e ( ( c = i n . r e a d ( ) ) != -1) out.w ri t e ( c ) ; i n .c lo se (); o u t . f l ush( ) ;

}
o u t.c lo s e O ; / / K o n t r o l n i z b i r j e vaei tek po z a t v a r a n ju d a to t e k e ! p rin t(" K o n tro ln i z b ir: " + cs u m .g e tC h e c k s u m ().g e tV a lu e ()) ;

774

Misliti na Javi

Podrku za iskljuive (engl. exclusive lock) ili deljene brave mora dati operativni sistem. Ukoliko operativni sistem ne podrava deljene brave, a primi zahtev da napravi jednu takvu, bie upotrebljena iskljuiva brava umesto deljene. Vrstu brave (deljena ili iskljuiva) moete saznati kad pozovete metodu FileLock.isShared(). Zakljuavanje delova preslikane datoteke Kao to je ve bilo reeno, obino se preslikavaju veoma velike datoteke. Ponekad treba zakljuati delove takve velike datoteke, kako bi ostali procesi mogli da menjaju njene nezakljuane delove. Primera radi, tako neto inimo s bazom podataka da bi bila dostupna veem broju korisnika istovremeno. Evo primera s dve niti, od kojih svaka zakljuava odreeni deo datoteke:
/ / : u i/Z a k lju c a v a n je P re s lik a n ih D a to te k a .ja v a / / Z a k lj u a v a n je delova p r e s l i k a n i h d a t o t e k a . / / {RunByHand} im p o r t j a v a . n i o . * ; im p o rt j a v a . n i o . c h a n n e l s . * ; im p o r t j a v a . i o . * ; p u b l i c c la s s Z a k lj u c a v a n je P r e s lik a n ih D a t o t e k a { s t a t i c f i n a l i n t DUZINA = 0x8FFFFFF; / / 128 M B s t a t i c FileChannel f c ; p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] a rg s) throws E x ce p tio n { fc = new R a n d o m A c c e s s F ile ( " t e s t . d a t" , " r w " ) . g e t C h a n n e l ( ) ; MappedByteBuffer i z l a z = f c . m a p ( F i 1eChannel.MapMode.READ_WRITE, 0, DUZINA); f o r ( i n t i = 0; i < DUZINA; i+ + ) i z l a z.p u t((b yte ) x ' ); new Z a k lju c a j I P r o m e n i ( i z l a z , 0, 0 + DUZINA/3); new Z a k l j u c a j l P r o m e n i ( i z l a z , DUZINA/2, DUZINA/2 + DUZINA/4);

}
p r i v a t e s t a t i c c la s s Z a k lj u c a jl P r o m e n i extends Thread { p r iv a te B yteB uffer baf; p r i v a t e i n t po ce ta k, k r a j ; Z a k l j u c a j I P r o m e n i ( B y t e B u f f e r mbb, i n t p o ce ta k , i n t k r a j ) t h i s . p o c e t a k = po ce ta k; th is .k ra j = k ra j; mbb.1imi t ( k r a j ) ; m b b .p o sitio n (p o ce ta k); b a f = m b b . s li c e ( ) ; p o c e ta k ();

}
p u b l i c vo id r u n ( ) { try { / / I s k l j u i v a brava bez p re k la p a n ja : F ile L o c k f l = f c . l o c k ( p o c e t a k , k r a j , f a l s e ) ; S y s t e m . o u t . p r i n t l n ( " Z a k l ju c a n o : + poce tak +" do "+ k r a j ) ; / / Obavi izmene: w h i l e ( b a f . p o s i t i o n ( ) < b a f . l i m i t ( ) - 1)

Poglavlje 18: Javin ulazno-izlazni sistem

775

b a f.p u t((b y te )(b a f.g e tf) fl.re le a s e ();

+ 1 )); "+ poce tak +" do "+ k r a j ) ;

S y s te m .o u t.p rin tln ("O tk lju c a n o : } c a tc h (IO E x c e p tio n e) { throvv new R u n t im e E x c e p t io n ( e ) ;

} } }

} III-Klasa niti ZakljucajlProm eni priprema oblast bafera i metodom slice() pravi odseak (engl. slice) koji e biti izmenjen, a u metodi r u n ( ) pribavlja se brava za kanal datoteke (ne moete pribaviti bravu bafera, nego samo kanaia). Metoda Iock() poziva se veoma slino pribavljanju zakljuane niti objekta - sada imate kritinu sekciju' sa iskljuivim pristupom tom delu datoteke.5 Brave se otkljuavaju automatski kada JVM izade ili kada se zatvori kanal za koji je brava pribavljena, ali za otkljuavanje objekta tipa FileLock moete i eksplitno da pozovete metodu release() kao to je uraeno u prethodnom programu.

Komprimovanje
Javina U/I biblioteka sadri klase koje podravaju itanje i upisivanje tokova u komprimovanom formatu. Te klase treba omotati oko postojeih U/I klasa. Ove klase nisu izvedene iz klasa Reader i Writer, ve su deo hijerarhija klasa InputStreain i O utputStream . Razlog je to to biblioteka za komprimovanje radi s bajtovima, a ne sa znacima. Ipak, ponekad ete biti prim orani da kombinujete ove dve vrste tokova. (Zapamtite da za jednostavnu konverziju izmeu ova dva tipa moete koristiti klase InputStream Reader i OutputStreamReader.)
Klasa za kom prim ovanje CheckedlnputStream CheckedOutputStream DeflaterOutputStream ZipOutputStream GZIPOutputStream InflaterlnputStream ZiplnputStream GZIPInputStream Funkcija

Metoda G etCheckSum ( ) vraa kontrolni zbir za bilo koji objekat klase InputStream (ne samo dekomprimovanje). Metoda GetCheckSum f ) vraa kontrolnl zbirza bilo koji objekat klase OutputStream (ne samo komprimovanje). Osnovna klasa za kompresiju. Potklasa klase DeflaterO utputStream koja komprimuje podatke u formatu Zip. Potklasa klase DeflaterOutputStream koja komprimuje podatke u formatu GZIP Osnovna klasa za dekomprimovanje. Potklasa klase InflaterlnputStream koja dekomprimuje podatke snimljene u formatu Zip Potklasa klase InflaterlnputStream koja dekomprimuje podatke snimljene u formatu GZIP

V ie in fo rm a c ija o n itim a n a i ete u p o g la v lju Paralelno izvravanje.

776

Misliti na Javi

Iako postoje mnogi algoritmi za komprimovanje, Zip i GZIP se verovatno najee koriste. Zbog toga s komprimovanim podam a moete lako da radite pomou brojnih alatki za itanje i upisivanje u pom enutim formatima.

Jednostavno komprimovanje u formatu GZIP


Interfejs GZIP je jednostavan i stoga je verovatno podesniji za sluajeve kada treba komprimovati jedan tok podataka (a ne kontejner meusobno neslinih podataka). Evo primera komprimovanja jedne datoteke:
//: u i/G ZIP Ko m p rim ova n je .ja va // { A rg s : GZIPKomprimovanje.java} im port j a v a . i o . * ; im p o r t j a v a . u t i l . z i p . * ; p u b l i c c la s s GZIPKomprimovanje { p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] a rg s) throws IOException { i f ( a r g s . l e n g t h == 0) { System. out. . p r i n t l n ( "Up otreba: \nGZIPKomprimovanje d a t o t e k a \ n " + " \ t U p o t r e b l ja v a GZIP komprimovanje da bi komprimovao " + " d a to te k u u t e s t . g z " ) ; S y s te m .e x it(l);

}
Buffe redReader i n = new Buffe red Re a d e r( new F ile R e a d e r ( a r g s [ 0 ] ) ) ; B u ffe red O u tp u tS tre a m o u t = new B u fferedO utputStre am ( new GZIPOutputStream( new F i l e O u t p u t S t r e a m ( " t e s t . g z " ) ) ) ; S y s te m .o u t.p rin tln ("U p is u d a to te k u "); i n t c; w h i l e ( ( c = i n . r e a d ( ) ) != -1) o u t.w ri-te (c ); in .c lo s e O ; o u t.c lo s e (); S y s t e m . o u t . p r i n t l n ( "C i t a n j e d a t o t e k e " ) ; BufferedReader in 2 = new Buffe red R e a d e r( new InputStreamReader(new GZIPInputStreamf new F i 1e l n p u t s t r e a m ( " t e s t . g z " ) ) ) ) ; S t r i n g s; w h i l e ( ( s = i n 2 . r e a d L i n e ( ) ) != n u l l ) S y s te m .o u t.p rin tln (s );

}
} /* ( P o k r e n it e da b i s t e v i d e l i re z u lta te ) *///:-

Klase za komprimovanje prilino se jednostavno koriste - treba samo da omotate i lazni tok u klase G ZIPO utputStream ili ZipO utputStreani, a ulazni tok u klase GZIPInputStream ili ZipInputStream . Sve ostalo svodi se na uobiajeno itanje i upisivanje.

Poglavlje 18: Javin ulazno-izlazni sistem

777

Ovo je prim er kombinovanja znakovno orijentisanih tokova s binarno orijentisanim tokovima: objekat in koristi klase iz hijerarhije Reader, dok konstruktor klase GZIPOutputStream prihvata samo objekat klase O utputStream , ali ne i klase Writer. Kada se datoteka otvori, G ZIPInputStream se pretvara u tip Reader.

Komprimovanje veeg broja datoteka u formatu Zip


Biblioteka koja podrava format Zip mnogo je obimnija. Pomou nje moete lako da komprimujete vie datoteka, a postoji ak i posebna klasa koja olakava postupak itanja Zip datoteke. Ova biblioteka koristi standardni format Zip, pa bez problema radi sa svim alatkama koje se mogu preuzeti sa Interneta. Sledei primer lii na prethodni, ali moe da radi s proizvoljnim brojem argumenata s komandne linije. Osim toga, prikazuje i korienje Checksum klasa za izraunavanje i proveru kontrolnog zbira datoteke. Postoje dve vrste kontrolnih zbirova: Adler32 (bri) i CRC32 (sporiji, ali neto taniji):
/ / : u i/Z ip K o m p r im o v a n je .ja v a / / K o r i s t i fo rm a t Z ip za komprimovanje p r o i z v o l j n o g / / b r o j a d a to te k a k o j i se zadaje na komandnoj l i n i j i . / / { A rg s : Z ip K om prim ovanje .ja va) im p o r t j a v a . i o . * ; im p o r t j a v a . u t i l . * ; im p o r t j a v a . u t i l . z i p . * ; im p o r t s t a t i c n e t . m i n d v i e w . u t i l . P r i n t . * ; p u b l i c c la s s ZipKomprimovanje { p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] args) thro ws IO Exception { Fi 1eOutputStream f = new F i l e O u t p u t S t r e a m C t e s t . z i p " ) ; CheckedOutputStream csum = new Check edOutputStream ff, new A d l e r 3 2 ( ) ) ; Z ipOutputStream zos = new Z ipO utputStream (csum ); BufferedO utputStre am o u t = new B u ff e r e d O u t p u t S t re a m (z o s ) ; zos.setComment("Proba pakovanja u form atu Z i p " ) ; / / Ip a k , nema odgovarajue metode getComment(). f o r ( S t r i n g arg : args) { p r i n t ( " U p i s u d a to te k u " + a r g ) ; BufferedReader i n = new Buffe redReader( new F i l e R e a d e r ( a r g ) ) ; z o s .p u tN e x tE n try (n e w Z i p E n t r y ( a r g ) ) ; i n t c; w h i l e ( ( c = i n . r e a d ( ) ) != -1) out.w ri t e ( c ) ; in .c lo s e (); o u t . flu s h ( ) ;

}
o u t.c lo s e O ; / / K o n t r o l n i z b i r j e vaei te k po z a t v a r a n ju d a t o t e k e ! p r i n t ( " K o n t r o l n i z b i r : " + csu m .g e tC h e c k su m ().g e tV a lu e ()) ;

778

Misliti na Javi

// Sada raspakujemo datoteke: print("itanje datoteke "); F ilelnputStream fi = new Fi le In pu t S t r e a m C t e s t . z i p " ) ; Ch ec ke dl np ut St re am csumi = new C h e c k e d I np ut St re am (fi, new Adler32()); Zi pI np ut St re am in2 = new ZipInputStream(csumi); Bu fferedlnputStream bis = new Bu ff er ed In pu tS tr ea m(i n2 ); ZipEntry ze; wh ile((ze = in2.getNextEntry()) int x; whil e( (x = bis.read()) System.out.wri t e ( x ) ; != -1) != null) { print("itanje datoteke " + z e ) ;

}
if(args.length == 1) printC'Kontrolni bis.closeO; // Drugi nain za otvaranje i itanje zip datoteka: ZipFile zf = new ZipFile("test.zip"); Enumeration e = zf.entries(); w h i 1e ( e . h a s M o r eE le me nt s()) { ZipEntry ze2 = (Z ip En tr y) e. ne xt El eme nt (); print("Datoteka: " + ze2); // ... i raspakivanje podataka isto kao prethodno zbir: " + csumi .getChecksum() .getValue());

}
/* i f ( a r g s .1ength == 1) */ rezultate) *///:

}
} /* (Pokrenite da biste videli

Metodi putN extE ntry( ) m orate da prosledite objekat klase ZipEntry za svaku datoteku koja se doaje u arhivu. Takav objekat sadri obim an interfejs koji om oguuje itanje i zadavanje svih podataka o datom elem entu Zip datoteke; ime, kom prim ovana i nekom prim ovana veliina, datum , kontrolni zbir, dodatna polja podataka, komentar, rnetoda kompresije i da li se radi o stavki direktorijum a. M edutim , Javina biblioteka ne podrava uvoenje lozinke iako je form at Zip om oguuje. Pored toga, mada klase C lieckedlnputStream i CheckedOutputStream podravaju oba tipa kontrolnih zbirova (Adler32 i CRC32), klasa ZipEntry podrava sam o interfejs za kontrolni zbir tipa CRC. To ogranienje nam ee form at Zip, ali zbog toga se ne moe koristiti bri algoritam Adler32. Klasa Z ipInputStream ima m etodu getN extE ntry( ) za raspakivanje arhive. Ta metoda vraa sledei objekat klase ZipEntry ako on postoji. Postoji i kraa varijanta - proitajte datoteku pom ou klase ZipFile koja im a m etodu en trie s( ), a ona vraa objekat tipa Enum eration u kojem su nabrojani objekti klase ZipEntries. Da biste proitali kontrolni zbir, m orate na neki nain da obezbedite pristup odgovarajuem objektu klase Checksum. Pri tom se zadrava referenca na objekte tipa CheckedO utputStream i CheckedlnputStream , ali m oete da se oslonite i na referencu na objekat tipa Checksum. Z bunjujua m etoda u Zip tokovim a je setC om m ent( ). Kao to je pokazano u gornjem prim eru, prilikom upisivanja u datoteku m oete da stavite komentar, ali ne postoji nain

Poglavlje 1 8: Javin ulazno-izlazni sistem

779

da ga pronaete u klasi ZipInputStream . Komentari su p o tp u n o podrani sam o u klasi ZipEntry, i to pojedinano, po svakom unosu u datoteku. N aravno, pri korienju klasa za kom prim ovanje GZIP ili Zip niste ogranieni na datoteke - m oete da kom prim ujete bilo ta, ukljuujui i podatke s m renog prikljuka.

Java arhive (JAR)


Form at Zip se koristi i u datotekam a form ata JAR (Java ARhiva) koje objedinjuju grupe datoteka u jednu kom prim ovanu datoteku, slino Zip arhivam a. Kao i sve ostalo u Javi, JAR datoteke m ogu da se koriste na raznim platform am a. U Java arhive m oete da smestite audio datoteke, slike, ali i datoteke s klasama. JAR arhive su naroito korisne pri radu s Internetom . Pre njih, ita Weba je m orao da alje nekoliko zahteva Web serveru da bi preuzeo sve datoteke jednog apleta. O sim toga, nijedna od tih datoteka nije bila kom prim ovana. Pakovanjem svih datoteka odreenog apleta u jednu JAR arhivu, om oguuje se njihovo istovrem eno preuzim anje sam o jednim zahtevom serveru, a prenos je bri zbog kompresije. Svakom elem entu JAR datoteke m oe se radi bezbednosti pridruiti i digitalan potpis. JAR arhiva se sastoji od zbirke datoteka u form atu Zip i manifest datoteke, to je, u stvari, opis arhive. (M anifest datoteku moete da napravite sami ili e um esto vas to uiniti program jar.) Vie inform acija o manifest datotekam a pronai ete u dokum entaciji razvojnog paketa. Usluni program ja r koji se dobija uz razvojni paket za Javu kom panije Sun, autom atski kom prim uje datoteke koje odaberete. Poziva se s kom andne linije:
jar [opcije] odredite [manifest] ulaznedatoteke

O pcije su sam o zbirka slova (nisu neophodne crtice ili neki drugi pom oni znaci). Korisnici Unixa/Linuxa prim etie slinost s alatkom tar. Opcije su:
c t X x datoteka f Pravi novu praznu arhivu. Lista sadraj. Dekomprimuje sve datoteke iz arhive. Dekomprimuje navedenu datoteku Kae Saa u ti dostaviti ime datoteke". Akoje ne koristite, ja r pretpostavlja da e ulazm poaci stii iz standardnog ulaznog toka ili, ako pravi datoteku, da izlazne podatke treba da poalje na standardm izlazm tok. Kae da e prvi argument biti ime manifest datoteke koju je napravio korisnik. Detaljno ispisuje informacije tokom rada. Samo snima datoteke, a ne komprimuje lh. (Koristi se za pravljenje JAR datoteke koju moete da stavite u putanju klasa.) Ne pravi automatski manifest datoteku.

m V 0 M

Ako spisak datoteka koje treba arhivirati sadri poddirektorijum , taj poddirektorijum se autom atski arhivira sa svim svojim poddirektorijum im a itd. uvaju se i inform acije o putanji.

780

Misliti na Javi

Evo nekih tipinih naina za korienje program a jar. Sledea kom anda pravi JAR arhivu mojaJarDatoteka.jar koja sadri sve datoteke s klasam a u tekuem direktorijum u, kao i autom atski generisanu m anifest datoteku:
jar cf mojaJarDatoteka.jar *.class

Sledea kom anda radi isto to i p reth o d n i prim er, ali ukljuuje i korisniki napravljenu manifest datoteku mojManifest.mf:
j a r cmf mojaJarDatoteka m o j M an if es t. mf *.class

N aredna kom anda daje sadraj arhive m ojaJarD atoteka.jar u obliku liste datoteka: j a r t f m ojaJarD atoteka.jar etaljnije ispisuje inform acije o datotekam a u arhivi m ojaJarDatoteka.jar:
jar tvf mojaJarDatoteka.jar

Pod pretpostavkom da su audio, klase i slike poddirektorijum i, na ovaj nain se svi objedinjuju u datoteku mojaAplikacija.jar. Tokom izvravanja program a jar, ispisuju se d odatne inform acije zbog opcije v:
jar cvf m o j a A p l ikacija.jar audio klase slike

Ako napravite JAR datoteku korienjem opcije o (nula), moi ete da je stavite u putanju klasa:
CL AS SP AT H= "1 ib l. ja r;l ib 2. ja r;

Nakon toga Java moe da trai datoteke s klasama u bibliotekam a lib l.jar i lib2.jar. Alatka jar nije toliko korisna kao usluni program zip. Na prim er, ne m oete da dodajete ili aurirate datoteke u postojeoj JAR arhivi, tj. ovakve arhive m orate da pravite ,,od nule. Takode, datoteke ne moete da prem etate u JAR arhivu i da ih nakon prem etanja briete. M eutim , JAR datoteku, koja je napravljena u jednoj platform i, moi e da proita alatka ja r na bilo kojoj drugoj platform i, to je problem koji ponekad mui uslune program e zip. Kao to ete videti u poglavlju Grafika korisnika okruenja , JAR arhive se koriste i za pakovanje zrna Jave.

Serijalizovanje objekata
Kada napravite objekat, on postoji onoliko dugo koliko vam je potreban, ali ni pod kojim okolnostim a ne moe postojati kada program prestane da se izvrava. Koliko god da to isprva im a smisla, lm a situacija u kojim a bi bilo izvanredno korisno kada bi objekat m ogao da postoji i sauva svoje inform acije ak i kada se program ne izvrava. Tada bi objekat postojao i kada se program pokrene sledei put, i sadrao bi iste inform acije koje je imao i kada se program izvravao pretho dn i put. Naravno, to m oete postii upisivanjem inform acija u datoteku ili bazu podataka, ali u duhu politike da sve treba da bude objekat,

Poglavlje 18: Javin ulazno-izlazni sistem

781

bilo bi veom a pogodno deklarisati da je odredeni objekat ,,trajan, a da se Java p ri tom autom atski pobrine za sve pojedinosti. Serijalizovanje objekata u Javi om oguuje pretvaranje svakog objekta koji realizuje interfejs Serializable u niz bajtova iz kojeg se taj objekat kasnije moe p o tp u n o rekonstruisati. To vai ak i u m ream a, to znai da m ehanizam serijalizovanja autom atski kom penzuje razlike izm eu operativnih sistema. Dakle, m oete da napravite neki objekat na raunaru koji radi pod W indow som , serijalizujete ga i poaljete preko mree na Unix raunar gde e on biti pravilno rekonstruisan. Ne m orate da brinete o predstavljanju objekata na razliitim raunarim a, redosledu bajtova niti o drugim detaljima. Samo po sebi, serijalizovanje objekata je zanimljivo zato to om oguuje realizovanje lake trajnosti (engl. lightw eight persistence). Setite se da trajnost znai kako ivotni vek objekta nije odreen izvravanjem program a, ve objekat ivi i izm e u izvravanja programa. Efekat trajnosti moete da postignete upisivanjem serijalizovanog objekta na disk i njegovim rekonstruisanjem pri ponovnom pozivanju program a. Epitet laka potie otud to ne m oete da definiete objekat p om ou neke rezervisane rei za trajnost i da prepustite sistem u da brine o detaljim a (iako e i to m oda jednog dana biti m ogue). Umesto toga, m orate eksplicitno da serijalizujete i deserijalizujete objekat u program u. Ukoliko vam treba ozbiljniji m ehanizam trajnosti, razm otrite alatku H ibernate ( h ttp ://h ib ern a te.sourceforgc.net). Vie inform acija o njoj potraite u knjizi T hin king in Enterprise Java, koja se m oe preuzeti sa adrese w w w .M in d V iew .n et. Serijalizovanje objekata je odato u jezik radi podrke dvema vanim funkcijama. Javino daljinsko pozivanje m etoda (engl. reniote m eth o d invocation, R M I) om oguuje objektim a na drugim raunarim a da se ponaaju kao da se nalaze na vaem raunaru. Pri slanju poruka udaljenim objektim a, serijalizovanje je neophodno da bi se preneli argum enti i povratne vrednosti. Daljinsko pozivanje m etoda objanjeno je u knjizi T h in kin g in Enterprise Java.

Serijalizovanje objekata je neophodno i za zrna Jave, o em u e biti rei u poglavlju


Grafika korisnika okruenja. Kada se koristi zrno, inform acije o njegovom stanju po pra-

vilu se podeavaju ve tokom projektovanja. O ne se m oraju sauvati i rekonstruisati kasnije, kada se program pokrene. Taj zadatak obavlja serijalizovanje objekata. Serijalizovanje objekta je prilino jednostavno, pod uslovom da on realizuje interfejs Serializable (ovaj interfejs je sam o indikator i nem a m etode). Kada je serijalizovanje dodato u jezik, m noge klase standardne biblioteke prom enjene su da bi ga podrale, ukljuujui i om otae za proste tipove, sve kontejnerske klase i m noge druge. Mogu se serijalizovati ak i objekti klase Class. Da bi se objekat serijalizovao, treba napraviti neku vrstu izlaznog toka i om otati ga unutar objekta klase ObjectOutputStream. Nakon toga treba sam o pozvati m etodu writeObject( ) i objekat e biti serijalizovan i poslat u izlazni tok. (Serijalizovanje objekata je binarno orijentisano i zbog toga koristi hijerarhije klasa InputStream i O utputStream .) Za ob rn u t postupak, objekat klase InputStream se om otava u n u tar objekta tipa ObjectInputStream i poziva se m etoda readO bject( ). Kao i obino, dobija se referenca na objekat klase Object, pa je neophodna konverzija nanie u ispravan tip. Posebno pam etno reenje prim enjeno u serijalizovanju objekata jeste to to se pored uvanja slike objekta prate i sve reference koje on sadri, a uvaju se i ti objekti, prate se

782

Misliti na Javi

njihove reference itd. Ovaj princip se ponekad naziva m rea objekata povezana s jednim objektom , i ona sadri niz referenci na objekte i njihove lanove. Kada biste m orali da odravate sopstvenu em u za serijalizovanje, bilo bi preteko napisati kod koji bi pratio sve veze. Izgleda da serijalizovanje objekata u Javi om oguuje da se to postigne bez po muke, verovatno korienjem optim izovanog algoritm a koji krstari po mrei objekata. U sledeem prim eru testira se m ehanizam serijalizovanja pravljenjem ,,crva od povezanih objekata, pri em u svaki pokazuje na sledei segm ent crva i sadri niz referenci na objekte drugaije klase, nazvane Podatak:
//: ui/Crv.java // Prikazuje serijalizaciju objekata. import java.io.*; import j a v a . u t i l .*; import static ne t. mi nd vi ew .u ti l.Print.* class Podatak implements Serializable { private int n; public Podatak(int n) { this.n = n; } public String t o S t r i n g O {return Integer. to St ri ng (n );}

}
public class Crv implements Serializable { private static Random rand = new Random(47);

}
private Podatakf] d = { new Po datak(rand.nextlnt(10)), new Po datak(rand.nextlnt(10)), new Po datak(rand.nextlnt(10))

};
private Crv s l e d e c i ; private char c; // Vrednost i == broj segmenata public Crv(int i, char x) { print(" Konstruktor crva: c = x; if(--i > 0) sledeci = new Crv(i, (char)(x + 1)); " + i);

}
publ ic Crv() { k o ns tr uk to r" ); { print("Podrazumevani

}
public String t o S t r i n g O re zu lt at .a pp en d( c); re zu lt at .a pp en d( "("); for(Podatak pod : d ) ; re z u l t a t .ap pe n d ( p o d ) ; rezult at .a pp en d( ") "); i f (s le de ci!= nu l 1) StringBuilder rezultat = new St ri ng Bu i1d e r ( ":");

Poglavlje 18: Javin ulazno-izlazni sistem

783

rezultat.append(sledeci); return rezultat.toString( );

}
public static void main(String[] throws ClassNotFoundException, Crv w = new Crv(6, pri nt("w = " + w); ObjectOutputStream out = new ObjectOutputStream( new Fi le Ou tputStreamC'crv.out")); ou t. wr iteObject("Pamcenje cr v a \ n " ) ; o u t. wr it eO bj ec t( w); out.close(); // Prelazi u sledeci red ObjectlnputStream in = new ObjectInputStream( new Fi leInputStream("crv.out")); String s = (String)in.readObject(); Crv w2 = (Crv)in.readObject(); pri nt(s + "w2 = " + w 2 ) ; By teArrayOutputStream bout = new By te Ar ra yO ut pu tS tr eam (); ObjectOutputStream out2 = new O b j e ct Ou tp ut St re am (bo ut ); out2.write0bject("Pamcenje c r va \n "); out2 .w ri te 0b je ct (w ); o u t 2 . f l u s h (); ObjectlnputStream in2 = new ObjectInputStream( new ByteArrayI np ut St re am( bo ut.t o B y t e A r r a y ())); s = (Strin g ) in 2 .re ad Ob je ct(); Crv w3 = (Crv)in 2. re ad Ob je ct(); print(s + "w3 = " + w 3 ) ; 'a'); args) IOException {

}
} /* Ispis: Konstruktor crva: 6 Konstruktor crva: 5 Konstruktor crva: 4 Konstruktor crva: 3 Konstruktor crva: 2 Konstruktor crva: Pamcenje crva w2 = :a (853):b (119):c(802):d(788):e (199):f(881) Pamcenje crva w3 = : a ( 8 5 3 ) :b( 1 1 9 ) : c ( 8 0 2 ) : d ( 7 8 8 ) : e ( 1 9 9 ) : f (8 8 1 ) 1 w = :a (8 53):b(119):c (802) :d (788):e(199):f(881)

* ///:D a bi bilo zanimljivije, niz objekata klase Podatak u n u tar crva inicijalizuje se sluajnim brojevim a. (Na taj nain neete posum njati da prevodilac zadrava kakve metainform acije.) Svaki segm ent klase Crv oznaava se objektom tipa char koji se automatski generie tokom rekurzivnog pravljenja ulanane Iiste crva. Kada napravite objekat klase Crv, saoptavate konstruktoru koliko treba da bude dugaak. Da bi napravio referencu na sledeci, on poziva konstruktora klase Crv s duinom koja je m anja za jedan itd. Poslednjoj referenci sledeci ostaje vrednost null koja ukazuje na to da je crv zavren.

784

Misliti na Javi

Svrha svega bila je da se napravi neto to je dovoljno sloeno da se ne m oe lako serijalizovati runo. Postupak serijalizovanja, m eutim , prilino je jednostavan. Kada se napravi O bjectO utputStream od nekog drugog toka, serijalizuje ga m etoda w riteO bject(). O bratite panju i na poziv m etode w riteO b ject( ) za objekat tipa String. Pom ou istih m etoda kao za D ataO utputStream m oete da upisujete i sve proste tipove (oni koriste isti interfejs). Postoje dva zasebna odeljka koda koji izgledaju slino. Prvi upisuje i ita datoteku, a drugi ita i upisuje u niz bajtova. Serijalizovanje om oguuje itanje ili upisivanje objekta u bilo koji D atalnputStream ili D ataO utputStream , ukljuujui i mreu, kao to je navedeno u knjizi T hinking in Enterprisc Java. Prim eujete li da deserijalizovan objekat zaista sadri sve veze koje su postojale u originalnom objektu? U postupku deserijalizovanja objekta interfejsa Serializable ne poziva se nijedan konstruktor, ak ni podrazum evani. Ceo objekat se rekonstruie na osnovu podataka iz ulaznog toka.

Veba 27: (1) Napravite klasu koja realizuje interfejs Serializable i sadri referencu na objekat neke druge klase koja se m oe serijalizovati. Napravite instancu svoje klase, serijalizujte je tako da bude snim ljena na disk, zatim je rekonstruiite i proverite da li je postupak ispravno sproveden.

Pronalaenje klase
Moda se pitate ta je potrebno da bi se objekat rekonstruisao iz serijalizovanog stanja. Na prim er, pretpostavim o da ste serijalizovali objekat i poslali ga nekoin drugom raunaru kao datoteku ili preko mree. Da li bi program na drugom raunaru mogao da rekonstruie objekat sam o na osnovu sadraja datoteke? Najbolji nain da se odgovori na ovo pitanje jeste, kao i obino, da se proba. Sledea datoteka treba da se smesti u poddirektorijum ovoga poglavlja:
//: ui/Zeleni.java // Klasa koja se moe serijalizovati. import java.io.*; public class Zeleni implements Serializable {} ///:-

Datoteka koja pravi i serijalizuje objekat klase Zeleni nalazi se u istom direktorijum u:
//: ui/ZamrzavanjeZelenog.java // Pravi serijalizovanu izlaznu datoteku. import java.io.*; public class ZamrzavanjeZelenog { public static void main(String[] args) throws Exception { ObjectOutput izlaz = new ObjectOutputStream( new FileOutputStream("dosije.X")); Zeleni zorcon = new Zeleni();

Poglavlje 18: Javin ulazno-izlazni sistem

785

izlaz.writeObject(zorcon);

} } ///= Um esto da hvata i obrauje izuzetke, ovaj program ih iz m etode m a in ( ) prosleuje na konzolu. Kada prevedete i pokrenete program , on e dobijenu datoteku dosije.X sm estiti u direktorijum ui. U njegov p oddirektorijum dosijex stavite sledei listing:
//: ui/dosijex/MaliZeleni.java // Pokuaj da se serijalizovana datoteka rekonstruie // bez klase objekata uskladitenih u toj datoteci. // {RunByHand} import java.io.*; public class MaliZeleni { public static void main(String args[]) throws Exception { ObjectlnputStream ulaz = new ObjectlnputStreamf new Fi 1 elnputStream (new File("..", "dosije.X"))); Object misterija = ulaz.readObject(); System.out.println(misterija.getClass());

}
} /* Ispis: class Zeleni

///:ak i otvaranje datoteke i uitavanje objekta m isterija zahteva objekat klase Class za klasu Zeleni; Javina virtuelna m aina (JVM) nee moi da nae datoteku Zeleni.class, osim ako se ona nekim sluajem ne nae u putanji klase, to nije sluaj u ovom prim eru, pa e generisati izuzetak ClassNotFoundException. (Kao i obino, svaki dokaz o postojanju vanzem aljaca nestaje pre nego to se moe proveriti njegova verodostojnost!) Javina virtuelna m aina m ora da pronae odgovarajuu datoteku .class.

Upravljanje serijalizovanjem
Kao to vidite, podrazum evani m ehanizam serijalizovanja lako se koristi. M eutim , ta ako im ate specijalne potrebe? Moda posebno brinete o bezbednosti i zato ne elite da serijalizujete delove objekta, ili m oda nem a smisla da se neki podobjekat serijalizuje ako e m orati ponovo da se pravi kada se objekat bude rekonstruisao. Postupak serijalizovanja moete da kontroliete realizovanjem interfejsa Externalizable um esto interfejsa Serializable. Interfejs Externalizable proiruje interfejs Serializable i dodaje m u dve m etode, writeExternal( ) i readExternal( ), koje se autom atski pozivaju tokom serijali/ovanja i deserijalizovanja objekta, da bi omoguile izvravanje specijalnih operacija. Sledei prim er prikazuje jednostavne realizacije m etoda interfejsa Externalizable. Klase Blipl i Blip2 su skoro iste, a malu razliku izm eu njih prim etiete ako prouite kod:

786

Misliti na Javi

//: ui/Blipovi.java // Jednostavno korienje interfejsa Externa1izable i jedna zamka. import java.io.*; import static net.mindview.util.Print.*; class Blipl implements Externalizable { public Blipl() { printf'Konstruktor za Blipl

}
public void writeExternal(ObjectOutput out) throws IOException { print("Blipl.writeExternal");

}
public void readExternal(Objectlnput in) throws IOException, ClassNotFoundException { print("Blipl.readExternal");

} }
class Blip2 implements Externalizable { Blip2() { print("Konstruktor za Blip2 ");

}
public void writeExternal(ObjectOutput out) throws IOException { print("Blip2.writeExternal");

}
public void readExternal(Objectlnput in) throws IOException, C1assNotFoundException { print("Blip2.readExternal");

public class Blipovi { public static void main(String[] args) throws IOException, ClassNotFoundException { print("Pravljenje objekata:"); Blipl bl = new Blipl(); B1 i p2 b2 = new B1ip2 (); ObjectOutputStream o = new ObjectOutputStream( new Fi1eOutputStream("Blips.out")); print("Snimanje objekata:"); o.writeObject(bl); o.write0bject(b2); o.closeO; // Sada ih vraamo: ObjectlnputStream in = new ObjectInputStream( new Fi1eInputStream("Blips.out")); print(Rekonstruisanje bl:); bl = (Blipl)in.readObject(); // UPS! Generie izuzetak:

Poglavlje 18: Javin ulazno-izlazni sistem

787

//! print("Rekonstruisanje b2:"); //! b2 = (Blip2)in.read0bject();

}
} /* Ispis: Pravljenje objekata: Konstruktor za Blipl Konstruktor za Blip2 Snimanje objekata: B1ipl.writeExternal B1ip2.writeExternal Rekonstruisanje bl: Konstruktor za Blipl Blipl.readExternal

* ///:O bjekat Blip2 nije rekonstruisan zato to bi pokuaj da se to uradi generisao izuzetak. Vidite li razliku izm edu klasa B lipl i Blip2? K onstruktor za B lip l je javan, dok konstrukto r za Blip2 nije, i to prouzrokuje izuzetak prilikom rekonstruisanja. K onstruktor za klasu Blip2 pretvorite u javni i uklonite kom entare iza //! da biste videli ispravne rezultate. Kada se objekat b 1 rekonstruie, poziva se podrazum evani konstruktor za B lip l. To se razlikuje od postupka rekonstruisanja objekta interfejsa Serializable, u kom e se objekat p o tp u n o rekonstruie na osnovu sauvanih podataka, bez pozivanja konstruktora. Uz objekat interfejsa E xternalizable podrazum evani konstruktor se ponaa na uobiajen nain (ukljuujui i inicijalizovanje tokom definisanja polja), a za tim se poziva m etoda read E xternal( ). O ovome m orate voditi rauna, posebno o injenici da se uvek pozivaju podrazum evani konstruktori, kako biste postigli ispravno ponaanje objekata interfejsa E xternalizable. Evo prim era koji pokazuje ta m orate da uradite da biste sauvali i rekonstruisali objekat tipa Externalizable:
//: u i / B 1 i p 3 .java // Rekonstruisanje objekta externalizable. import java.io.*; import static net. mi nd vi ew .u ti1 .P r i n t .*; public class Blip3 implements Externalizable { pri vate i nt i ; private String s; // Nema inicijalizacije publi c B1i p 3 () { p r i n t ("Konstruktor za Blip3"); // s, i nisu inici jal izovani

}
public Blip3(String x, int a) s = x; i = a; // s i i se inicij a l izuju samo u nepodrazumevanom k o n s tr uk to ru. { p r i n t ( " B l i p 3 (String x, int a)");

}
public String toString()

return

s +

i;

public void writeExternal(ObjectOutput out)

788

Misliti na Javi

throws IOException { print("Bl ip3.wri teExternal1 1 ); // Ovo morate da uradite: out.writeObject(s); out.writeInt(i);

}
public void readExternal(Objectlnput in) throws IOException, ClassNotFoundException { print("Blip3.readExternal"); // Ovo morate da uradite: s = (String)in.readObject(); i =in.readlnt();

}
public static void main(String[] args) throws IOException, ClassNotFoundException { print("Pravljenje objekata:"); Blip3 b3 = new B1 ip3( tekst 1 1 , 47); print(b3); ObjectOutputStream o = new ObjectOutputStream( new FileOutputStream("Bli p3.out")); print("Snimanje objekta:"); o.wri te0bject(b3); o. c los e O; // Sada ga vraamo: ObjectlnputStream in = new ObjectInputStream( new FileInputStream("Blip3.out")); print("Rekonstruisanje b3:"); b3 = (Blip3)in.readObject(); print(b3);

}
} /* Ispis: Pravljenje objekata: Blip3(String x, int a) tekst 47 Snimanje objekta: B1ip3.writeExternal Rekonstruisanje b3: Konstruktor za B1ip3 B1ip3.readExternal tekst 47

* ///:Polja s i i inicijalizuju se tek u drugom konstruktoru, a ne u podrazum evanom . Ako ih ne inicijalizujete u metodi readE xternal( ), s e imati vrednost null, a i vrednost nula, poto se m em orijska lokacija za nove objekte popunjava nulam a. Ako dva reda koda posle reenice Ovo m orate da uraite" pretvorite u kom entare i pokrenete program , videete da, nakon rekonstruisanja objekta, s im a vrednost null, a i ima vrednost nula. Ako izvodite neto iz objekta tipa Externalizable, obino ete pozivati verzije m etoda w rite E x te rn a l() i re a d E x te rn a l() osnovne kiase da biste obezbeili pravilno snim anje i rekonstruisanje kom ponenata osnovne klase.

Poglavlje 18: Javin ulazno-izlazni sistem

789

Da bi sve radilo kako treba, znai da, pored upisivanja vanih podataka iz objekta tokom izvravanja m etode w riteE xternal( ) (ne postoji podrazum evano ponaanje kojim se upisuju bilo koji lanovi objekta interfejsa Externalizable), m orate i da rekonstruiete te podatke u m etodi readE xternal( ). To isprva m oe da zbuni zato to bi ponaanje podrazum evanog konstruktora objekta Externalizable m oglo stvoriti pogrean utisak da se neka vrsta pam enja i rekonstruisanja deava autom atski. To se ne deava.

Veba 28: (2) U program u Blipovi.java kopirajte datoteku i preim enujte je u ProveraBlipova.java. Preimenujte i klasu Blip2 u ProveraBlipova (prepravite je u javnu, a ispred klase Blipovi uklonite oznaku public) . U klonite oznake //! iz datoteke i izvrite novi program . Potom postavite znakove za kom entar ispred podrazum evanog konstruktora za klasu ProveraBlipova. Izvrite program i objasnite zato radi. O bratite panju na to da nakon prevoenja m orate da izvrite program sa java Blipovi" zato to se m etoda m a in ( ) i dalje nalazi u klasi Blipovi. Veba 29: (2) U program u Blip3.java, pretvorite u kom entar po dva reda iza reenica
Ovo m orate da uradite: i pokrenite program . O bjasnite rezultat i zato se on razlikuje od sluaja kada su ta dva reda u program u.

Rezervisana re transient
Ako upravljate serijalizovanjem, m oda neete eleti da Javin m ehanizam serijalizovanja autom atski snim a i rekonstruie neki podobjekat. To je esto sluaj kada se u podobjektu uvaju poverljive inform acije koje ne elite da serijalizujete, npr. lozinka. ak i ako je takva inform acija u objektu privatna, nakon serijalizovanja neko bi mogao da je proita iz datoteke ili da je presretne prilikom prenosa preko mree. Jedan nain za spreavanje serijalizovanja osetljivih delova objekta jeste da se klasa realizuje kao Externalizable, kao to je upravo pokazano. Na taj nain nita se ne serijalizuje autom atski, to jest moete eksplicitno da serijalizujete sam o neophodne delove u n u tar m etode w riteE x te rn a I(). M eutim , ako radite sa objektom tipa Serializable, serijalizacija se obavlja autom atski. Da biste to kontrolisali, serijalizovanje bilo kog elem enta m oete da iskljuite rezervisanom reju tran sien t, koja kae: Nemoj da se trudi da ovo rekonstruie - ja u se postarati za to. Na prim er, posm atrajm o objekat P rijavljivanje koji uva inform acije o odreenom prijavljivanju na sistem. Pretpostavim o da nakon provere ispravnosti prijavljivanja elite da snim ite podatke, ali bez lozinke. To je najlake uraditi tako to se im plem entira (realizuje) interfejs Serializable a polje lozin ka oznai kao tra n sie n t. Evo kako to izgleda:
//: ui/P ri ja vl jivanje.java // Prikazuje korienje rezervisane reci transient. import j a va .u ti1 .c oncurrent.*; irrport java.io.*; import j a v a .ut i 1 .*; import static n e t . mi nd vi ew .u ti l.Print.*; public class Prijavljivanje implements Serializable { private Date datum = new D a t e ();

790

Misliti na Javi

private String korisnickolme; private transient String lozinka; public Prijavljivanje(String ime, String loz) korisnickolme = ime; lozinka = loz; {

}
public String toString() snickolme + "\n datum: " + datum + "\n lozinka: args) " + loz; { 1 1 + "korisniko ime: " + korireturn "informacije o prijavljivanju: \n

}
public static void main(String[] throws Exception { "mojMaliPoni"); Prijavljivanje a = new Prijavljivanje("Hulk", print( "prijavljivanje a = " + a); Ob je ctOutputStream o = new ObjectOutputStream( new Fi le Ou tputStream("Prijavljivanje.out")); o. wr i t e O b j e c t ( a ) ; o.closeO; T i m e U n it .S EC ON DS .s lee p( l); // Sada ih vraamo: Ob je ct ln pu tS tr ea m in = new ObjectInputStream( new Fi1eI np ut St re am (" Pr ij avl ji va nj e. ou t")); print("Rekonstruisanje objekta " + new Date()); a = (P ri javljivanje)in.readObject(); print( "prijavljivanje a = " + a); // Kanjenje

}
} /* Ispis: (primer) prijavljivanje a = informacije o prijavlj ivanju korisnicko ime: Hulk datum: Sat Nov 19 15:03:26 MST 2005 lozinka: mojMaliPoni Rekonstruisanje objekta Sat Nov 19 15:03:26 MST 2005 prijavljivanje a = informacije o prij av ljivanju korisnicko ime: Hulk datum: Sat Nov 19 15:03:26 MST 2005 lozinka: null

* ///:Vidi se da su polja datum i korisnickolm e obina (a ne transient) i zato se autom atski serijalizuju. M eutim , polje lozinka je oznaeno kao transient i zato se ne snim a na disk, niti m ehanizam serijalizovanja pokuava da ga rekonstruie. Kada se objekat rekonstruie, polje lozinka im a vrednost null. O bratite panju na to da ok toString( ) sastavlja String objekat preklopljenim operatorom +, referenca jednaka null autom atski se konvertuje u znakovni niz null. Moda ste uoili i to da je polje datum snim ljeno i rekonstruisano s iska, odnosno da nije pravljeno ponovo. Foto objekti interfejsa Externalizable podrazum evano ne uvaju nijedno polje, rezervisana re transient koristi se iskljuivo za objekte interfejsa Serializable.

Poglavlje 18: Javin ulazno-izlazni sistem

791

Alternativa za interfejs Externalizable


Ako vam se prim ena interfejsa Externalizable iz nekog razloga ne svia, isprobajte drugaiji pristup. Moete da realizujete interfejs Serializable i da m u dodate (obratite panju na to da sam rekao dodate, a ne ,,redefiniete ili im plem entirate") m etode writeO b ject( ) i readO bject( ) koje e se autom atski pozivati prilikom serijalizovanja i deserijalizovanja objekta. To jest, ako obezbedite te dve m etoe, one e se koristiti um esto podrazum evanih m etoda za serijalizaciju. M etode m oraju da imaju ba ovakve potpise:
private void wr iteObject(ObjectOutputStream tok)

throws IOException; private void readObject(ObjectInputStream tok)

throws IOException, C1assNotFoundException

S take gledita program iranja, ovo je prilino udno. Prvo m oete pom isliti da te metode, poto nisu deo osnovne klase ili interfejsa Serializable, treba da b u d u definisane u sopstvenim interfejsima. O bratite panju na to da su one definisane kao privatne, to znai da treba da ih pozivaju sam o drugi lanovi iste klase. M eutim , njih ne pozivaju drugi lanovi klase, ve m etode writeObject( ) i readO bject( ) klasa ObjectO utputStream i ObjectlnputStream . (Izuzetno se trudim da se ne uputam u iscrpljujuu raspravu o istim im enim a m etoda. Objanjenje: sam o bih vas zbunio.) M oda se pitate kako objekti klasa ObjectO utputStream i O bjectlnputStream im aju pistup privatnim m etodam a vae klase. M oemo sam o pretpostaviti da je to deo maginog postupka serijalizovanja.6 U svakom sluaju, sve to se definie u interfejsu podrazum evano je javno, pa ako metode w riteO bject( ) i readO bject( ) m oraju da budu privatne, one ne m ogu biti deo interfejsa. Poto m orate tano da se pridravate potpisa, uinak je isti kao da realizujete interfejs. Izgleda kao da se posle poziva m etode O bjectO utputStream .w riteObject( ) objekat interfejsa Serializable koji joj prosleujete ispituje (bez sum nje, korienjem refleksije) kako bi se ustanoviio cia li realizuje sopstvenu m etodu w riteO b ject( ). Ako je tako, preskae se uobiajeni postupak serijalizacije i poziva nam enska m etoda w riteO bject( ). Isto vai i za m etodu readO bject( ). Postoji jo jedna zakoljica. U nutar m etode w riteO bject( ) koju piete moete da izvedete podrazum evanu akciju upisivanja objekta pozivom m etode defaultW riteO bject( ). Slino, unutar m etode readO bject( ) moete da pozovete m etodu defaultR eadO bject( ). Evo jednostavnog prim era koji pokazuje kako m oete da upravljate uvanjem i rekonstruisanjem objekta koji realizuje interfejs Serializable:
//: ui/KontrolaSerij a l izovanja.java // Upravljanje serijalizovanjem dodavanjem sopstvenih // metoda w r i teObject() import java.io.*; public class KontrolaSerijalizovanja implements Serializable { i re adObject().

U o d e ljk u ,,I n te rfe jsi i p o d a c i o tip u n a k ra ju p o g la v lja Podaci o tipu p o k a z a n o je k a k o je m o g u e p ris tu p iti p riv a tn im m e to d a m a k a d a je p ozivalac izv an n jih o v e klase.

792

Misliti na Javi

private String a; private transient String b; public Ko nt ro laSerijalizovanja(String aa, String bb) a = "Nije transient; b = "transient: " + aa; " + bb; { return a + \n" + b; } {

}
public String t o S t r i n g O throws IOException { t o k . de fa ul tW ri te Ob jec t( ); t o k. wr it e O b j e c t ( b ) ; private void w r it eO bj ec t( Ob je ct Out pu tS tr ea m tok)

}
private void re ad Ob je ct (ObjectInputStream tok) throws IOException, Cl assNotFoundException { t o k. de fa ul tR ea dO bj ect (); b = (Str in g) to k. re ad Ob jec t( );

}
public static void main(String[] args) { throws IOException, C1 assNotFoundException "T e s t 2 " ) ; System.out.println("Pre:\n" + sc); By te Ar ra yO ut pu tS tr eam buf = new B y te Ar ra yO ut pu tS tr eam (); Obje ct Ou tp ut St re am o = new Ob j e c t O u t p u t S t r e a m ( b u f ) ; o. wr i t e O b j e c t ( s c ) ; // Sada ga vraamo: Object ln pu tS tr ea m in = new ObjectInputStream( new By t e A r r a y In pu tS t r e a m ( b u f .t o B y t e A r r a y ())); KontrolaSerijalizovanja sc2 = (KontrolaSerijalizovanja) in.readObjectO; Sy st em .o ut.println("Posle:\n" + sc2);

KontrolaSerijalizovanja sc = new Ko ntrolaSerijal izovanja("Testl",

}
} /* Ispis: Pre: Nije transient: Testl transient: Posle: Nije transient: Testl transient: Test2 Test2

* ///:U ovom p rim eru jedno polje tipa String je obino, a drugo je oznaeno kao transient kako bi se dokazalo da m etoda defaultW riteO bject( ) snim a i rekonstruie polje koje nije transient, a da se transient polje snim a i rekonstruie eksplicitno. Polja se inijalizuju n konstruktoru, a ne prilikom definisanja, kako bi se dokazalo da se ne inicijalizuju nekim autom atskim m ehanizm om tokom deserijalizovanja. Ako nam eravate da koristite podrazum evani m ehanizam za upis delova objekta koji nisu oznaeni sa transient, m orate da pozovete m etodu efaultW riteObject( ) kao prvu operaciju u m etodi w riteO bject( ), odnosno m etodu defaultReadObject( ) kao prvu operaciju u m etodi readO bject( ). To su udni pozivi metoda. Izgledalo bi, na primer, kao da

Poglavlje 18: Javin ulazno-izlazni sistem

793

pozivate m etodu defau!tW riteO bject( ) za objekat klase O bjectOutputStream i ne prosleujete nikakve argum ente, ali se on ipak nekako snalazi - dobija referencu na va objekat i zna kako da upie sve delove koji nisu oznaeni sa transient. Da oveka p odiu marci. Za uvanje i rekonstruisanje polja oznaenih sa transient koristi se razumljiviji kod. Ipak, razm islite o tom e ta se deava. U funkciji m a in ( ) pravi se objekat KontroIaSerijalizovanja koji se p o tom serijalizuje u tok tipa ObjectOutputStream . (O bratite panju na to da se u ovom sluaju um esto datoteke koristi m em orijski blok, kao i za ObjectO utputStream.) Serijalizacija se deava u redu:
o.writeObject(sc);

M etoda w riteO bject( ) m ora da ispituje objekat sc da bi ustanovila im a li sopstvenu m etodu w riteO b ject( ). (Ne proveravanjem interfejsa, jer on ne postoji, niti tipa ldase, ve stvarnim traenjem m etode pom ou refleksije.) Ako pronae tu m etodu, iskoristie je. Slian pristup se koristi i u m etodi readO bject( ). M oda je ovo bio jedini nain za reavanje problem a, ali je ipak udan.

Zadavanje verzije
M oda ete hteti da prom enite verziju klase koja se m oe serijalizovati (na prim er, objekti originalne klase m ogu biti uvani u bazi podataka). To je podrano u favi, ali ete to raditi sam o u specijalnim sluajevima, a pri tom m orate bolje razum eti problem , to ovde neemo razm atrati. HTM L dokum entacija razvojnog paketa za Javu koja se m oe preuzeti s lokacije http:lljava.sun .co m , detaljno obrauje ovu temu. U ovoj dokum entaciji uoiete m noge kom entare koji poinju sa:
U pozorenje: Serijalizovani objekd ove klase nee biti ko m p a tib iln i s buduim verzijam a Svvinga. T renutna podrka serijalizaciji pogodna je za kratkotrajno uvanje m etoda koje se pozivaju daljinski u raznim aplikacijam a...

Ovo upozorenje stoji zato to je m ehanizam dodeljivanja verzije previe jednostavan da bi pouzdano radio u svim situacijam a, naroito sa zrnim a Jave. Radi se na ispravci projekta, o em u upravo i govori ovo upozorenje.

Korienje trajnosti
Tehnika serijalizovanja je prilino privlana za uvanje stanja nekog program a da bi se on kasnije m ogao lako vratiti nazad. M eutim , pre toga, m ora se odgovoriti na neka pitanja. ta e se desiti ako se serijalizuju dva objekta, a svaki od njih sadri referencu na trei objekat? Kada rekonstruiete ta dva objekta iz serijalizovanog stanja, da li e se trei objekat pojaviti sam o jednom ? ta e se desiti ako se dva objekta serijalizuju u zasebne datoteke, a potom deserijalizuju u razliitim delovima koda? Evo prim era koji prikazuje ovaj problem:
//: ui/MojSvet.java import java.io.*; import j a v a . u t i 1.*; import static n e t .mindview.uti1 .P r i n t .*;

794

Misliti na Javi

class Kucica implements Se rializable {) class Zivotinja implements Serial iz ab le { private String ime; private Kucica omiljenaKucica; Zivotinja(String im, Kucica k) ime = im; omiljenaKucica = k; {

}
public String t o S t r i n g O { return ime + "[" + super.toString() + "], + omiijenaKucica + "\n";

} }
public class MojSvet { public static void main(String[] Kucica kucica = new Kucica(); List<Zivotinja> zivotinje = new Ar ra y L i s t < Z i v o t i n j a > ( ) ; zivotinje.add(new Zivotinja("pas Lesi", kucica)); zivotinje.add(new ZivotinjaC'mrmot Bobi", kucica)); zivotinje.add(new Zi vo tinja("maka Cuca", print("ivotinje: " + zivotinje); ByteArrayOutputStream bufl = new B y te Ar ra yO ut pu tS tr eam (); ObjectOutputStream ol = new Ob je ct O u t p u t S t r e a m ( b u f l ) ; ol.writeOb je ct (z iv oti nj e); 01.wri te Ob je ct (z iv oti nj e); // Upisivanje drugog skupa // Upisivanje u drugaiji tok: By teArrayOutputStream buf2 = new B y te Ar ra yO ut pu tS tr eam (); ObjectOutputStream o2 = new 0b j e c t 0 u t p u t S t r e a m ( b u f 2 ) ; 02.wri te 0b je ct (z iv oti nj e); // Sada ih vraamo: ObjectlnputStream inl = new ObjectInputStream( new B y t e Ar ra yI np ut St re am( bu f1 .t o B y t e A r r a y ())); ObjectlnputStream in2 = new ObjectInputStream( new B y t e A r r a y I np ut St re am( bu f2 .t oB yt eA rr ay())); List zivotinjel = (L ist)inl.r e a d O b j e c t (), zivotinje2 = ( L is t) in l. re ad Ob je ct(), zivotinje3 = (List)in2.r e a d O b j e c t (); print("ivotinjel: print("ivotinje2: print("ivotinje3: " + zivotinjel); " + zivotinje2); " + zivotinje3); kucica)); args) { throws IOException, C1 assNotFoundException

}
} /* Ispis: ivotinje: (primer) [pas L e s i [ Z iv ot in ja @l cc 76 c], Kucica?lcc769

, mrmot Bobi [Z iv otinja@lcc76d], Kucica@lcc769 , maka C u c a [Z iv ot in ja @l cc 76e ], Kucica@lcc769

Poglavlje 18: Javin ulazno-izlazni sistem

795

ivotinjel: [pas Le si [Z iv ot in je @l cc aOc ], Kucica@lccal6 , mrmot Bobi[Z iv ot in ja @l cc al7 ], Kucica@lccal6 , maka Cuca[Z iv ot in ja @l cc alb ], Kucica@lccal6

]
ivotinje2: [pas Le si [Z iv ot in je Ol cc aOc ], Kucica@lccal6 , mrmot Bobi[Z iv ot in ja @l cc al7 ], Kucica@lccal6 , maka Cuca[Z iv ot in ja @l cc alb ], Kucica@lccal6

]
ivotinje3: [pas Lesi[Z iv ot in je @l cc a52 ], Kucica@lcca5c , mrmot Bobi [Zivotinja@lcca5d], Kucica@lcca5c , maka Cu ca [Z iv ot in ja @l cc a61 ], Kucica@lcca5c

] * ///:Ovde je zanimljivo to da serijalizaciju objekata m oete koristiti uz niz bajtova, kao nain za tem eljno kopiranje svih objekata koji se m ogu serijalizovati. (Temeljno kopiranje znai da se kopira itava mrea objekata, a ne sam o osnovni objekat i njegove reference.) Kopiranje objekata je detaljno obraeno u m renim dodacim a ove knjige. Objekti klase Zivotinja sadre polja tipa Kucica. U funkciji m a in ( ), pravi se lista tih ivotinja i dvaput se serijalizuje u jedan, a zatim i u drugi tok. Kaa se ti tokovi deserijalizuju i ispiu, videete rezultate prikazane za jedno izvravanje (objekti e pri svakom pokretanju biti na razliitim lokacijama u m em oriji). Naravno, oekuje se da deserijalizovani objekti imaju drugaije adrese od originalnih. M edutim , u objektim a zivotinjel i zivotinje2 pojavljuju se iste adrese, ukljuujui i reference na objekat klase Kucica koji oba objekta sadre. S druge strane, prilikom rekonstruisanja objekta zivotinje3, sistem ne zna da su objekti u drugom toku isti kao objekti u prvom toku, pa pravi po tp u n o razliitu m reu objekata. Kad serijalizujete sve objekte u jedan tok, m oi ete da rekonstruiete mreu objekata koju ste napisali, bez sluajnog dupliranja objekata. Naravno, moete da prom enite stanje objekata u periodu izm edu pisanja prvog i poslednjeg, ali za to sami odgovarate: u trenutku kada ih serijalizujete, objekti e biti upisani u stanju u kom e se nalaze (i s postojeim vezam a do drugih objekata). Ukoliko elite da zabeleite stanje sistema, najbezbednije je da se to obavi kao nedeljiva operacija. Ako serijalizujete neto, pa ponete da radite neto drugo, zatim opet serijalizujete itd., neete moi bezbedno da zabeleite stanje sistema. Umesto toga, smestite sve objekte koji odslikavaju stanje sistema u isti kontejner koji ete snim iti u jednoj operaciji. Na taj nain moi ete da ga rekonstruiete pozivom sam o jedne metode. Sledei prim er je sistem za projektovanje pom ou raunara (CAD) koji ilustruje ovaj princip. Pored toga, prim er obrauje i statika polja: u dokum entaciji ete videti da se objekat klase Class moe serijalizovati, pa bi statika polja trebalo lako da se uvaju tako to bi se jednostavno serijalizovali objekti te klase. U svakom sluaju, to zasad izgleda kao pam etan pristup.
//: ui/PamcenjeCADStanja.java // Pamenje stanja nazovi-CAD sistema. import java.io.*; import j a v a . u t i l .*;

796

Misliti na Javi

abstract class Oblik implements Serializable { public static final int CRVENA = 1, PLAVA = 2, ZELENA = 3; private int xPos, yPos, dimenzija; private static Random rand = new Random(47); private static int brojac = 0; public abstract void postaviBoju(int novaBoja); public abstract int citajBoju(); public 0blik(int x V a l , int yVal, int dim) xPos = x V a l ; yPos = y V a l ; dimenzija = dim; {

}
public String toString() return getClass() + 1 1 boja[" + c i t a j B o j u O + "] x P o s [ + xPos + "] yPos[" + yPos + "] dim[" + dimenzija + ]\n"; {

}
public static Oblik slucajnoGenerisanje() int xVal = r a nd .n ex tl nt (1 00 ); int yVal = r a n d .n ex tl nt (1 00 ); int dim = r a n d .n ex tl nt (1 00 ); switch(brojac++ % 3) { default: case 0: return new Krug(xVal, y V a l , dim); case 1: return new K v a d r a t ( x V a l , y V a l , dim); case 2: return new Linija(xVal, y V a l , dim); {

} }

class Krug extends Oblik { private static int boja = CRVENA; public Krug(int x V a l , int y V a l , int dim) super(xVal, y V a l , dim); {

}
public void postaviBoju(int novaBoja) public int citajBoju() { return boja; { boja = novaBoja; } |

}
class Kvadrat extends Oblik { private static int boja; public Kvadrat(int x V a l , int y V a l , int dim) super(xVal, yVal, dim); boja = CRVENA; {

}
public void postaviBoju(int novaBoja) public int citajBoju() { return boja; { boja = novaBoja; } }

class Linija extends Oblik {

Poglav|je 18: Javin ulazno-izlazni sistem

797

private static int boja = CRVENA; public static void serijal izujStaticke(ObjectOutputStreain os) throws IOException { os .w r i t e l n t ( b o j a ) ; } public static void deserijal iz ujStaticke(ObjectInputStream os) throws IOException { boja = o s . r e a d I n t ( ) ; public Linija(int xVal, int y V a l , int dim) super(xVal, yVal, dim); { }

}
public void postaviBoju(int novaBoja) public int citajBoju() { return boja; { boja = novaBoja; } }

}
public class PamcenjeCADStanja { public static void main(String[] args) throws Exception { ); List<Class<? extends O b l i k tipoviOblika = new ArrayList<Class<? extends O b l i k ( // Dodavanje referenci na objekte klase: tipoviObli ka .a dd (K rug .c la ss ); tipo vi Ob li ka .a dd (K vad ra t. cl as s); tipoviObli ka.add(Linija.cl a s s ) ; List<Oblik> oblici = new A r r a y L i s t <0 blik>( ); // Pravljenje nekih oblika: for(int i = 0; i < 10; i++) o b l i c i .a d d (Obli k.sluc aj no Ge ne ris a nj e( )) ; // Postavljanje svih statikih boja na ZELENA: for(int i = 0; i < 10; i++) ( (Obli k ) ob lic i. ge t(i) ) .po st av iB oj u( 0b li k . Z E LE NA ); // Snimanje vektora stanja: ObjectOutputStream out = new 0bject0utputStream( new FileOutp ut St re am (" CAD St an je .o ut ") ); out.wri te O b j e c t (ti povi O b l i k a ) ; Linija.serijalizujStaticke (out); o u t ,writeObject(obli c i ); // Prikazivanje oblika: Sy s t e m . o u t .pri ntln( ob li c i );

}
} /* Ispis: [class Krug boja xPos yPos dim , class Kvadrat boja, xPos yPos dim , class Linija boja xPos yPos dim , class Krug boja xPos yPos dim , class Kvadrat boja xPos yPos dim , class Linija boja xPos yPos dim , class Krug boja xPos yPos dim , class Kvadrat boja xPos yPos dim , class Linija boja xPos yPos dim , class Krug boja xPos yPos dim

] ///:-

798

Misliti na Javi

Klasa Oblik realizuje interfejs Serializable, pa se sve to se izvodi iz klase Oblik automatski m oe serijalizovati. Svaki objekat klase Oblik sadri podatke, a svaka klasa izvedena iz klase Oblik sadri statiko polje koje odreuje boju svih figura izvedenog tipa. (Stavljanje statikog polja u osnovnu klasu dalo bi samo jedno polje, poto se statika polja ne dupliraju u izvedenim klasama.) M etode osnovne klase se m ogu redefinisati da bi se zadala boja razliitih tipova oblika (za statike m etode se ne prim enjuje dinam iko povezivanje, tj. radi se o obinim m etodam a.) M etoda slucajnoG enerisanje( ), kad god se pozove, pravi razliit objekat tipa Oblik tako to nasum ino bira tip oblika. Klase Krug i Kvadrat jednostavna su proirenja klase Oblik; razlikuju se sam o po tom e to se u klasi Krug boja inicijalizuje tokom definisanja, a u klasi Kvadrat - u konstruktoru. O dloiem o razm atranje klase Linija. U funkciji m a in ( ) koristi se jedna lista tipa ArrayList za uvanje objekata tipa Class i jo jedna za uvanje oblika. R ekonstrukja objekata je prilino jednostavna:
//: ui /R ekonstruisanjeCADStanja.java // Rekonstruisanje stanja nazovi-CAD sistema. // {RunFirst: PamcenjeCADStanja} import java.io.*; import j a v a . u t i l .*; public class RekonstruisanjeCADStanja { @SuppressWarni ngs("unchecked") public static void main(String[] args) throws Exception { Ob je ct lnputStream in = new ObjectInputStream( new Fi 1e In putStream("CADStanje.out")); // itanje po redosledu upisivanja: List<Class<? extends O b l i k tipoviOblika = (List<Class<? extends Obl i k ) in.rea dO bj ec tf ); Line.deserijalizujStati cke(i n ) ; List<Oblik> oblici = (L is t<0blik>)in.readObject(); System.out.pri ntln(obl ici);

}
} /* Ispis: [class Krug boja xPos yPos dim , class Kvadrat boja, xPos yPos dim , class Linija boja xPos yPos dim , class Krug boja xPos yPos dim , class Kvadrat boja xPos yPos dim , class Linija boja xPos yPos dim , class Krug boja xPos yPos dim , class Kvadrat boja xPos yPos dim , class Linija boja xPos yPos dim , class Krug boja xPos yPos dim

] *///:-

Poglavlje 1 8: Javin ulazno-izlazni sistem

799

Vidi se da se vrednosti xPos, yPos i dim uspeno snim aju i rekonstruiu, ali neto nije u redu sa rekonstruisanjem statikih informacija. Sve tri ulaze, ali ne izlaze neprom enjene. Krugovi im aju vrednost 1 (CRVENA, po definiciji), a kvadrati im aju vrednost 0 (setite se da su inicijalizovani u konstruktoru). Izgleda kao da se statika polja uopte nisu serijalizovala! To je tano: iako klasa Class moe da se serijalizuje, ona ne radi ono to se oekuje. Znai, ako elite da serijalizujete statike podatke, to m orate da uradite sami. U tu svrhu slue statike m etode serijalizujStaticke( ) i deserijalizujStaticke( ) klase Linija. V idite da se one eksplicitno pozivaju u postup ku snim anja i rekonstruisanja. (O bratite panju na to da m ora biti odran redosled upisivanja i itanja iz datoteke za serijalizaciju.) Dakle, da bi ovi program i radili ispravno, m orate da:

1 . D odate m etode serijalizujStaticke() i deserijalizujStaticke() u sve klase oblika.


2. U klonite listu tipo viO b lika i kod koji je povezan s tom listom. 3. U oblike dodate pozive novih statikih m etoda za serijalizovanje i deserijalizovanje. Bezbednost je jo jedna tem a o kojoj bi valjalo razmiljati, poto se serijalizovanjem uvaju i privatni p o d a . Ako imate problem s bezbednou, ta polja treba da oznaite kao tra n sie n t. M edutim , tada m orate da smislite bezbedan nain za uvanje poverljivih inform acija tako da prilikom rekonstruisanja m oete da vratite privatne prom enljive u prvobitno stanje. Veba 30: (1) Popravite program CA D Stanje.java kako je opisano u tekstu.

XML
Vano ogranienje serijalizovanja objekata jeste njihova iskljuiva upuenost na Javu: takve objekte m ogu da deserijalizuju samo Java program i. Konverzijom podataka u form at XML dobilo bi se interoperabilnije reenje koje bi mogle da koriste razne platform e i jezici. Zbog rairenosti XML-a postoji zbunjujue velik broj opcija za program iranje koje ga proizvode; m eu njim a su i biblioteke javax.xm l.,< ' koje se isporuuju uz razvojni paket )ave. O dluio sam da upotrebim biblioteku otvorenog izvornog koda XOM (preuzim anje i dokum entacija na w w w .xo tn .m i ) - autor je Elliotte Rusty Harold. Izgleda mi da je to najjednostavniji nain da se XML proizvodi i m enja u Javi. Pored toga, XOM proizvodi veoma ispravan XML kod. Prim era radi, pretpostavim o da imate objekte tipa O soba i u njim a im ena i prezim ena koja elite da serijalizujete u form atu XML. Naredna klasa O soba ima m etodu getX M L () koja pom ou XOM-a proizvodi podatke o klasi O soba konvertovane u XML Elem ent, i konstruktor koji prim a Elem ent i vadi odgovarajue podatke o klasi O soba (obratite panju na to da su XML primeri u sopstvenom poddirektorijum u):
//: x m l / O s o b a .java // Upotreba biblioteke X0M za pisanje i itanje XML-a // {Zahteva: nu.xom.Node; Morate instalirati // biblioteku X0M sa http://www.xom.nu }

800

Misliti na Javi

import nu.xom.*; import java.io.*; import ja v a . u t i 1.*; public class Osoba { private String im, prez; public Osoba(String im, String prez) this.im = im; this.prez = prez; {

}
// Napravi XML Element od ovog objekta Osoba; public Element getXML() { Element osoba = new E l em en t( "o so ba "); Element ime = new El e m e n t ( " i m " ) ; ime.appendChild(im); Element prezime = new El em en t ( " p r e z " ) ; p r e z i m e. ap pe nd Ch il d(p re z); o s o b a. ap pe nd Ch il d( ime ); o s o b a . a p pe nd Ch il d( pre zi me ); return osoba;

}
// Konstruktor za rekonstrukciju Osobe iz XML Elementa: public Osoba(Element osoba) { im= osoba.getFirstChildElement("im").getVal u e ( ) ; prez = osoba. ge tF ir st Ch il dEl em en t( "p re z" ). ge tV alu e( );

}
public String t o S t r i n g O public static void format(OutputStream os, Document doc) throws Exception { S erializer serializer= new Serializer(os,"IS0-8859-l"); seriali ze r.s e t l n d e n t (4); s e r i a l iz er .s et Ma xL eng th (6 0); s e r i a l i z er .w ri te (d oc); serial izer.fl ush (); { return im + " " + prez; } // Neka bude itljivo za ljude:

}
public static void main(String[] new Osoba("Dr. Bunsen", new Osoba("Gonzo", new O s o b a (Phi11 ip J.", Sy st em .o ut .p ri nt ln (lj u d i ) ; Element koren = new El em e n t ( " l j u d i " ) ; for(Osoba p : 1j u d i ) k o re n. ap pe nd Ch il d( p.g et XM L( )) ; Document doc = new D o c u m e n t ( k o r e n ) ; format(System.out, doc); fo rmat(new Bu ff er ed OutputStream(new FileOutputStreamf "Ljudi .x ml) ) , d o c ) ; args) throws Exception { List<Osoba> Ijudi = Arrays.asList( "Honeydew"), "Fry")); "The Great"),

Poglavlje 18: Javin ulazno-izlazni sistem

801

} /* Ispis: [Dr. Bunsen Honeydew, Gonzo The Great, Phillip J. Fry] <?xml version="1.0" encoding="IS0-8859-l"?> < 1judi> <osoba> <im>Dr. Bunsen</im> <prez>Honeydew</prez> </osoba> <osoba> <im>Gonzo</im> <prez>The Great</prez> </osoba> <osoba> <im>Phi11ip J.</im> <prez>Fry</prez> </osoba> </ljudi>

* ///:XOM m etode su jasne same po sebi, a mogu se nai i u XOM dokum entaciji. XOM sadri i klasu S erializer koja je u m etodi f o r m a t( ) upotrebljena za pretvaranje XML-a u itljiviji oblik. Ukoliko sam o pozovete to X M L (), dobiete sve baeno na gomilu, pa je S erializer podesna alatka. Jednostavno je i deserijalizovanje objekata tipa O soba iz XML datoteka:
//: x m l / L J u d i .java // {Zahteva: nu.xom.Node; Morate instalirati // biblioteku X0M sa http://www.xom.nu } // {RunFirst: Osoba} import nu.xom.*; import j a v a . u t i l .*; public class Ljudi extends A r ra yL ist<Osoba> { public Ljudi(String imeDatoteke) throws Exception Document doc = new Bui1d e r ( ) .b u i1d( im eD at ot ek e); Elements elementi = do c . g e t R o o t E l e m e n t ().getC hi1d E1 e m e n t s (); for(int i = 0; i < e l e m e n t i .size(); i++) add(new O s o b a ( e l e m e n t i .g e t (i) ) ) ; {

}
public static void main(String[] System.out.println(p); args) throws Exception { Ljudi p = new Ljudi ("Ljudi ,xml");

}
} /* Ispis: [Dr. Bunsen Honeydew, Gonzo The Great, Phillip J. Fry]

* ///= K onstruktor klase Ljudi otvara i ita datoteku XOM-ovom m etodom B u ild er.b u ild (), a m etoda g etC h ild E lcm e n ts() proizvodi listu Elem ents (to nije standardna Javina lista, nego objekat koji im a samo m etode s iz e () i g e t( ) - Harold nije eleo da natera korisnike

802

Misliti na Javi

da upotrcbljavaju Javn SE5, ali je ipak eleo da ima kontejner koji om oguuje bezbedan rad s tipovim a). Svaki Element u toj listi predstavlja objekat tipa Osoba, pa se predaje drugom k onstrukto ru klase Osoba. Vodite rauna o tom e da m orate unapred znati struktu ru svoje XML datoteke, ali ba to je esto sluaj u ovoj vrsti problem a. Ako ta struktura ne odgovara onom e to oekujete, XOM e generisati izuzetak. Svakako da m oete pisati i sloeniji kod koji e ispitati XML d o k um en t um esto da o njem u ita pretpostavlja, u sluajevima kada im ate m anje konkretne inform acije o dolaznoj XML strukturi. Da bi ovi prim eri mogli da se prevedu, m oraete da stavite JAR datoteke iz XOM distribucije u svoju putan ju klasa. Ovo je bio sam o kratak uvod u XML program iranje na Javi i s bibliotekom XOM; vie inform acija potraite na adresi w w w .xo m .n u .

Veba 31: (2) Program im a Osoba.java i Ljudi.java dodajte odgovarajue podatke o adresi. Veba 32: (4) Koristei M ap<String,Integer> i uslunu klasu net.mindview. util.TextFile, napiite program koji prebrojava rei u datoteci (kao drugi argum ent u konstruktoru klase TextFile upotrebite "\\W +"). Rezultate sauvajte u obliku XML datoteke.

Preferences
Interfejs za program iranje aplikacija (API) Preferences m nogo je blii konceptu trajnosti nego serijalizovanju objekata, zato to inform acije autom atski skladiti i vadi iz skladita. M eutim , m oe se prim enjivati sam o na oreene male skupove podataka - na proste tipove i znakovne nizove (objekte tipa S tring), a svaki uskladiten znakovni niz ne sme biti dui od 8 K (nije ba da je siuno, ali nije za ozbiljne poslove). Kao to mu ime govori, API Preferences slui za skladitenje i vadenje param etara koje je izabrao korisnik i postavki konfiguracije program a. Poeljni param etri i postavke sm etaju se u skupove klju - vrednost (kao m ape), uskladitene u hijerarhiji vorova. lako se u hijerarhiji vorova mogu napraviti sloene strukture, obino se pravi sam o jedan vor nazvan po klasi o kojoj se inform acije skladite u njem u. Evo jednostavnog prim era:
//: ui/PrimerZaPreferences.java import java.util.prefs.*; import static net.mi nd vie w .u t i 1 .P r i n t .*; public class PrimerZaPreferences {

public static void m a i n ( S t r i n g [] args) throws Exception { Preferences prefs = Preferences .u s e r N o de Fo rP ac ka ge (Pr im er Za Pr ef er en ce s.c l a s s ) ; prefs.put("Mesto", "Oz"); p r e f s .p u t ( " O b u a " , "Crvene papuice"); p r e f s .p u tl n t ( " D r u g a r a " , 4); pr ef s.putBoolean("Ima li vetica?", t r u e ) ; int brojUpotreba = pr ef s. ge tI nt (UsageCount", 0); brojUpotreba++; prefs.putInt("UsageCount", br o j U p o t r e b a ) ; for(String kljuc : p r e f s .k e y s ()) print(kljuc + ": "+ prefs.get.(kl juc, nul 1 ) ) ;

Poglavlje 18: Javin ulazno-izlazni sistem

803

// Uvek morate zadati podrazumevanu vrednost: print("Koliko drugara ima Doroti? " + prefs.getlntC'Drugara", 0));

}
} /* Ispis: Mesto: Oz Obua: Crvene papuice Drugara: 4 Ima li vetica?: true UsageCount: 53 Koliko drugara ima Doroti? 4 (primer)

* ///:O vde je upotrebljena m etoda userNodeForPackage( ), a m ogao sam odabrati i system N odeForPackage( ); izbor je donekle proizvoljan, ali ideja je da se ,,user upotrebljava za poeljne param etre i postavke pojedinanih korisnika, a ,,system za konfiguraciju cele instalacije. Poto je m etoda main( ) statina, za identifikaciju vora upotrebljena je klasa PrimerZaPreferences.cIass, ali u nutar nestatine m etode obino se koristi getC lass( ). Kao identifikator vora ne m orate da koristite tekuu klasu, ali je to uobiajena praksa. N akon to napravite vor, on vam je na raspolaganju za upisivanje ili itanje podataka. U p reth o d n o m p rim eru u vor su uitane razliite vrste stavki, a zatim je pozvana m etoda keys( ). N jen rezultat su kljuevi (engl. keys) u obliku znakovnog niza (String[]), to poznavaoci istoim ene m etode iz biblioteke kolekcija ne bi oekivali. O bratite panju na drugi argum ent m etode g e t( ). To je podrazum evana vrednost koja se vraa kao rezultat ukoliko se vrednost za dati klju ne pronade. Tokom iteracija kroz skup kljueva uvek znate da neka stavka postoji, pa je korienje null kao podrazum evane vrednosti bezbedno. Uobiajeno biste pribavljali neki imenovani klju, kao u:
pr ef s. ge tI nt (" Dr ug ara ", 0));

U n orm aln om sluaju treba da zadate razum nu podrazum evanu vrednost. Zapravo, ovo je jedan od tipinih idioma:
int brojUpotreba = prefs.getInt("UsageCount", 0); brojUpotreba++; p r ef s. pu tl nt (" Us ag eCo un t", b r oj Up ot re ba );

Na taj nain, U sageC ount e biti nula kada program pokrenete prvi put, ali e u kasnijim pokretanjim a biti razliit od nule. Kada pokrenete PrimerZaPreferences.java, videete da se UsageCount zaista poveava za jedan svaki put kada pokrenete program . Ali gde se taj broj uva? Nema lokalne datoteke koja bi se pojavila nakon prvog pokretanja program a. API Preferences za obavljanje tog svog posla koristi odgovarajue sistemske resurse, a oni se m enjaju u zavisnosti od operativnog sistema. U Windovvsu se za to upotrebljava registar (poto je on ionako hijerarhija vorova s parovim a klju - vrednost). Poenta je sledea: inform acije se autom atski skladite, a mi ne m oram o da brinem o o tom e kako se to obavlja u bilo kojem pojedinanom sistemu.

804

Misliti na Javi

API Preferences um e vie nego to je ovde prikazano. Dalje pojedinosti o tom e potraite u dokum entaciji razvojnog paketa JDK, koja je prilino razumljiva. Veba 33: (2) Napiite program koji prikazuje tekuu vrednost direktorijum a nazvanog osnovni direktorijum i trai od vas njegovu novu vrednost. Za skladitenje te vrednosti upotrebite API Preferences.

Saetak
Javina biblioteka U/I tokova zadovoljava osnovne zahteve: om oguuje itanje sa konzole i upisivanje u nju, u datoteke, u m em orijske blokove, ak i preko Interneta. Pom ou nasleivanja moete da napravite nove vrste ulaznih i izlaznih objekata. Moete ak i da izmcnite nain na koji tokovi rade sa objektim a tako to ete redefinisati m etodu to S trin g () koja se automatski poziva kada objekat prosledite m etodi iji je argum ent tipa String (Javina ograniena autom atska konverzija tipa). U dokum entaciji i projektu biblioteke U /I tokova, neka pitanja su ostala nedoreena. Na prim er, bilo bi lepo kada biste mogli da generiete izuzetak pri pokuaju pisanja preko postojee datoteke tokom njenog otvaranja; neki program erski sistemi om oguuju da zadate otvaranje izlazne datoteke, ali sam o ako ona ve ne postoji. Izgleda da u Javi treba koristiti objekat klase File kako bi se odredilo da li datoteka postoji, zato to e se ta datoteka uvek menjati ako je otvarate kao FileOutputStream ili FileVVriter. Biblioteka U/I tokova izaziva oprena oseanja; ipak, radi dosta toga i prenosiva je. Ukoliko ne razum ete projektni obrazac Decorator, njen projekat nije intuitivan pa je potreban dodatni napor da se ona objasni i razum e. Istovrem eno, biblioteka nije zaokruena: na prim er, ne bi trebalo da ja m oram da piem uslune klase kao to je TextFile (nova (SE5) Javina klasa PrintVVriter predstavlja korak u pravom sm eru, ali to je tek deliinino reenje). Java SE5 je donela veliko poboljanje: najzad je dodato form atiranje izlaza kakvo oduvek podravaju gotovo svi ostali jezici. Kada shvatite dekoratorski projektni obrazac i ponete da upotrebljavate ovu biblioteku tam o gde je potrebna njena prilagodljivost, uvideete da je korisna i tada vam nekoliko dodatnih redova koda vie nee predstavljati problem .
R eenja o d a b ra n ih vebi d a ta su u e le k tro n sk o m d o k u m e n tu The Thinking in Java A naotated Soln-

tion Guide, koji se m o e k u p iti na W eb lokaciji w ww.M iiuiView .net.

Nabrojani tipovi
R ezervisana re e n u m slui zapravljenje novog tipa od ogranienogskupa itnenovanih vrednosti; zhog tije se te vrednosti tretiraju kao p u tiopravne kotnponenteprogratna. Ispostavlja se d a je to veom a ko risn o .1
N a b ro ja n e tip o v e u k r a tk o sm o p re d s ta v ili n a k ra ju POGLAVLJA I n ic ij a l iz a c ij a I

ienje. M eutim , poto sada razum ete i neke kom plikovanije oblasti u Javi, m oem o detaljnije da razm otrim o nabrojane tipove (engl. en u m era ted types ) u Javi SE5. Videete da

s nabrojanim tipovim a m oe da se uradi i poneto zanimljivo, ali ovo poglavlje bi trebalo da vam prui bolji uvid i u druge m ogunosti jezika koje ste dosad upoznali, kao to su generiki tipovi i refleksija. Nauiete i jo nekoliko projektnih obrazaca.

Osnovne mogunosti nabrojanih tipova


Kao to je bilo reeno u poglavlju Inicijalizacija i ienje, kroz listu en u m konstanti m oete iterirati m etodom v a lu e s() tog nabrojanog tipa. M etoda v a lu e s() proizvodi niz en um konstanti u poretku njihovog deklarisanja; dobijeni niz moete (na prim er) upotrebiti u petjji foreach. Kada napravite nabrojan tip, prevodilac proizvodi njem u pridruenu klasu. O na autom atski nasleduje klasu java.lang.E num , to joj daje odreene m ogunosti koje u prikazati u narednom prim eru:
//: nabrojani/KlasaEnum.java // Mogunosti klase Enum import static ne t, mindview.uti1 .Print.*; enum Odmor { MORE, PLANINA, BANJA } public class KlasaEnum { public static void m a i n ( S t r i n g [] args) for(0dmor o : Od mo r . v a l u e s ()) { print(o + " rednibroj: " + o . o r d i n a l ()) ; + " "); + " "); printnb(o.compareTo(Odmor.PLANINA) printnb(o.equals(Odmor.PLANINA) print(o == O d mo r. PL AN IN A); prin t( o. ge tD ec la ri ngC la ss () ); pri n t (o .n am e( )); p r i n t ( ------------------------- "); {

}
// Pravi nabrojani tip od znakovnog niza: { for(String o : "BANJA PLANINA M O R E " .s p l i t (" ")) Odmor odm = Enum.val ue Of (O dm or .cl as s, o ) ; pri n t (o dm );

}
Pri p is a n ju o v o g p o g la v lja m n o g o mi je p o m o g a o J o s h u a B loch.

806

Misliti na Javi

}
} /* Ispis: MORE rednibroj: 0 -1 false false class Odmor MORE PLANINA rednibroj: 0 true true class Odmor PLANINA BANJA rednibroj: 2 1 false false class Odmor BANJA BANJA PLANINA MORE 1

* ///:M etoda o rd in al( ) proizvodi ceo broj koji pokazuje redosle deklarisanja svake enum instance, poev od nule. enum instance uvek moete bezbedno porediti operatorom ==, i metode equals( ) i hashC ode( ) bivaju autom atski napravljene. Klasa Enuni realizuje interfejs Comparable, stoga ima svoju m etodu com pareTo( ), a realizuje i interfejs Serializable. Pozivom m etode getD eclaringClass( ) za enum instancu, dobiete obuhvatajuu enum klasu. M etoda n am e( ) daje ime tano onako kako je bilo deklarisano, a isto to dobijate i metodom to S trin g ( ). valueO f( ) je statini lan klase Enum i proizvodi enum instancu koja odgovara znakovnom nizu koji joj je prosleen ili baca izuzetak ukoliko takav niz ne moe da pronae.

Uvoz statinih lanova u nabrojani tip


Evo jedne varijacije program a Pljeskavica.java iz poglavlja Im cijalizacija i ienje:
//: nabrojani/LJutina.java package nabrojani; public enum Ljutina { NE, BLAGO, SREDNJE, MNOGO, PALI

} III-/ / : nabrojani/Pljeskavica.java package nabrojani; import static enumerated.LJutina.*; public class Pljeskavica {

Poglavjje 19: Nabrojani tipovi

807

LJutina degree; public P1jeskavica(LJutina stepen) public String toString() public static void main(String[] { this.stepen = stepen;} { { return "Pljeskavica je "+ stepen;} args)

System.out.println(new P1 je sk av ic a( NE )); System.out.println(new Pljeskavica(SREDNJE)) ; System.out.println(new P1 je sk av ic a( MN OG O) );

}
} /* Ispis: Pljeskavica je NE Pljeskavica je SREDNJE Pljeskavica je MNOGO

* ///:-

static im port uvodi sve identifikatore enum instanci u lokalni prostor im ena, pa oni ne m oraju biti po tp un i (kvalifikovani). Da li je to dobro ili je bolje biti izriit i navesti potpu na im ena svih enum instanci? To verovatno zavisi od sloenosti koda. Prevodilac sigurno nee dopustiti da se upotrebi pogrean tip, pa sam o treba da se postarate da kod bude razum ljiv itaocu. U m nogim situacijam a e i n epotpuna im ena biti dovoljno razumljiva, ali se to moe prosuditi samo od sluaja do sluaja. Vodite rauna o tom e da ovu tehniku nije m ogue koristiti ako je enum definisan u istoj datoteci ili podrazum evanom paketu. (Kao da je u kom paniji Sun bilo razliitih miljenja o tom e treba li to dozvoliti.)

Dodavanje metoda nabrojanom tipu


Iz nabrojanog tipa ne m oete izvoditi nove potklase, inae se enum moe tretirati kao obina klasa. To znai da mu moete dodavati m etode. enum ak moe imati svoju metodu m a in ( ). Videli ste da porazum evana inetoda to S trin g ( ) navodi sam o ime enum instance. Ukoliko elite da date drugaiji opis odreenog nabrojanog tipa, moete napraviti konstruktor koji e hvatati dodatne informacije i m etode koje e davati obim niji opis, kao u sleeem prim eru:
//: na br ojani/VesticalzOza.java // Vetice iz Oza. import static net.mind vi ew .u ti l.Print.*; public enum VesticalzOza { // Prvo morate definisati instance, pre metoda: ZAPAD("Gospoica Gal, poznata i kao zla vetica sa zapada"), SEVER("G1inda, dobra vetica severa"), ISTOK("Zla vetica istoka, nosilac crvenih " + "papuica, koju je smrvila Dorotina kua"), JUG("Verovatno dobra, ali je nema"); private String opis; // Konstruktoru se mora pristupati paketno ili privatno: private VesticaIzOza(String opis) {

808

Misliti na Javi

this.opis = opis;

}
public String getDescription() { return opis; args) { } public static void main(String[] print(vestica +

for(VesticaIzOza vestica : V e s t i c a l z O z a . v a l u e s O ) 1 1 + vestic a. ge tD es cr ip tio n( ));

}
} /* Ispis: WEST: Gospoica Gal, poznata i kao zla vetica sa zapada NORTH: Glinda, dobra vetica sa severa EAST: Zla vetica sa istoka, nosilac crvenih papuica, koju je smrvila Dorotina kua SOUTH: Verovatno dobra, ali je nema

* ///:Ukoliko nam eravate da definiete m etode, sekvencu enum instanci m orate zavriti znakom taka i zarez. Takoe, Java nas prisiljava da u nabrojanom tipu (enum ) najpre definiemo instance. Izazvaete greku u vrem e prevoenja ako pokuate da ih definiete nakon neke od m etoda ili polja. K onstruktor i m etode im aju isti oblik kao u uobiajenim klasama, zato to ovo i jeste obina klasa uz nekoliko ogranienja. Dakle, s nabrajanjim a m oete raditi gotovo sve to poelite (iako ete se verovatno tru diti da ostanu prilino jednostavna). M ada je u prethodnom prim eru konstruktor privatan, ne bi bilo velike razlike i da ste prim enili neki drugi pristup - ko n struktor se moe upotrebiti samo za pravljenje enuin instanci koje ste deklarisali u n u tar definicije nabrojanog tipa; prevodilac nee dozvoliti da ga upotrebite za pravljenje novih instanci nakon zavretka te definicije.

Redefinisanje enum metoda


Sledi opis drugog naina proizvodnje drugaijih znakovnih nizova za nabrojane tipove. U ovom sluaju, imena instanci su dobra, ali elimo da ih preform atiram o radi prikazivanja. Redefinisanje m etode to S trin g ( ) za enum isto je kao redefinisanje za uobiajenu klasu:
//: nabrojani/SvemirskiBrod.java public enum SvemirskiBrod { IZVIDJAC, T E R E T N I , TRANSPORTNI, KRSTARICA, BOJNIBROD, M A T I C N I ; public String toString() String id = n a m e ( ) ; String mala = i d .s u b s t r i n g ( 1 ).t o Lo we rC as e( ); return id.charAt(O) + mala; {

}
public static void main(String[] fo r(SvemirskiBrod s : v a l u e s O ) System.out.pri ntln (s); args) { {

} }
} /* Ispis: Izvi djac Teretni

Poglavlje 19: Nabrojani tipovi

809

Transportri Krstarica Bojnibrod Maticni

* ///:M etoda to S trin g ( ) pribavlja ime klase SvemirskiBrod pozivanjem m etode n a m e ( ), a njen rezultat m enja tako da je sam o prvo slovo veliko.

Nabrojani tipovi u naredbama svvitch


Jedno veom a podesno svojstvo nabrojanih tipova jeste nain na koji se oni mogu u potrebiti u naredbam a switch. switch obino radi sam o s celobrojnim vrednostim a, ali poto nabrojani tipovi im aju utvren celobrojni poredak, a redni broj instance daje metoda o rd in a l( ) (izgleda da neto slino radi prevodilac), nabrojane tipove m oem o upotrebljavati u naredbam a switch. Iako im e enum instance obino m orate upotpuniti njenim tipom , to ne m orate da radite u naredbi case. U sledeem prim eru enum je upotrebljen za pravljenje malc maine stanja:
//: nabrojani/Semafor.java // Nabrojani tipovi u naredbama switch. import static n e t. mi nd vi ew .u ti l.Print.*; // Definicija nabrojanog tipa: enum Signal { ZELENO, ZUTO, CRVENO, }

public class Semafor { Signal svetlo = Signal.CRVENO; { { p u b l i c void change() s w i t c h (svetlo)

// Obratite panju na to da u naredbi case // ne mora te da piete S i g n a l .CRVENO: case CRVENO: svetlo = Signal.ZELENO; break; case ZELENO: svetlo = Signal.ZUTO; break; case ZUTO: svetlo = Signal.CRVENO; break;

} }
public String toString() { return Na semaforu je " + svetlo;

}
public static void main(String[] args) Semafor s = new Semafor(); for(int i = 0 ; pri nt (s); s.changef); i <7; i++) { {

810

Misliti na Javi

} /* Ispis: Na semaforu je CRVENO Na semaforu je ZELENO Na semaforu je ZUTO Na semaforu je CRVENO Na semaforu je ZELENO Na semaforu je ZUTO Na semaforu je CRVENO

* ///:Prevodilac se ne ali da unu tar naredbe switch nem a naredbe default, ali ne zato to je uoio da za svaku instancu klase Signal im ate naredbe case. Nee se aliti ni ako jednu od naredaba case pretvorite u kom entar. Znai, sam i m orate paziti na to da definiete sve mogue sluajeve. S druge strane, ukoliko iz naredbe case pozivate return, prevodilac e se aliti ako nem ate default - ak i ako ste definisali sve mogue vrednosti tog nabrojanog tipa.

Veba 1: (2) Izmenite Semafor.java pom ou uvoza statinih lanova tako da ne m orate navoditi p otpuna imena enum instanci.

Misterija metode valuesf)


Ve sam rekao da prevodilac pravi sve enum klase i da one nasleuju klasu Enum. M eutim , iz dokum entacije klase Enum vidi se da ona nem a m etodu v alues( ), iako smo mi tu m etodu koristili. Ima li jo skrivenih" metoda? Napisaemo mali program koji e to otkriti pom ou refleksije:
//: nabrojani/Refleksija.java // Analiza nabrojanih tipova pomou refleksije. import java.lang.reflect.*; import ja v a . u t i l .*; import net.mindview.util.*; import static ne t. mi nd vi ew .u ti l.Print.*; enum Istrazi ( OVDE, TAMO }

public class Refleksija ( public static Set<String> analiziraj(C1ass<?> enumKlasa) p rintC'----- Analiza klase " + enumKlasa + " ------ "); pr in t( "I nt er fe js i:" ) ; for(Type t : en um Kl as a. ge tG en er icl nt er fa ce s()) print(t); print("Natklasa: print("Metode: " + enumKlasa.getSuperclassO); "); (

Set<String> metode = new Tr ee S e t < S t r i n g > ( ) ; for(Method m : e n um Kl as a. ge tM et ho ds()) me to de .a dd (m .g et Na me( )); pr in t( me to de ); return metode;

Poglavlje 19: Nabrojani tipovi

811

public static void main(String[] args)

Set<String> metodelstrazi = analiziraj (I st ra zi .cl as s); Set<String> enumMetode = analiziraj(Enum.class); print("Istrazi . c o n t a i n s A U (Enum)? 1 1 + m e t o de ls tr az i. co nt ain sA l1 (enumMetode)); printn b( "I st ra zi .r emo ve Al1 (Enu m): " ) ; m e t o d e ls tr az i. re mo veA ll (e nu mM et od e); p r in t( me to de ls tr az i); // Prevedi unazad kod za enum: O S Iz vrsenje.komanda("javap Is tr az i");

}
} /* Ispis: ----- Analiza klase class Istrazi Interfejsi: Natklasa: class ja va .lang.Enum Metode: [compareTo, equals, getClass, g e tD ec la ri ng Cl as s, hashCode, name, notify, notifyAU, ordinal, toString, valueOf, values, wait] ----- Analiza klase class ja va .lang.Enum ----Interfejsi: j a v a . 1a n g .Comparable<E> interface j a v a . io . Se ria l izable Natklasa: class j a v a .1ang.Object Metode: [compareTo, equals, getClass, getDec la ringCla s s , hashCode, name, notify, notif y A l 1 , ordinal, toString, valueOf, wait] I s t r a z i .conta in sA ll(Enum)? true I s t r a z i .r e mo ve Al1 (Enu m): [values] Compiled from "Refleksija.java" final class Istrazi extends j a v a .1an g.Enum{ public static final public static final public static final static {}; Istrazi OVDE; Istrazi TAMO; Istrazi[] values(); ------

public static Istrazi valueO f( ja va .1an g. S t r i n g ) ;

} * ///:Dakle, odgovor je da je v alu es( ) statina m etoda koju je odao prevodilac. I m etoda valu eO f( ) je dodata klasi Istrazi dok se pravio enuin. To pom alo zbunjuje, zato to postoji i m etoda valueO f( ) koja je deo klase Enum, ali ona ima dva argum enta, a dodata m etoda sam o jedan. M etoda interfejsa Set ovde je upotrebljena samo za pronalaenje im ena m etoda, ne i njihovih potpisa, pa posle poziva Istrazi.removeAll(Enum) preostaje sam o niz [values]. Iz rezultata vidite da je prevodilac klasu Istrazi oznaio sa final, pa od nje ne moete izvoditi potklase. Tu je i statina oreba za inicijalizaciju koja se moe redefinisati (pokazau to kasnije).

812

Misliti na Javi

Zbog brisanja (opisanog u poglavlju Generiki tipovi), dekom pajler nem a p u n u inform aciju o nabrojanom tipu (Enurn), pa prikazuje da je natklasa od Istrazi sirovi Enum, um esto stvarne Enum <Istrazi>. Poto je valu es( ) statina m etoda koju je u definiciju enum u m etn u o prevodilac, ako neki od enum tipova svedete navie na Enum, m etoda values( ) nee biti dostupna. M edutim , im ajte u vidu da u klasi Class postoji m etoda getE num C onstants( ), pa ak i ako m etoda v alu es( ) nije deo interfejsa od Enum, enum instance ipak m oete dobiti pom ou Class objekta:
//: nabrojani/SvodjenjeEnumaNavise.java / / Kada se nabrojan tip svede navie, // metoda va1ues() postaje nedostupna enum Pretrazivanje { OVAMO, ONAMO } public class SvodjenjeEnumaNavise { public static void main(String[] args) { Pretrazivanje[] vrdns = Pretra zi va nj e. va lu es( ); Enum e = Pretrazivanje.OVAMO; // Svoenje navie // e.values(); // Enum nema metodu values() for(Enum en : e.getClass() .g et En um C o n s t a n t s O ) S y st em .o ut .p ri nt ln (en );

}
} /* Ispis: OVAMO ONAMO

* ///:Poto m etoda getE num C onstants( ) pripada klasi Class, moete je pozvati i za klase koje nem aju nabrojane tipove:
//: nabrojani/NijeEnum.java public class NijeEnum { public static void main(String[] try { for(0bject en : in t K l as a. ge tE nu mC on sta nt s()) S y s t e m .o ut .p ri nt ln (en ); } catch(Exception e) { System.out.pri ntl n ( e ) ; args) { C1 as s<Integer> intKlasa = Integer.class;

} }
} /* Ispis: j a v a . 1a n g . N u l 1Poi nterExcepti on

* ///:M etoda vraa null, pa ete dobiti izuzetak kada pokuate da upotrebite njen rezultat.

Poglavlje 19: Nabrojani tipovi

813

Realizuje, ne nasleuje
Rckao sam da su svi nabrojani tipovi izvedeni iz klase java.lang.Enum . Poto Java ne podrava viestruko nasleivanje, to znai da nabrojan tip ne m oete napraviti preko nasleivanja:
en um NijeMoguce extends Pet { . . . / / Ne radi

M eutim , mogue je napraviti nabrojan tip koji realizuje jedan ili vie interfejsa:
//: nabrojani/crtaci/EnumRealizacija.java // Nabrojani tip moe da realizuje interfejs package na br oj an i. cr ta ci; import ja v a . u t i l .*; import net.mindview.util.*; enum LiklzCrtaca implements Generator<LikIzCrtaca> { FUCA, GRDA, TIBA, BLESA, SKOKA, public LiklzCrtaca next() { LUDA, BOBA; private Random slucajan = new Random(47); return v a l u e s ()[slucajan.nextlnt(values().1e n g t h ) ] ;

public class EnumRealizacija { public static <T> void printNext(Generator<T> gsb) System.out.print(gsb.next() + ", "); {

}
public static void main(String[] args) { // Izaberite bilo koju instancu: LiklzCrtaca lic = LiklzCrtaca.BOBA; for(int i = 0 ; i <10; i++) printNext(l i c ) ;

}
} /* Ispis: BOBA, TIBA, BOBA, GRDA, LUDA, TIBA, FUCA, LUDA, LUDA, FUCA,

* ///:Rezultat je malo neobian, poto m orate im ati instancu nabrojanog tipa da biste za nju mogli pozvati m etodu. M edutim , L iklzC rtaca sada m oe da prihvati svaka m etoda koja prim a G enerator, na prim er, m etoda p r in tN e x t(). Veba 2: (2) Umesto da reali/.ujete interfejs, napravite n e x t( ) statinom m etodom . Koje su prednosti i m ane tog pristupa?

814

Misliti na Javi

Nasumian izbor
U m nogim prim erim a u ovom poglavlju neophodan je nasum ian izbor enum instanci, kao to ste videli u m etodi LikIzCrtaca.next( ). To m oem o uoptiti pom ou generikih tipova i rezultat sm estiti u zajedniku biblioteku:
/ / : n e t/ mi nd vi ew /u ti l/ Nab ro ja ni Ti po vi.java package net.mindview.util; import java.util public class NabrojaniTipovi { {

private static Random slucajan = new Random(47); public static <T extends E n u m < T T random(Class<T> ec) return random(e c. ge tE nu mC ons ta nt s( ));

}
public static <T> T random(T[] vrednosti) { return vredno st i[ sl uc aj an .ne xt In t( vr ed no st i. le ngt h) ];

} } ///= Prilino neobina sintaksa <T extends E n u m < T opisuje T kao instancu nekog nabrojanog tipa. O bjekat te klase inim o dostupnim tako to prosleujem o Class<T>, i zato se m oe napraviti niz enum instanci. Preklopljena m etoda ra n d o m ( ) treba da zna sam o to da dobija T[], poto ne m ora da obavlja Enum operacije; ona sam o nasum ino bira neki elem ent niza. Povratni tip je tano taj nabrojan tip. Evo iednostavne provere m etode ra n d o m ( ):
//: na br ojani/ProveraMetodeRandom.java import net.mindview.util.*; enum Aktivnost { SEDENJE, TRCANJE, LEZANJE, STAJANJE, POSKAKIVANJE,

IZMICANJE, SKAKANJE, PADANJE, LETENJE }

public class ProveraMetodeRandom { public static void m a i n ( S t r i n g [] args) for(int i = 0; i < 20; i++) S y s t e m . o u t .p r i n t ( N a br oj an iT ip ovi.r a nd om (A kt iv no st.class) + " "); {

}
} /* Ispis: STAJANJE LETENJE TRCANJE STAJANJE TRCANJE STAJANJE LEZANJE IZMICANJE SEDENJE TRCANJE POSKAKIVANJE POSKAKIVANJE POSKAKIVANJE TRCANJE STAJANJE LEZANJE PADANJE TRCANJE LETENJE LEZANJE

* ///:Iako su N abrojaniTipovi mala klasa, videete da njenom zaslugom u ovom pogiavlju izbegavamo prilinu koliinu dupliranja. D upliranje esto dovoi do greaka, pa je uklanjanje dupliranja koristan posao.

Poglavlje 19: Nabrojani tipovi

815

Upotreba interfejsa za organizovanje


To to je nem ogue naslediti nabrojan tip um e ponekad da zasmeta. Nasleivanje nabrojanog tipa potrebno je zbog poveavanja broja elem enata prvobitnog nabrojanog tipa i zbog pravljenja potkategorija pom ou podtipova. Kategorizaciju m oete ostvariti grupisanjem elem enata u n u tar interfejsa i pravljenjem nabrojanog tipa na osnovu tog interfejsa. Na prim er, pretpostavim o da im ate razliite klase hrane koje biste hteli da napravite kao nabrojane tipove, ali biste ipak hteli da svaka od njih bude neki tip izveden iz klase H ran a. Evo kako to izgleda:
//: nabrojani/jelovnik/Hrana.java // Potkategorizacija nabrojanih tipova unutar interfejsa. package na b r o j a n i .jelovnik; public interface Hrana { enum Predjelo implements Hrana { SALATA, SUPA, PROLECNE_ROLNICE;

}
enum GlavnoJelo implements Hrana { LAZANJE, PLJESKAVICA, P A D T H A I , MAHUNARKE, PILAV, SARMA;

enum Desert implements Hrana { TIRAMISU, SLADOLED, SVARCVALD_TORTA, VOCE, K A R A M E L K R E M ;

}
enum Kafa implements Hrana { CRNA_KAFA, KAFA_BEZ_KOFEINA, ESPRESSO, KAFA_S_MLEKOM, CAPPUCCINO, CAJ, B I LJ NI _C AJ;

} III-Poto nabrojan tip moe imati samo podtipove napravljene realizacijom interfejsa, svaki ugneeni en u in realizuje obuhvatajui interfejs H ran a. Sada m oem o rei da je svako jelo neki tip klase Hrana", kao to vidite ovde:
/ / : nabrojani/jelovnik/VrstaHrane.java package nabrojani.jelovnik; import static n a b r oj an i.jelovnik.Hrana.*; public class VrstaHrane { public static void main(String[] Hrana hrana = Predjelo.SALATA; hrana = G 1 avnoJelo.LAZANJE; hrana = Desert.SLADOLED; hrana = Kafa.CAPPUCCINO; args) {

III---

816

Misliti na Javi

Svoenje navie na klasu H rana funkcionie za svaki enum tip koji realizuje interfejs H rana, pa su svi oni tipovi klase H rana. M eutim , za rad sa skupom tipova interfejs nije toliko upotrebljiv kao enum. Ukoliko elite da im ate nabrojan tip drugih nabrojanih tipova, m oete napraviti obuhvatajudi

enum s po jedn om instancom za svaki enum u klasi Hrana:


//: na br oj ani/jelovnik/VrstaJela.java package nabrojani.jelovnik; import net.mindview.util.*; public enum VrstaJela { P R ED JE LO (H ra na .P re dje lo .c la ss ), GL A V N O J E L O ( H r a na .G lav no Je lo .c la ss ), DE S E R T ( H ra na .D es er t.c la ss ), KA FA (H ra na .K af a. cl ass ); private Hranaf] vrednosti; { private VrstaJela(Class<? extends Hrana> vrsta) vrednosti = v r s t a . g e t E n u m C o n s t a n t s ();

}
public Hrana ra nd om S e l e c t i o n () { return N a b r o j an iT ip ov i.r a nd om (v re dn os ti);

} } ///:Svaki od gorenavedenih nabrojanih tipova prim a odgovarajui objekat tipa Class kao argum ent konstruktora od kojega m etodom g e tE n u m -C o n sta n ts() moe da izvue i uskladiti sve en u m instance. Te instance kasnije koristi m etoda ra n d o m S e le c tio n (), pa sada m oem o da napravim o nasum ino generisan obrok tako to em o izabrati po jednu stavku klase H ra n a iz svakog tipa V rstajela:
//; nabrojani/jelovni k/Obrok.java package n a b r o j a n i .j e l o v n i k ; public class Obrok { public static void m a i n ( S t r i n g [] args) for(int i = 0; i < 5; i++) { : VrstaJela.vrednosti ()) { for(VrstaJela p o j e d i n o j e l o S y s t e m . o u t .pri ntl n ( h r a n a ) ; {

Hrana hrana = po je di n o _ j e l o . r a n d o m S e l e c t i o n ();

}
Sy st em .o ut .p ri nt ln (" ");

} }
} /* Ispis: PROLECNE_ROLNICE SARMA VOCE KAFA BEZ KOFEINA SUPA

Poglavlje 19: Nabrojani tipovi

817

SARMA VOCE CAJ SALATA PLJESKAVICA VOCE CAJ SALATA PLJESKAVICA KARAMEL_KREM KAFA_S_MLEKOM SUPA PLJESKAVICA TIRAMISU ESPRESSO

* ///:U ovom sluaju, svrha pravljenja nabrojanog tipa nabrojanih tipova jeste iteracija kroz sve tipove V rstajela. Kasnije, u prim eru A utom atZ aM aloprodaju.java, videete drugaiji pristup kategorizaciji odreen drugaijim ogranienjim a. Drugaiji, kom paktniji pristup problem u kategorizacije jeste ugnedivanje nabrojanih tipova u n u ta r drugih nabrojanih tipova, to se radi ovako:
//: nabrojani/KategorijeVrednosnihPapira.java // Saetija potkategorizacija nabrojanih tipova. import net.mindview.util.*; enum KategorijeVrednosnihPapira { DEONICE(Vrednosni Papi ri.Deoni ca . c l a s s ) , OB VEZNICE(VrednosniP a p i r i .O bv ez ni ca .c la ss ); V r e d n o s n i P a p i r i [] vrednosti; KategorijeVrednosnihPapira(Class<? extends Vred no sn iPapiri> vrsta) vrednosti = vr st a. ge tE nu mC on st ant s( ); {

I
interface Vred no sn iP ap iri { enum Deonica implements VrednosniPapiri JEMSTVENE } en u m Obveznica implements VrednosniPapiri { GRADSKE, RAZNE } { KRATKOROCNE, DUGOROCNE,

}
public VrednosniPapiri randomSelection() { return Nabrojani Ti p o v i .ra nd om(vrednosti) ;

}
public static void m a i n ( S t r i n g [] args) for(int i = 0; i < 10; i++) { {

KategorijeVrednosnihPapira kategorija = Nabrojani Ti p o v i .random(KategorijeVrednosni hPapi ra.cla s s ) ; Sy st em.out.println(kategorija + " +

818

Misliti na Javi

kategorija.randomSelectionO);

} }
} /* Ispis: OBVEZNICE: GRADSKE OBVEZNICE: GRADSKE DEONICE: JEMSTVENE DEONICE: JEMSTVENE OBVEZNICE: RAZNE DEONICE: KRATKOROCNE DEONICE: DUGOROCNE OBVEZNICE: GRADSKEDEONICE: DUGOROCNE OBVEZNICE: RAZNE

* ///:Interfejs VrednosniPapiri neophodan je za objedinjavanje obuhvaenih nabrojanih tipova u jedan zajedniki tip. Zatim se on i kategorizuju u zasebne nabrojane tipove u nutar KategorijeVrednosnihPapira. Ukoliko prim er H rana obradim o na ovaj nain, rezultat e izgledati ovako:
//: nabrojani/jelovnik/0brok2.java package nabrojani.jelovnik; import net.mindview.util.*; public erum 0brok2 { PREDJELO(Hrana.Predjelo.class), GLAVNOJELO(Hrana.GlavnoJelo.class), D E SE RT (H rana.Desert.class), KAFA(Hrana.Kafa.cla s s ) ; private Hrana[] vr e d n o s t i ; private 0brok2(C1ass<? extends Hrana> vrsta) vrednosti = v r s t a . g e tE nu mC on st ant s( ); {

}
public interface Hrana { enum Predjelo implements Hrana { SALATA, SUPA, PROLECNE_ROLNICE;

}
enum GlavnoJelo implements Hrana { LAZANJE, PLJESKAVICA, PAD_THAI, MAHUNARKE, PILAV, SARMA;

}
enum Desert implements Hrana { TIRAMISU, SLADOLED, SVARCVALD_TORTA, VOCE, KARAMEL_KREM;

}
enum Kafa implements Hrana { CRNA_KAFA, KAFA_BEZ_KOFEINA, ESPRESSO, KAFA S MLEKOM, CAPPUCCINO, CAJ, BILJNI CAJ;

Poglavlje 19: Nabrojani tipovi

819

public Hrana randomSelection()

return Nabr oj an iT ip ov i. ra ndo m( vr ed no st i);

}
public static void main(String[] args) for(int i = 0 ; i < 5 ; i++) { {

for(0brok2 obrok : 0 b r o k 2 . v a l u e s ()) { Hrana hrana = obrok.randomSelection(); System .o ut .p ri nt ln (hr an a);

}
System.out.println('' ");

} }
} /* Ispis je isti kao u primeru Obrok.java *///:-

Na kraju sm o dobili praktino isti kod, ali reorganizovan. U nekim sluajevima, tim e se dobija jasnija struktura.

Veba 3: (1) Dodajte nov tip Vrstajela u Vrstajela.java i pokaite da to radi u program u Obrok.java. Veba4: (1) Ponovite p rethodnu vebu za Obrok2.java. Veba 5: (4) Izmenite program kontroIa/SamoglasnicilSuglasnici.java (iz etvrtog poglavlja) tako da koristi tri nabrojana tipa: SAMOGLASNIK, PONEKAD SAMOGLASNIK i SUGLASNIK. K onstruktor nabrojanog tipa treba da prim a sva slova koja ine odgovarajuu kategoriju. Uputstvo: upotrebite argum ente promenljive duine i ne zaboravite da oni autom atski prave niz. Veba 6: (3) Daje li ugneivanje tipova Predjelo, Glavnojelo, Desert i Kafa unutar interfejsa H rana ikakve prednosti nad sluajem kada bism o ih deklarisali kao samostalne nabrojane tipove koji sluajno svi realizuju interfejs Hrana?

Skup EnumSet umesto indikatora


Skup (Set) jedna je vrsta kolekcije koja ne moe sadrati vie prim eraka istog tipa objekta. N aravno, i za nabrojan tip je neophodno da svi njegovi lanovi budu jedinstveni, pa se utoliko ponaa kao skup, ali poto m u ne m oete dodavati elem ente niti ih uklanjati iz njega, kao skup nije preterano upotrebljiv. EnumSet je dodat u Javu SE5 kako bi zajedno s nabrojanim tipovim a napravio zam enu za nekadanje celobrojnebit indikatore (engl. b it Jlugs). Takvi indikatori se upotrebljavaju za uvanje neke vrste inform acija tipa ukljueno/iskljueno, ali m orate da radite s bitovim a um esto s konceptim a, pa je dobijeni kod esto nerazumljiv. EnumSet radi brzo, poto m ora da se takmii s bit indikatorim a (njegove operacije su obino m nogo bre nego one u skupu HashSet). Interno, EnumSet se (ako je mogue) predstavlja jednim brojem tipa long koji se tretira kao bit-vektor, pa je izuzetno brz i delotvoran. Prednost je to to sada im am o m nogo izraajniji nain pokazivanja postojanja ili nepostojanja binarnih informacija, a pri tom ne m oram o da brinem o zbog performansi.

82 0

Misliti na Javi

Elementi skupa EnumSet m oraju poticati iz istog nabrojanog tipa. U n arednom prim eru im am o enum svih m esta u zgradi na kojim a je ugraen alarm ni senzor:
//: nabrojani/AlarmneTacke.java package n a b r oj an i; public enum AlarmneTacke { STEPENISTEl, STEPENISTE2, HODNIK, K A N C EL AR IJ Al, KANCELARIJA2, KANCELARIJA3, KANCELARIJA4, KUPATILO, SALA, KUHINJA

} ///= Taj EnumSet se moe upotrebiti za praenje statusa alarma:


//: nabrojani/SkupEnumSet.java // Operacije na skupu tipa EnumSet package n a b r oj an i; import j a va .u ti1 import static nabrojani.A1armneTacke.*; import static net.mind vi ew .u ti l.Print.*; public class SkupEnumSet { public static void m a i n ( S t r i n g [] args) EnumSet<AlarmneTacke> tacke = EnumSet.noneOf(AlarmneTacke.class); // Prazan skup ta ck e. ad d( KU PA TI LO ); print(tacke); tacke.addAl1 (E nu mS et.of(STEPENISTEl, STEPENISTE2, KUHI NJ A)) ; pri n t ( t ac ke ); tacke = En um Se t. al lO f( Al ar mne Ta ck e. cl as s); ta cke.removeAI1 (E nu mS et,of(STEPENISTEl, STEPENISTE2, KUHI NJ A)); print(tacke); tacke.removeAl1 (EnumSet.r a ng e( KA NC EL AR IJ Al, KANCELARIJA4)); pri n t ( t ac ke ); tacke = En um Se t. co mp le me nt Of( ta ck e); pr in t( ta ck e); {

}
} /* Ispis: [KUPATILO] [S TEPENISTEl, STEPENISTE2, KUPATILO, KUHINJA] [HODNIK, KANCEL AR IJ Al, KANCELARIJA2, KA NC EL AR IJ A3, KANCELARIJA4, KUPATILO, SALA] [HODNIK, KUPATILO, SALA] [STEPENISTEl, S T EP EN IS TE 2, KA NC E L A R I J A l , KANCELARIJA2, KANCELARIJA3, KANCELARIJA4, KUHINJA]

* ///:Statian uvoz pojednostavljuje korienje enu m konstanti. Imena m etoda su prilino jasna sam a po sebi, a detalje m oete proitati u HTML dokum entaciji na Webu. Kada to uradite, videete neto zanimljivo - m etoda o f ( ) je preklopljena i za argum ente promenljive duine i za pojedinane m etode koje prim aju od dva do pet eksplicitnih argum enata. To je pokazatelj brige za perform anse skupa Enum Set, poto bi problem reila

Poglavlje 19: Nabrojani tipovi

821

ve i jed n a m etoda o f ( ) sa argum entim a prom enljive duine, ali bi to bilo neto m anje efikasno nego kada su argum enti zadati eksplicitno. Dakle, ako m eto d u o f ( ) pozovete sa od dva o d pet argum enata, dobiete eksplicitne (neto bre) m etode, ali ukoliko je pozovete s jednim ili s vie od pet argum enata, dobiete vararg verziju od o f ( ). O bratite panju na sledee: ukoliko je pozovete s jednim argum entom , prevodilac nee praviti vararg niz, pa pozivanje te verzije s jednim argum entom nee poveati reijske trokove. EnumSet se pravi povrh podupirueg broja tipa long, long im a 64 bita, a svaka enum instanca zahteva jedan bit za inform aciju ukljueno/iskljueno ili postojanje/nepostojanje. To znai da EnumSet za jedan enum od najvie 64 elem enta troi sam o jedan broj tipa long. ta se deava ako u nabrojanom tipu im ate vie od 64 elementa?
//: nabrojani/VelikiEnumSet.java import java.util public class Vel ikiEnumSet { enum Veliki { AO, Al, A2, A3, A4, A5, A5, A7, A8, A9, AIO, A 11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23, A24, A25, A26, A27, A28, A29, A30, A31, A32, A33, A34, A35, A36, A37, A38, A39, A40, A41, A42, A43, A44, A45, A46, A47, A48, A49, A50, A51, A52, A53, A54, A55, A56, A57, A58, A59, A60, A61, A62, A63, A64, A65, A56, A67, A68, A69, A70, A71, A72, A73, A74, A75 } public static void main(String[] args) System.out .p ri nt ln (bi gE nu mS et ); { E n um Se t< Ve 1iki> bigEnumSet = En um Se t.al1O f (Veli k i .c la ss );

}
} /* Ispis: [AO, Al, A2, A 3 , A4, A5, A6, A7, A8, A9, AIO, All, A12, A13, A14, A15, A16, A 1 7 , A18, A19, A20, A21, A22, A23, A24, A25, A26, A27, A28, A29, A30, A31, A 3 2 , A33, A34, A35, A36, A37, A38, A39, A40, A41, A42, A43, A44, A45, A46, A 4 7 , A48, A49, A50, A51, A52, A53, A54, A55, A56, A57, A58, A59, A60, A61, A62, A63, A64, A65, A66, A67, A68, A69, A70, A71, A72, A73, A74, A75]

* ///:E num S et oigledno nem a problem a s nabrojanim tipom koji im a vie od 64 elementa, pa m oem o pretpostaviti da drugi long dodaje po potrebi. Veba 7: (3) Pronaite izvorni kod za Enum Set i objasnite kako radi.

Korienje mape EnumMap


E num M ap je specijalizovana m apa iji kljuevi m oraju poticati iz istog nabrojanog tipa. Zbog ogranienja kojima je podvrgnut enum , Enum M ap se interno moe realizovati kao niz. Zato je izuzetno brza, pa je slobodno moete koristiti za pretraivanja iju osnovu ine nabrojani tipovi. Za kljueve u nabrojanom tipu moete pozivati sam o m etodu p u t ( ), a osim toga, sa ovom m apom radite kao sa svakom drugom .

822

Misliti na Javi

U naredn om prim eru prikazana je upotreba projektnog obrasca C o m m a n d (Kom anda). Taj obrazac poinje interfejsom koji (najee) sadri sam o jed n u m etodu; onda se prave razne realizacije u kojim a se ta m etoda razliito ponaa. Vi instalirate objekte tipa C om m and, a program ih poziva po potrebi:
//: nabrojani/MapeEnumMap.java // Osnove rada s mapama EnumMap. package n a b r oj an i; import java.util import static nabrojani.AlarmneTacke.*; import static net.mi nd vi ew .u ti l.Print.*; interface Command { void action(); public class MapeEnumMap { public static void main(String[] args) { EnumMap<AlarmneTacke,Command> em = new En um Map<AlarmneTacke,Command>(AlarmneTacke.class); em.put(KUHINJA, new Command() public void action() { } { printC'Poar u kuhinji!"); { } }

});
em.put(KUPATILO, new Command() public void action() { print("Uzbuna u kupatilu!");

});
for(Map.Entry<AlarmneTacke,Command> e : e m .e nt ry Se t()) { printnb(e.getKey() + e.ge tV al ue () ,a ct io n(); ");

}
try { // Ako se ne pronae vrednost za odreeni em .g et (S AL A) .a ct io n(); } catch(Exception e) print(e); { klju:

} }
} /* Ispis: KUPATILO: KUHINJA: Uzbuna u kupatilu! Poar u kuhinji!

java.lang.NullPointerExcepti on

* ///:Kao u skupu Enum Set, redosle elem enata u m api E num M ap odreen je redosledom njihovog definisanja u odgovarajuem nabrojanom tipu. Poslednji deo m etode m a in ( ) pokazuje da u m api za svaki nabrojani tip uvek postoji stavka klju, ali je njena vrednost n u ll ukoliko za taj klju niste pozvali p u t ( ). Jedna od prednosti m ape E num M ap nad m etodnm a koje sc m enjaju u znvisnosti od konstante (nabrojanog tipa) jeste to to je u E num M ap dozvoljeno m enjanje objekata vrednost, dok m etode koje se m enjaju u zavisnosti od konstante bivaju fiksirane u vreme prevoenja.

Poglavlje 19: Nabrojani tipovi

823

Kao to ete videti u nastavku poglavlja, m apam a EnumMap m oemo obavljati viekratno otkrivanje tipa u situacijama gde vie nabrojanih tipova ulazi u m eusobnu interakciju.

Metode koje se menjaju u zavisnosti od konstante


Javini nabrojani tipovi im aju veom a zanimljivu osobinu koja om oguuje da svakoj enum instanci definisanjem njenih m etoda date razliito ponaanje. To ete postii definisanjem jedne ili vie apstraktnih m etoda u sklopu nabrojanog tipa, a zatim definisanjem tih m etoda za svaku enum instancu. Na prim er:
//: nabrojani/MetodeKojeSeMenjajuUZavisnostiOdKonstante.java import java.util import java.text.*; public enum MetodeKojeSeMenjajuUZavisnostiOdKonstante { DATE_TIME { String getlnfof) return Da te F o r m a t . g e t D a t e l n s t a n c e O .format(new D a te ()); {

} },
CLASSPATH { String g e t l n f o O { return System.g et en v( "C LA SSP AT H" );

} },
VERSION { String g e t l n f o O { return Sy st em .g et Pr op er ty ("j av a. ve rs io n" );

} };
abstract String g e t l n f o O ; public static void main(String[] args) { for(MetodeKojeSeMenjajuUZavisnostiOdKonstante mksmuzok : v a l u e s O ) S y s t e m . o u t .printl n ( m k s m u z o k . g e t l n f o O );

}
} /* (Pokrenite da biste videli ispis rezultata) *///:-

M etode m oete pronai i pozvati preko njim a pridruene en u m instance. To se esto naziva kdd upravljan tabelom. (O bratite panju na slinost s prethodno spom enutim obrascem C om m and.) U objektno orijentisanom program iranju, razliita ponaanja se povezuju s razliitim klasama. Poto svaka instanca nabrojanog tipa m oe im ati sopstveno ponaanje ostvareno m etodam a koje se menjaju u zavisnosti od konstante, stie se utisak da je svaka instanca zaseban tip. U gornjem prim eru, svaka en u m instanca se tretira kao osnovni tip M etodeK ojeSeM enjajuU Z avisnostiO dK onstante, a polim orfno ponaanje dobijam o pozivom m etode g e t!n fo ().

824

Misliti na Javi

M eutim , ta slinost ne ide predaleko. en u m instance ne m oete tretirati kao tipove klasa:
//: nabrojani/NisuKlase.java // {Pokretanje: javap -c KaoKlase} import static net.mindview.util .Print.*; enum KaoKlase { NAMIGNUTI { void ponasanje() TREPNUTI { void ponasanje() KLIMNUTI { void p o n a s a n j e O abstract void ponasanje(); { pr i n t ( " P o n a s a n j e l " ) ; } }, { pr in t( " P o n a s a n j e 2 " ) ; } }, { pr i n t ( " P o n a s a n j e 3 " ) ; } };

}
public class NisuKlase { // void fl(KaoKlase.NAMIGNUTI instance) } /* Ispis: Compiled from "NisuKlase.java" abstract class KaoKlase extends java.lang.Enum) public static final KaoKlase NAMIGNUTI; public static final KaoKlase TREPNUTI; public static final KaoKlase KLIMNUTI; {} // Ne moe

* ///:U m etodi f l ( ), vidite da prevodilac ne ozvoljava u p otrebu en u m instance kao tipa klase, to ima smisla ako razm otrite kod koji on generie - svaki e n u m element je statina finalna instanca tipa KaoKlase. Takode, zbog svoje statinosti, en u m instance u nutranjih nabrojanih tipova ne ponaaju se kao obine unutranje klase; m i nem am o pristupa nestatinim poljima ili metodam a u spoljnoj klasi. Zanimljiviji prim er je perionica autom obila. Svakom korisniku se daje m eni opcija za pranje. Svaka opcija obavlja drugu aktivnost i moe joj se pridruiti m etoda koja se menja u zavisnosti od konstante (nabrojanog tipa). Po jedan E num S et uva opcije koje je svaki korisnik odabrao:
//: nabrojani/PerionicaAutomobi 1 a .java import ja v a . u t i 1.*; import static net.mindview.uti1 .P r i n t .*; public class PerionicaAutomobila { public enum Ciklus { DONJIPOSTROJ { void d e j s t v o O { printf'Prskanje donjeg postroja"); }

},
PRANJETOCKOVA {

Poglavlje 19: Nabrojani tipovi

825

void dejstvo()

{ print("Pranje tokova");

},
PRETPRANJE { void d e j s t v o O { print("Omekavanje neistoe"); }

},
PRANJE { void d e j s t v o O { pr in t( " P r a n j e " ) ; }

},
VOSKIRANJE { void d e j s t v o O { print("Nanoenje vrueg voska"); }

},
ISPIRANJE { void d e j s t v o O { pr in t( "I s p i r a n j e " ) ; }

},
SUSENJE { void d e j s t v o O { print("Suenje venti 1 a t o r o m " ) ; }

};
abstract void dejstvo();

}
EnumSet<Ciklus> ciklusi = E n u m S e t .of ( C i k l u s .PRANJE, C i k i u s .IS PI R A N J E ) ; public void add(Ciklus ciklus) public void operiKola() c.dejstvoO ; { for(Ciklus c : ciklusi) { ci kl us i . a d d ( c i k l u s ) ; }

}
public String toString() { return c i k l u s i .t o S t r i n g (); } { public static void main(String[] args) pri nt (pranje); pranje.operi K o l a ( ) ; // Redosled dodavanja nije vaan: pran je .a dd (C ik lu s. SUS EN JE ); pran je .a dd (C ik lu s. SUS EN JE ); // Duplikati p r an je .a dd (C ik lu s. ISP IR AN JE ); p r an je .a dd (C ik lu s.VO SK I R A N J E ) ; pr i n t ( p r a n j e ) ; pranje.operi Kola (); se zanemaruju

PerionicaAutomobila pranje = new Pe ri on ic aA ut om ob i1a ( ) ;

}
} /* Ispis: [PRANJE, Pranje Ispi ranje [PRANJE, VOSKIRANJE, Pranje Nanoenje vrueg voska Ispiranje Suenje ventilatorom ISPIRANJE, SUSENJE] ISPIRANJE]

* ///:-

82 6

Misliti naJavi

Sintaksa za definisanje m etode koja se m enja u zavisnosti od konstante (nabrojanog tipa) u osnovi je kao ona za ano nim n u u nu tranju klasu, ali saetija. U ovom p rim eru pokazana su i neka obeleja skupa E num Set. Poto se radi o skupu, on uva sam o po jedan prim erak svake stavke, pa se zanem aruju duplikati poziva m etode a d d ( ) sa istim argum entom (to im a smisla, poto stanje bita sam o jed n o m moete da prom enite u ,,ukljueno). Sem toga, nije vaan redosled dodavanja en u m instanci redosled ispisivanja rezultata biva odreen redosledom deklarisanja u nabrojanom tipu. Da li je m ogue redefinisati m etode koje se m enjaju u zavisnosti od k.onstante, um esto to realizujemo apstraktnu m etodu? Da, kao to se vidi u ovom prim eru:
//: nabrojani/RedefinisanjeMetUZavOdK.java import static net.mind vi ew .u ti l.Print.*; public enum RedefinisanjeMetUZavOdK { NAVRTKA, ZAVRTANJ, PODLOSKA { void f() { printC'Redefinisana metoda"); } } {

};
void f() { print("podrazumevano ponaanje"); public static void main(String[] printnb(rmuzok + " : "); rm u z o k . f (); args) {

for(RedefinisanjeMetUZavOdK rmuzok : values())

} }
} /* Ispis: NAVRTKA: podrazumevano ponaanje ZAVRTANJ: podrazumevano ponaanje PODLOSKA: Redefinisana metoda

* ///:Iako nabrojani tipovi ne dozvoljavaju pisanje odredenih vrsta koda, po pravilu bi trebalo da eksperim entiete s njim a kao da su klase.

Stvaranje lanca odgovornosti pomou nabrojanih tipova


U projektnom obrascu C hain o f Responsibility (Lanac odgovornosti), definie se vie razliitih naina reavanja odredenog problem a, a zatim se oni ulanavaju. Kada se pojavi neki zahtev, on se prosleuje du lanca sve d ok jed n o od reenja ne uzm ogne da ga obradi. lednostavan Chain o f Responsibility lako je realizovati pom ou m etoda koje se m enjaju u zavisnosti od konstante. Zamislite model pote u kojoj se pokuava obraivanje svake poiljke na najoptiji mogui nain i tako sve dok se poiljka ne proglasi za neuruljivu. Svaki pokuaj m oem o sm atrati jednim projektnim obrascem Strategy ; a celokupnu listu za Chain o f ResponsibiIity. Poeemo opisom poiljke. Sva njena vana obeleja m ogu biti izraena nabrojanim tipovim a. Poto em o objekte tipa Posiljka generisati nasum ino, najlake em o smanjiti verovatnou da e (na prim er) odreena poiljka dobiti DA za O pstaD ostava ukoliko

Poglavlje 19: Nabrojani tipovi

827

napravim o vie ne-DA instanci, pa e vam definicije nabrojanih tipova spoetka izgledati malo udno. U klasi Posiljka videete m etodu nasum icnaPosiljka( ) koja pravi nasum ine prim erke prob nih poiljki. M etoda g en erato r( ) proizvodi objekat koji realizuje interfejs Iterable i m etodom nasum icnaPosiIjka( ) proizvodi vie poiljki, po jednu za svaki poziv m etode n e x t( ) putem iteratora. Takva konstrukcija om oguuje jednostavno pravljenje foreach petlje pozivom m etode P osiljka.generator( ):
//: nabrojani/Posta.java // Modelovanje pote. import j a va .u ti1 import net.mindview.util.*; import static n e t . mi nd vi ew .u ti l.Print.*; class Posiljka ( Tipovi NE su tu da bi se smanjila verovatnoa nasuminog izbora: enum OpstaDostava {DA,NE1,NE2,NE3,NE4,NE5} enum MozeSeSkenirati {N E M O ZE SE SK EN IR AT I,D A 1,D A 2 ,D A 3 ,D A 4 } enum Citljivo {NECITLJIV0,DA1,DA2,DA3,DA4} en um Adresa {NETACNA,0K1,0K2,0K3,0K4,0K5,0K6} enum PovratnaAdresa {NEDOSTAJE,OK1,OK2,OK3,OK4,OK5} OpstaDostava opstaDostava; Moze Se Sk en irati moze Se Sk en ir a t i ; Citljivo citljivo; Adresa adresa; PovratnaAdresa povratnaAdresa; static long brojac = 0; long id = brojac++; public String toString() public String details() return t o S t r i n g O + " + opstaDostava + " + MozeSeSkenirati + " + Citljivo + ", Opta dostava: { { return "Poiljka " + id; }

", Adresa se moze skenirati: ", itljivost adrese: ", Adresa Adresa: ", Povratna adresa:

1 1 + adresa + " + povratnaAdresa;

}
// Generisanje probne Poiljke: public static Posiljka nasumicnaPosiljka() Posiljka p = new Posiljka(); p.opstaDostava= Na br oj an iT ip ov i.rand om (O ps ta Do st av a.c la ss ); p.MozeSeSkeni rati = N a b r o j an iT ip ov i.ra nd om (M oz eS eS ke ni rat i. cl as s); p.Citljivo = Na br oj an iT ip ov i.ra nd om(Citljiv o . c l a s s ) ; p.adresa = Nabr oj an iT ip ov i. ra ndo m( Ad re sa .c la ss ); p.povratnaAdresa = NabrojaniTi p o v i .r a nd om (P ov ra tn aA dr esa .c la ss ); return p; {

}
public static Iterable<Posiljka> generator(final return new Iterable<Posi1jka>() int n = b r o j ; public Iterator<Posi1jka> iterator() { { int broj) {

828

Misliti na Javi

return new I t er at or <P os i1jka>() public boolean hasNext() public Posiljka next() public void remove()

{ > 0; }

{ return n

{ return nasu mi cn aP os il jk a( ); }

{ // Nije realizovano

throw new U n s u p p o r te dO pe ra ti onE xc ep ti on ();

} }; } }; }

public class Posta { enum PrZaObraduPoste { 0P ST A_D0STAVA { boolean obrada(Posiljka p) switch(p.opstaDostava) case DA; print(p + " ; koristi optu dostavu"); return true; default: return false; { {

} } },
MA SINS K0_S KENIRANJ E { boolean obrada(Posiljka p) { sw i t c h (p.MozeSeSkenir a t i ) { case N E M O Z E S E S K E N I R A T I : return false; default: switch(p.adresa) case NETACNA: default: printfp + " : isporui a u to ma ts ki"); return true; { return false;

},
VIZUELNIPREGLED { boolean ob ra da (Posiljka p) switch(p.Citljivo) case NECITLJIVO: default: switch(p.adresa) default: print(p + " : isporui obino"); return true; { case NETACNA: return false; { return false; {

} }

Poglavlje 19: Nabrojani tipovi

829

} },
VRATI_POSILJAOCU { boolean obrada{Posiljka p) { switch(p.povratnaAdresa) default: print(p + " : vrati p o i 1j a o c u " ) ; return true; { case NEDOSTAJE: return false;

} } };
abstract boolean obrada(Posiljka p);

}
static void o b ra da (P os i1jka p) { for(PrZaObraduPoste przaobradu : PrZaObraduPoste.values()) i f(przaobradu.obrada(p)) return; print(p + " se ne moe uruiti");

}
public static void main(String[] pr in t( po si lj ka .d et ails( ) ); obrada(posilj k a ) ; p r i n t C 1* * * * * " ) ; args) { for(Posiljka posiljka : Po si1jka.gene ra to r( lO )) {

} }
} /* Ispis: Poiljka 0, Opta dostava: Poiljka 0 : isporui obino Poiljka I, Opta dostava: NE5, Adresa se moze skenirati: DA3, Adresa NE2, Adresa se moze skenirati: N E MO ZE SE SK EN IR AT I, Adresa itljiva: DA3, Adresa Adresa: OKl, Povratna adresa: OKl

*****

itljiva: NECITLJIVO, Adresa Adresa: 0K5, Povratna adresa: OKl Poiljka 1 : isporui automatski
*****

Poiljka 2, Opta dostava:

DA, Adresa se moze skenirati: DA3, Adresa

itljiva: DAl, Adresa Adresa: OKl, Povratna adresa: 0K5

*****

Poiljka 2 : koristi optu dostavu Poiljka 3, Opta dostava: NE4, Adresa se moze skenirati: DA3, Adresa Povratna adresa: 0K4

itljiva: DAl, Adresa Adresa: NETACNA, Poiljka 3


* * * * *

: vrati poiljaocu NE4, Adresa se moze skenirati: N E M O ZE SE SK EN IR AT I,

Poiljka 4, Opta dostava: Poiljka 4

Adresa itljiva: DAl, Adresa Adresa: NETACNA, Povratna adresa: 0K2 : vrati poiljaocu NE3, Adresa se moze skenirati: DAl, Adresa

*****
Poiljka 5, Opta dostava: itljiva: NECITLJIVO, Adresa Adresa: 0K4, Povratna adresa: 0K2

83 0

Misliti na Javi

Poiljka 5 : isporui automatski

*****
Poiljka 6, Opta dostava: DA, Adresa se mo ze skenirati: DA4, Adresa Povratna adresa: 0K4 itljiva: NECITLJIVO, Adresa Adresa: 0K4, Poiljka 6 : koristi optu dostavu
* * * * *

Poiljka 7, Opta dostava:

DA, Adresa se mo ze skenirati: DA3, Adresa

itljiva: DA4, Adresa Adresa: 0K2, Povratna adresa: NEDOSTAJE Poiljka 7 : koristi optu dostavu
* * * * *

Poiljka 8, Opta dostava:

NE3, Adresa se mo ze skenirati: DAl, Adresa Povratna adresa: NEDOSTAJE

itljiva: DA3, Adresa Adresa: NETACNA, Poiljka 8 se ne mo e uruiti


* * * * *

Poiljka 9, Opta dostava: Poiljka 9 : isporui obino


* * * * *

NEl, Adresa se mo ze skenirati: NEMOZESESKENIRATI,

Adresa itljiva: DA2, Adresa Adresa: OKl, Povratna adresa: 0K4

* ///:Chain o f Responsibility je izraen u nabrojanom tipu P rZ aO b rad u P o ste, a redosled en u m definicija odreuje redosled isprobavanja strategija na svakoj poiljci. Isprobava se svaka strategija redom dok jedna ne uspe ili dok sve ne propadnu - u tom sluaiu poiljka se ne m oe uruiti. Veba 8: (6) Izm enite program Posta.java tako da poiljke moe da prosleuje (s jedne na drugu adresu). Veba 9: (5) Izm enite klasu P osta tako da koristi Enum M ap. P rojekat:2 Na specijalizovanim jezicima kao to je Prolog ovakvi problem i se reavaju ulanavanjem u n a za d (engl. backw ard chaining). Uz Posta.java kao nadahnue, prouite takve jezike i napiite program koji om oguava lako oavanje novih pravila" sistemu.

Maine stanja s nabrojanim tipovima


N abrojani tipovi m ogu biti idealni za pravljenje m aina stanja. Maina stanja se moe nalaziti u jed no m od konanog broja odreenih stanja. Maina obino prelazi iz jednog stanja u drugo na osnovu ulaza, ali postoje i prelazna stanja; maina izlazi iz njih im obavi njihove zadatke. U svakom stanju dozvoljeni su odreeni ulazi. Razni ulazi menjaju stanje maine u razliita nova stanja. Poto nabrojani tipovi ograniavaju skup m oguih sluajeva, veoma su podesni za nabrajanje razliitih stanja i ulaza. Svakom stanju obino je pridruen i neki izlaz. D obar prim er m aine stanja je m aloprodajni autom at. Prvo u nabrojanom tipu definiem o razne ulaze:
Projekti su predlozi koji se m ogu koristiti (recim o) za sem inarske radove. Vodi s reenjim a ne sari reenja p rojekata.

Poglavlje 19: Nabrojani tlpovi

831

//: nabrojani/Ulaz.java package nabrojani; import j a v a . u t i l .*; public enum Ulaz { P E T 0 P A R A C ( 5 ) , DE SE TO P A R A C ( I O ) , F R TA LJ(25), DINAR(IOO), Z U B N A P A S T A ( 2 0 0 ) , CIPS(75), SOK(IOO), SAPUN(50), O D US TA NI_OD_TRANSAKCIJE { public int iznos(() { // Onemoguiti throvv new Ru nt im eE xc ep ti on (" PRE KI NI .i zn os () ");

} },
STOP { // Ovo mora biti poslednja instanca. public int iznos() { // Onemoguiti throw new RuntimeE xc ep ti on (" UGA SI.i z n o s ()");

} };
int vrednost; // U parama Ulaz(int vrednost) Ulaz() {} { return vrednost; }; // U parama { // STOP ne int iznos() { t h i s .vrednost = vrednost; }

static Random slucajan = new Random(47); public static Ulaz randomSelection() // treba obuhvati t i : return v a l u e s ()[slucajan.nextlnt(values().1ength - 1)];

} III-Vodite rauna o tom e da je dvam a ulazima p ridruen odreeni iznos, pa je u interfejsu definisana m etoda iz n o s ( ). M edutim , iz n o s ( ) nije prikladno zvati za ostala dva tipa ulaza, pa oni generiu izuzetak ako je pozovete za njih. Iako je ovo neobino (definisati m etodu u interfejsu, a potom generisati izuzetak ukoliko je pozovete za neke realizacije), to m oram o da radim o zbog ogranienja koja namee rezervisana re enum . A u tom atZ aM aloprodaju e na ove ulazc reagovati tako to e ih najpre kategorizovati pom ou nabrojanog tipa K ategorija, pa e te kategorije upotrebiti u naredbi svvitch. Iz ovog prim era vidite kako se nabrojani tipovi mogu upotrebiti da bi kod bio jasniji i lake se odravao:
//: nabrojani/AutomatZaMaloprodaju.java // { A r g u m e n t i : UlazUAutomatZaMaloprodaju.txt} package n a b r o j a n i ; import j a v a . u t i l .*; import net.mindview.util.*; import static n a b r o j a n i .U1az.*; import static ne t. mi nd vi ew .u ti1 .P r i n t .*; enum Kategorija { NOVAC(PETOPARAC, DESETOPARAC, FRTA LJ, DINAR), IZBOR_ARTIKLA(ZUBNAPASTA, CIPS, SOK, S A P U N ) , PREKINI TRANSAKCIJU(ODUSTANI OD TRANSA KC IJ E),

832

Misliti na Javi

U6ASI(STOP); private Ulaz[] vr e d n o s t i ; Kategorija(Ulaz... tipovi) { vrednosti = tipovi; } private static En umHap<Ulaz,Kategorija> kategorije = new EnumMap<Ul a z ,Kategorija>(Ul a z .cl a s s ) ; static { for(Kategorija c : Kategorija.class.getEnumConstants()) for(Ulaz type : c.vrednosti) kategorije.put(type, c);

}
public static Kategorija kategorizuj(Ulaz ulaz) return ka te go rije.get(ulaz); {

} }
public class AutomatZaMaloprodaju private static int iznos = 0; private static Ulaz izbor = n u l l ; enum TrajanjeStanja { PR0LAZN0 } // Nabrojani // Kao oznaka enum Stanje { MIRUJE { void next(Ulaz ulaz) case NOVAC: iznos += ul a z . i z n o s ( ) ; stanje = PRIMANJEJIOVCA; break; case UGASI: stanje = TERMINAL; de fa ult: { switch(Kategorija . k a t e g o r i z u j (ulaz)) { tip {

private static Stanje stanje = Stanje.MIRUJE;

} } },
PRIMANJE_NOVCA { void next(Ulaz ulaz) case NOVAC: iznos += u l az.iznos (); break; case IZB0R_ARTIKLA: izbor = ulaz; if(iznos < i z b o r . i z n o s O ) print("Nedovoljno novca za " + izbor); else stanje = OBRACUN; break; case PREKINI_TRANSAKCIJU: stanje = V R A C A N J E K U S U R A ; break; { s w it ch (K at eg or ij a. kat eg or iz uj (u la z)) {

Poglav|je 19: Nabrojani tipovi

833

case UGASI: stanje = TERMINAL; default:

} } },
O B R A CU N( Tr aj an je St anj a.PROLAZNO) void next() { " + izbor); print("uzmite kupljeno: iznos -= izbor. iz no s( ); stanje = VRACANJEJOJSURA; {

} }.
VRACAN JE _K US UR A( Tr aja nj eS ta nj a.PROLAZNO) void next() { " + iznos); if(iznos > 0) { print("Va kusur: iznos = 0; {

}
stanje = MIRUJE;

} },
TERMINAL { void output() Stanjef) {} { stanjeProlazno = true; } { { p r in t( "Z au st av lj en "); } }; private boolean stanjeProlazno = false; Stanje(TrajanjeStanja trans) void next(Ulaz ulaz)

throw new RuntimeException("Pozivajte " + "next(Ulaz ulaz) samo za neprolazna stanja");

}
void next() { throw new Ru ntimeException("Metodu next() pozivajte za " + "stanja Trajan je St an ja .P RO LAZ NO ");

}
void output() { p r i n t( iz no s); } {

}
static void run(Generator<Ulaz> gen) while(stanje != Stanje.TERMINAL) { st an je .n ex t( ge n. ne xt( )); while(stanje.stanjeProlazno) st an je .n ex t( ); st a n j e .o ut pu t( );

public static void main(String[] i f (args.length == 1)

args)

Generator<Ulaz> gen = new R a n d o m In pu tG en er at or( ); gen = new Fi1elnp ut Ge ne ra to r( ar gs); run(gen);

834

Misliti na Javi

}
// Jednostavna provera ispravnosti: class RandomlnputGenerator implements Generator<Ulaz> { public Ulaz next() { return Ul az .r an do mS el ec ti on( ); }

}
// Napravi ulaze od dat. znakovnih nizova razdvojenih znakom class FilelnputGenerator implements Generator<Ulaz> { private Iterator<String> ulaz; public FileInputGenerator(String imeDatoteke) ulaz = new TextFile(imeDatoteke, { "; " ) .i te rator();

}
public Ulaz next() return n u l1; return Enum.valueOf(Ulaz.class, ul a z . n e x t ().trim()); { if(!ulaz.hasNext())

}
} /* Ispis: 25 50 75 uzmite kupljeno: CIPS

0
100
200 uzmite kupljeno: ZUBNAPASTA
0

25 35 Va kusur: 35

0
25 35 Nedovoljno novca za S0K 35 60 70 75 Nedovoljno novca za S0K 75 Va kusur: 75

0
Zaustavljen

* ///:Poto se enu m instanca najee bira pom ou naredbe svvitch (obratite panju na dodatni trud uloen u to da se naredba sw itch moe lako upotrebljavati s nabrojanim tipovima), jedno od najeih pitanja u vezi sa upotrebom nabrojanih tipova jeste: ,,Po kojem

Poglav[je 19: Nabrojani tipovi

835

osnovu da biram? U navedenom prim eru je najlake krenuti unazad od klase AutomatZaMaloprodaju. U svakom stanju treba izabrati jednu od osnovnih kategorija ulaznog dejstva: prim anje novca, izbor artikla, odustajanje od transakcije, i iskljuenje maine. M eutim , unutar tih kategorija imate razliite novane apoene koje kupac moe ubaciti u m ainu i razliite artikle koje moe izabrati. Nabrojani tip Kategorija grupie razliite tipove ulaza tako da m etoda kategorizuj( ) moe u nutar naredbe switch da proizvede odgovarajuu kategoriju. Ta m etoda delotvorno i bezbedno obavlja pretraivanje pom ou m ape

EnumMap.
Ako prouite klasu AutomatZaM aloprodaju, videete da se sva stanja razlikuju i da drugaije reaguju na ulaze. O bratite panju i na dva prelazna stanja; u m etodi r u n ( ) m aina eka na neki Ulaz i ne prestaje da m enja stanja dok ne izae iz prelaznog stanja. Autom atZaM aloprodaju m oem o ispitati na dva naina, pom ou dva razliita objekta tipa Generator. Random lnputG enerator stalno proizvodi nove ulaze, sve sem ulaza UGASI. Ukoliko dovoljno dugo izvravate takav program , donekle ete proveriti ispravnost, tj. videete da li je maina sklona da odluta u loe stanje. FilelnputG enerator prim a datoteku koja u tekstualnom obliku opisuje ulaze, pretvara ih u instance nabrojanog tipa i pravi objekte tipa Ulaz. Evo tekstualne datoteke koja proizvodi p rethodno prikazane rezultate:
//:! nabrojani/lllazUAutomatZaMaloprodaju.txt FRTALJ; FRTALJ; FRTALJ; CIPS; DINAR; DINAR; ZUBNAPASTA; F R T A L J ; DESETOPARAC; ODUSTANI_OD_TRANSAKCIJE; F R T A L J ; DESETOPARAC; SOK; F R T A L J ; DESETOPARAC; PETOPARAC; SOK; ODUSTANI_OD_TRANSAKCIJE; STOP;

///:Jedno od ogranienja ovog dizajna jeste to to polja klase Autom atZaM aloprodaju kojima pristupaju instance nabrojanog tipa Stanje m oraju biti statina, to znai da moete imati sam o jednu instancu klase AutomatZaM aloprodaju. To vas nee m nogo zabrinuti ako pom islite na stvarnu (ugraenu Java) realizaciju, poto ete verovatno imati samo jednu aplikaciju po autom atu.

Veba 10: (7) Izmenite (samo) klasu AutomatZaM aloprodaju koristei EnumMap, tako da program moe imati vie instanci klase AutomatZaM aloprodaju. Veba 11: (7) U pravom autom atu za m aloprodaju trebalo bi da se m ogu lako dodavati i menjati vrste artikala koji se prodaju, pa su ogranienja koja nabrojan tip nam ee na Ulaz nepraktina (poto enum definie konaan i neprom enljiv skup tipova). Izmenite program AutomatZaM aloprodaju.java tako da artikle koji se prodaju predstavlja klasa um esto to su deo Ulaza, i pom ou tekstualne datoteke inicijalizujte ArrayList tih objekata. (U potrebite net.mindview.utiI.TextFile.) Projekat: Projektujte m eunarodni autom at za m aloprodaju (koristei intern a o n alizaciju ), tako da se ista maina moe lako prilagoditi za razliite zemlje.

836

Misliti na Javi

Vlekratno otkrivanje tipa


Kada vie tipova ulazi u m eusobnu interakciju, program se lako zapetlja. Na prim er, razm otrite sistem koji ralanjuje i izraunava m atem atike izraze. Hteli biste da kaete Broj.plus(Broj), Broj.pomnozi(Broj) itd., gde je Broj osnovna klasa neke porodice num erikih objekata. Ali kada kaete a.plus(b), a ne znate taan tip ni od a ni od b, kako e oni ui u interakciju? O dgovor poinje neim o em u verovatno nikada niste razmiljali: Java obavlja samo jednokratno otkrivanje tipa (engl. single dispatching). D rugim reima, ukoliko obavljate operaciju s vie objekata nepoznatih tipova, Java moe da pozove m ehanizam dinamikog povezivanja sam o za jedan od njih. Tim e se ne bi reio p rethodno opisani problem, pa neke tipove m oram o sam i (runo) da otkrijem o i tako napravim o sopstveno dinam iko povezivanje. Reenje se naziva viekratno otkrivanje tipa (engl. m ultiple dispatching). (U ovom sluaju bie sam o dva otkrivanja - to je dvokratno otkrivanje tipa.) Polimorfizam je mogu sam o preko poziva m etoda, pa ako hoete dvokratno otkrivanje tipa, m orate im ati dva poziva metode: prvi da otkrijete prvi nepoznati tip, i drugi da otkrijete drugi nepoznati tip. Pri viekratnom otkrivanju tipa, m orate im ati po jedan virtuelni poziv za svaki od nepoznatih tipova - ukoliko im ate posla s dve razliite hijerarhije tipova koji su u interakciji, u svakoj hijerarhiji vam treba po jedan virtuelni poziv. Po pravilu, treba napraviti konfiguraciju tako da jedan poziv m etode proizvodi vie virtueinih poziva m etoda i tim e opsluuje vie tipova. To ete postii pom ou vie m etoda: za svako otkrivanje tipa treba vam jedan poziv metode. U narednom prim eru (koji realizuje igru papir, kamen, makaze koja se izvorno zove RoSham Bo) m etode su nazvane ta k m ic e n je () i iz rc ( ) i obe su lanovi istog tipa. O ne proizvode jedan od tri m ogua ishoda:
//: nabrojani/Ishod.java package n a b r o j a n i ; public enum Ishod { POBEDA, //: n a b r oj an i/ Ro Sh am Bo l.java // Primer viekratnog otkrivanja tipa. package na b r o j a n i ; import java.util import static n a b r o j a n i .Ishod.*; interface Stavka { Ishod takmicenje(Stavka st); Ishod izrc(Papir p ) ; Ishod izrc(Makaze m ) ; Ishod izrc(Kamen k ) ; PORAZ, NERESENO } ///:-

}
class Papir implements Stavka {

'

O v aj p r im e r je vie g o d in a s ta ja o n a p is a n n a C + + -U i Javi (u kn jiz i T hitiking iti Patterns) n a a d re si w w w .M indView .net a p o to m je, b e z s p o m in ja n ja a u to ra , n a v e d e n u k n jiz i d ru g ih pisaca.

Poglavlje 19: Nabrojani tipovi

837

publ ic Ishod takmicenje(Stavka st) public Ishod izrc(Makaze m) public String toString()

{ return st.izrc(this); } }

publ ic Ishod izrc(Papir p) { return NERESENO; { return POBEDA; } } publ ic Ishod izrc(Kamen k) { return PORAZ; { return "Papir";

}
class M a ka ze implements Stavka { publ ic Ishod takmicenje(Stavka st) publ ic Ishod izrc(Makaze m) public String toString() { return st.izr c( th is ); } } } } } public Ishod izrc(Papir p) { return PORAZ; publ ic Ishod izrc(Kamen k) { return POBEDA; { return "Makaze";

{ return NERESENO;

}
class Kamen implements Stavka { public Ishod takmicenje(Stavka st) { return st.i zr c( th is ); } } } } } public Ishod izrc(Papir p) { return POBEDA; publ ic Ishod izrc(Makaze m) { return PORAZ; public String t o S t r i n g O { return "Kamen";

public Ishod izrc(Kamen k) { return NERESENO;

}
public class RoShamBol { static final int VELICINA = 20; { { pr ivate static Random slucajan = new Random(47); public static Stavka newltem() sw i tc h ( s l u c a j a n , n e x t l n t (3)) d e f a u l t: case 0: return new Makaze(); case 1: return new Papir(); case 2: return new Kamen();

} }
public static void match(Stavka a, Stavka b) { System.out.pri ntln( a + " u odnosu na " + b + ": " + a.takm ic en je (b )); {

}
public static void main(String[] ma t c h ( n e w l t e m ( ) , newltem()); args) for(int i = 0; i < VELICINA; i++)

}
} /* Ispis: Kamen u odnosu na Kamen: NERESENO Papir u odnosu na Kamen: POBEDA Papir u odnosu na Kamen: Papir u odnosu na Kamen: POBEDA POBEDA

Makaze u odnosu na Papir: POBEDA Makaze u odnosu na Makaze: NERESENO Makaze u odnosu na Papir: POBEDA Kamen u odnosu na Papir: PORAZ

838

Misliti na Javi

Papir u odnosu na Papir: NERESENO Kamen u odnosu na Papir: PORAZ Papir u odnosu na Makaze: PORAZ Papir u odnosu na Makaze: PORAZ Kamen u odnosu na Makaze: POBEDA Kamen u odnosu na Papir: PORAZ Papir u odnosu na Kamen: POBEDA Makaze u odnosu na Papir: POBEDA Papir u odnosu na Makaze: PORAZ Papir u odnosu na Makaze: PORAZ Papir u odnosu na Makaze: PORAZ Papir u odnosu na Makaze: PORAZ

* ///:-

Stavka je interfejs za tipove koji e biti viekratno otkrivani. RoSham B ol.m atch( ) prim a dva objekta tipa Stavka i postupak dvokratnog otkrivanja tipa otpoinje pozivom funkcije Stavka.takm icenje( ). V irtuelni m ehanizam odreduje tip od a, pa se budi unutar funkcije takm icenje( ) konkretnog tipa od a. Funkcija takm icenje( ) obavlja drugo otkrivanje tipa pozivom m etode iz rc ( ) za preostali tip. Prosleivanjem sebe (this) kao argum enta m etode iz rc ( ) proizvodi poziv preklopljene funkcije iz r c ( ), pa ostaje sauvana inform acija o tipu dobijena u prvom otkrivanju tipa. Kada se dovri drugo otkrivanje tipa, znam o taan tip oba objekta tipa Stavka. Program iranje viekratnog otkrivanja tipa zahteva veliku cerem oniju, ali ne gubite iz vida dobijenu sintaksiku eleganciju sam og poziva - um esto da piete nezgrapan kod za utvrivanje tipa jednog ili vie objekata tokom nekog poziva, jednostavno kaete: Vas dvoje! Ne zanim a me koji su vai tipovi, sam o uite propisno u m eusobnu interakciju! M eutim , neka vam ta vrsta elegancije postane vana pre nego to krenete na viekratno otkrivanje tipa.

Otkrivanje tipa pomou nabrojanih tipova


Prosto prevoenje program a RoShamBol.java u reenje iju osnovu ine nabrojani tipovi nije jednostavno, jerenum instance nisu tipovi, pa preklopljene m etode i z r c ( ) nee raditi - enum instance ne m oete upotrebljavati kao tipove u argum entu. M eutim, realizacija viekratnog otkrivanja tipa p om ou nabrojanih tipova moe se obaviti na vie razliitih naina. U jednom nainu upotrebljava se k o nstru k to r za inicijalizaciju svake en u m instance celim ,,reom ishoda; zajedno uzev, tim e se dobija svojevrsna tabela za pronalaenje:
//: nabrojani/RoShamBo2.java // Izbor konstante jednog nabrojanog tipa // korienjem drugog u naredbi svvitch. package n a b r oj an i; import static na br oj an i.Ishod.*; public enum RoShamBo2 implements Ta km ic ar<RoShamBo2> { PAPIR(NERESENO, PORAZ, P O B E D A ) , MAKAZE(POBEDA, NERESENO, P O R A Z ) ,

Poglavjje 19: Nabrojani tipovi

839

KAMEN(PORAZ, POBEDA, NERESENO); p r i v a t e Ishod vPAPIR, vMAKAZE, vKAMEN; RoShamBo2(Ishod p a p i r . I s h o d makaze,Ishod kamen) { th is .v P A P IR = p a p i r ; this.vMAKAZE = makaze; this.vKAMEN = kamen;

}
p u b l i c Ishod takmicenje(RoShamBo2 i t ) s w itc h (it) { d e fa u lt: case PAPIR: r e t u r n VPAPIR; case MAKAZE: r e t u r n vMAKAZE; case KAMEN: r e t u r n vKAMEN; {

} }
p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] a rg s) RoShamBo.igraj(RoShamBo2.class, 2 0 ); {

}
} /* Is p is :
KAMEN u odnosu na KAMEN: NERESENO MAKAZE u odnosu na K A M E N : PORAZ MAKAZE u odnosu na KAMEN: PORAZ MAKAZE u odnosu na KAMEN: PORAZ PAPIR u odriosu na MAKAZE: PORAZ PAPIR u odnosu na PAPIR: NERESENO PAPIR u odnosu na MAKAZE: KAMEN u odnosu na MAKAZE: PORAZ POBEDA

MAKAZE u odnosu na MAKAZE: NERESENO KAMEN u odnosu na MAKAZE: POBEDA MAKAZE u odnosu na PAPIR: POBEDA MAKAZE u odnosu na PAPIR: POBEDA KAMEN u odnosu na PAPIR: PORAZ KAMEN u odnosu na M A K A Z E : POBEDA MAKAZE u odnosu na KAMEN: PAPIR u odnosu na MAKAZE: MAKAZE u odnosu na PAPIR: MAKAZE u odnosu na PAPIR: MAKAZE u odnosu na PAPIR: MAKAZE u odnosu na PAPIR: PORAZ PORAZ POBEDA POBEDA POBEDA POBEDA

* ///:Nakon to m etoda takm icenje( ) otkrije oba tipa, jedino dejstvo je vraanje rezultujueg objekta tipa Ishod. M eutim , mogli biste pozvati i neku drugu m etodu, ak (na prim er) preko objekta tipa C o m m a n d dodeljenom u konstruktoru. RoShamBo2.java je m nogo manji i jednostavniji od prvobitnog prim era, te ga je lake i pratiti. Vodite rauna o tom e da i dalje koristim o dva otkrivanja tipa da bism o utvrdili tip oba objekta. U program u RoShamBol.java oba otkrivanja su obavljena preko virtuelnih poziva m etoda, ali ovde je virtuelni poziv m etode upotrebljen sam o u prvom otkrivanju tipa. U drugom otkrivanju tipa upotrebljen je switch, ali sve je bezbedno zato to enum ograniava broj izbora u naredbi switch.

840

Misliti na Javi

Kod koji upravlja nabrojanim tipom tako je ralanjen da ga m oem o koristiti u drugim prim erim a. Prvo, interfejs Takmicar definie tip koji se takmii s drugim Takmicarom:
/ / : n a b r o ja n i/ T a k m ic a r . ja v a / / I z b o r k o n s tan te je dnog nabrojanog t i p a / / k o ri e n je m drugog u naredbi s w it c h . package n a b r o j a n i ; p u b l i c i n t e r f a c e Takmicar<T extends T a k m ic a r < T { Ishod ta k m ic e n je ( T t a k m i c a r ) ;

} ///= Z atim definiemo dve statine m etode (statine zato da ne bism o m orali eksplicitno zadati param etarski tip). Prvo m a tc h ( ) poziva takm icenje( ) za jedan objekat tipa Takm icar protiv drugog, pa vidite kako je u ovom sluaju dovoljno da param etarski tip bude Takmicar<T>. Ali u m etodi ig ra j( ), param etarski tip m ora biti Enum<T> jer se koristi u m etodi N abrojaniT ipovi.random ( ), i Takmicar<T>, zato to se prosleuje m etodi

m a tc h ():
//: nabrojani/RoShamBo.java // Zajednike alatke za RoShamBo primere. package n a b r oj an i; import net.mindview.util.*; public class RoShamBo { public static <T extends T a k m i c a r < T void match(T a, T b) { System.out.println( a + 1 1 u odnosu na " + b + ": " + a. ta km ic en je (b ));

}
public static <T extends Enum<T> & T a k m i c a r < T void igraj(Class<T> rsbKlasa, match( Na br oj an iT ip ovi.random(rsbKlasa), NabrojaniTi p o v i .ra nd om(rsbKlasa)); int velicina) { for(int i = 0; i < velicina; i++)

} 1 ///:M etoda ig ra j( ) nema povratnu vrenost koja obuhvata param etar tipa T, pa izgleda kao da bism o mogli upotrebiti dokerske argum ente u n u tar tipa Class<T> um esto to koristim o opis vodeih param etara. M eutim , dokerski argum enti se ne m ogu protezati na vie od jednog osnovnog tipa, pa m oram o da koristim o gornji izraz.

Korienje metoda koje se menjaju u zavisnosti od konstante nabrojanog tipa


Poto m etode koje se menjaju u zavisnosti od konstante nabrojanog tipa om oguuju pravljenje razliitih realizacija m etoda za svaku enum instancu, m oda vam izgleda da su

Poglavlje 19: Nabrojani tipovi

841

one savreno reenje za viekratno otkrivanje tipa. Ali iako n a taj nain m ogu dobiti drugaija ponaanja, enum instance nisu tipovi, pa ih ne m oete koristiti kao argum ente tipova u potpisim a metoda. U ovom prim eru upotrebljavate naredbu svvitch, i to je najvie to se m oe uraditi:
/ / : nabrojani/Ro ShamBo3.java / / K o r i e n je metoda k o je se menjaju u z a v i s n o s t i od k o n s ta n te / / (nabroja nog t i p a ) . package n a b r o j a n i ; im p o r t s t a t i c n a b r o j a n i . I s h o d . * ; p u b l i c enum RoShamBo3 implements Takmicar<RoShamBo3> { PAPIR { p u b l i c Ishod t a k m icenje(RoShamBo3 i t ) { s w itc h (it) { d e f a u l t : / / Da se p r e v o d ila c ne bi bunio case PAPIR: r e t u r n NERESENO; case MAKAZE: r e t u r n PORAZ; case KAMEN: r e t u r n POBEDA;

} } },
MAKAZE { public Ishod takmicenje(RoShamBo3 it) switch(it) defa ult: case PAPIR: return POBEDA; case MAKAZE: return NERESENO; case KAMEN: return PORAZ; { {

} } },
KAMEN { public Ishod takmicenje(RoShamBo3 it) switch(it) default: case PAPIR: return PORAZ; case MAKAZE: return POBEDA; case KAMEN: return NERESENO; { {

} } };
public abstract Ishod takmicenje(RoShamBo3 it); public static void main(String[] args) { RoSh am Bo .igraj(RoShamBo3.class, 2 0 ) ;

}
} /* Isti ispis kao u RoShamBo2.java *///-

842

Misliti na Javi

Iako ovo radi, a nije ni glupo, reenje u program u RoSham Bo2.java kao da zahteva m anje koda za dodavanje novog tipa, te izgleda jednostavnije. M eutim , RoSham Bo3.java m oem o pojednostaviti i saeti:
/ / : nabrojani/RoShamBo4.java package n a b r o j a n i ; p u b l i c enum RoShamBo4 implements Takmicar<RoShamBo4> { KAMEN { p u b l i c Ishod takmicenje(RoShamBo4 p r o t i v n i k ) { r e t u r n takmicenje(MAKAZE, p r o t i v n i k ) ;

} },
MAKAZE { p u b l i c Ishod takmicenje(RoShamBo4 p r o t i v n i k ) r e t u r n takmicenje(PAPIR, p r o t i v n i k ) ; {

} },
PAPIR {
public Ishod takmicenje(RoShamBo4 protivnik) return takmicenje(KAMEN, protivnik); {

} };
Ishod takmicenje(RoShamBo4 gubitnik, RoShamBo4 protivnik) return ((protivnik == this) ? Ishod.NERESENO ? Ishod.POBEDA : Ishod.PORAZ)) ; : ((protivnik == gubitnik) {

}
public static void m a i n ( S t r i n g [] args) Ro ShamBo.igraj(RoShamBo4.cl ass, 2 0 ) ; {

}
} /* Isti ispis kao u RoShamBo2.java *///:-

Ovde se rugo otkrivanje tipa obavlja verzijom m etode takm icenje( ) s dva argum enta; m etoda obavlja niz poreenja i stoga radi slino naredbi switch. Program je manji, ali i m anje jasan. U velikom sistem u ta nejasnost moe imati strane posledice.

Otkrivanje tipa pomou mapa EnumMap


,,Pravo dvokratno otkrivanje tipa m oem o obaviti pom ou klase EnumMap koja veoma efikasno radi s nabrojanim tipovim a. Poto je na cilj pravljenje izbora na osnovu dva nepoznata tipa, za dvokratno otkrivanje tipa m oem o upotrebiti EnumMap iji su lanovi drugi objekti tipa EnumMap:
//: na brojani/RoShamBo5.java // Viekratno otkrivanje tipa pomou mape EnumMap // iji su lanovi drugi objekti package nabrojani; import j a v a . u t i l .*; tipa EnumMap.

Poglavlje 19: Nabrojani tipovi

843

import static nabrojani.Ishod.*; enum RoShamBo5 implements Takmicar<RoShamBo5> { PAPIR, MAKAZE, KAMEN; stati c EnumMap<RoShamBo5,EnumMap<RoShamBo5,I s h o d tabela = new EnumMap<RoShamBo5, EnumMap<RoShamBo5,Ishod(RoShamBo5.class); static { for(RoShamBo5 it : RoShamBo5.vrednosti()) tabela.put(it, new EnumMap<RoShamBo5,Ishod>(RoShamBo5.class)); inicReda(PAPIR, NERESENO, PORAZ, POBEDA); inicReda(MAKAZE, POBEDA, NERESENO, PORAZ); inicReda(KAMEN, PORAZ, POBEDA, NERESENO);

}
static void inicReda(RoShamBo5 it, Ishod vPAPIR, Ishod vMAKAZE, Ishod vKAMEN) { EnumMap<RoShamBo5,Ishod> red = RoShamBo5.tabela.get(it); red.put(RoShamBo5.PAPIR, vPAPIR); red.put(RoShamBo5.MAKAZE, vMAKAZE); red.put(RoShamBo5.KAMEN, vKAMEN);

}
public Ishod takmicenje(RoShamBo5 it) { return tabela.get(this).get (it);

}
public static void main(String[] args) { RoShamBo.igraj(RoShamBo5.class, 20);

}
} /* Isti ispis kao u RoShamBo2.java *///:

E num M ap se inicijalizuje u statinoj odredbi; struk tu ra poziva m etode in ic R e d a () nalik je na tabelu. O bratite panju na m etodu ta k m ic e n je (), gde se oba otkrivanja tipa obavljaju u istoj naredbi.

Korienje 2-D niza


Reenje m oem o jo pojednostaviti kada uoim o da svaka en u m instanca im a fiksnu vrednost (dobijenu na osnovu rednog broja njenog deklarisanja) i da m etoda o r d in a l( ) proizvodi tu vrednost. Najm anje i najjednostavnije reenje (a m oda i najbre, m ada ne treba zaboraviti da Enum M ap upotrebljava interni niz) daje dvodim enzionalni niz koji takm iare preslikava u ishode:
//: nabrojani/RoShamBo6.java // Nabrojani tipovi i "tabele" umesto // viekratnog otkrivanja tipa. package nabrojani; import static nabrojani.Ishod.*; enum RoShamBo6 implements Takmicar<RoShamBo6> {

844

Misliti na Javi

PAPIR, MAKAZE, KAMEN; private static Ishod[] [] tabela = { NERESENO, PORAZ, POBEOA }, // { POBEDA, NERESENO, PORAZ }, // { PORAZ, POBEDA, NERESENO }, //

{ PAPIR MAKAZE KAMEN

};
public Ishod takmicenje(RoShamBo6 drugi) { return tabela[this.ordinal()][drugi.ordinal()];

}
public static void main(String[] args) { RoShamBo.igraj(RoShamBo6.class, 20);

} } ///= Tabela im a tano onaj poredak kao pozivi m etode in ic R e d a () u p rethodnom prim eru. Ovaj kratak kod deluje m nogo privlanije od p reth o d n ih prim era, delom zato to izgleda m nogo razumljiviji i izmenljiviji, ali i zato to bi se reklo da je jednostavniji. M edutim , nije ba onoliko ,,bezbedan kao p retho d n i prim eri, zato to se upotrebljava niz. Kada je niz vei, lake je pogreiti u veliini, i ako ispitivanje ne obuhvati sve m ogunosti, neto bi moglo da procuri. Sva ova reenja predstavljaju razliite vrste tabela, ali ih vredi istraiti da bismo pronali najprikladniju. Vodite rauna o sledeem: iako je poslednje reenje najkom paktnije, ono je i prilino neprilagodljivo, zato to m oe da proizvede sam o konstantan izlaz za date konstantne ulaze. M eutim , nita vas ne spreava da pom ou takve tabele napravite funkcijski objekat. Pri odreenim vrstam a problem a, koncept koda upravijanog tabelom um c veoma dobro da poslui.

Saetak
Iako nabrojani tipovi sami po sebi nisu previe sloeni, ovo poglavlje sam pom erio u drugi deo knjige zbog onoga to se m oe uraditi kom binacijom nabrojanih tipova i polim orfizma, generikih tipova i refleksije. M ada znatno sofisticiraniji nego u jezicim a C i C + + , nabrojani tipovi i dalje predstavljaju ,,malu m ogunost, neto bez ega je jezik opstajao (pom alo nezgrapno) mnogo godina. Ovo poglavlje pokazuje kako m oe biti vaan doprinos ,,male m ogunosti - katkada nam daje ba onu polugu kojom problem m oem o reiti elegantno i jasno, a u celoj knjizi sam isticao koliko je elegancija vana. Jasnoa m oe biti inilac koji odreuje da li je reenje uspeno ili nije dobro zato to ostali ne m ogu da ga razum eju. to se tie jasnoe, Java 1.0 je proizvela zbrku zbog korienja term ina ,,enum eration um esto uobiajenog i prihvaenog term ina iterator" za objekat koji bira svaki element neke sekvence. U nekim jezicim a ak se i nabrojani tipovi nazivaju ,,enum erators! Ta greka je ispravljena u Javi, ali interfejs E n u m e ra tio n nisu smeli sam o tako da izbace, pa se jo via u starim (a ponekad i u novim !) program im a, biblioteci i dokum entaciji.
R eenja o d a b r a n ih vebi d a ta su u e le k tro n sk o m d o k u m e n tu Thc Thinking in Java Annotatcd Sola-

tion Guide, koji se m o e k u p iti n a lok aciji www.MindView.com.

Anotacije
A notacije (ili m etapodaci) fo rm a lizo va n su nain dodavanja inform acija kodu, to olakava kasniju upotrebu tih p o d a ta k a .'
A n o t a c ije
su delom p o s l e d ic a o p St e g trenda

KOMBINOVANJA METAPODATAKA I

datoteka izvornog koda, to je bolje nego da te m etapodatke uvamo u spoljnim dokum entim a. U nete su u Javu zato to neto slino postoji i u drugim jezicima, C#-u, na primer. A notacije su jedna od osnovnih jezikih prom ena u Javi SE5. U njih se stavljaju informacije koje ne m ogu biti izraene Javom, a potrebne su za po tp u n o opisivanje program a. Dakle, anotacije om oguuju skladitenje dod atn ih inform acija o program u u form atu koji ispituje i proverava prevodilac. Anotacije se m ogu upotrebiti za generisanje opisnih datoteka ili ak definicija novih klasa, im e olakavaju teret pisanja ,,ablonskog koda. Pom ou anotacija, te m etapodatke m oete zadrati u Javinom izvornom kodu, i uz to im ate istiji kod, m ogunost provere tipova u vrem e prevoenja i pom o odgovarajueg API-ja u pravljenju alatki za o brad u sopstvenih anotacija. Iako Java SE5 ima nekoliko unapred defm isanih tipova m etapodataka, po pravilu sam o od vas zavisi kakve anotacije ete dodavati i ta ete s njim a raditi. Sintaksa anotacija je prilino jednostavna i svoi se uglavnom na dodavanje simbola @ u jezik. Java SE5 sadri tri ugraene anotacije opte nam ene, definisane u paketu java.lang: @ O verride pokazuje da definicija neke m etode redefinie m etodu osnovne klase. Prevodilac e generisati greku ukoliko sluajno pogreno napiete ime te m etode ili joj date netaan potpis.2 @ D eprecated, zbog koje prevodilac generie upozorenje ukoliko taj elem ent bude upotrebljen. @ SuppressW arnings, za iskljuivanje neprikladnih upozorenja prevodioca. U ranijim izdanjim a Jave SE5 dozvoljavala se ova anotacija, ali nije bila podravana (nego ignorisana). Za pravljenje novih anotacija slue etiri druga tipa anotacija koje u opisati u ovom poglavlju. Kad god piete opisne klase ili interfejse s m nogo ponavljanja, taj posao moete da autom atizujete i pojednostavite pom ou anotacija. Prim era radi, dobar deo dodatnog posla u Enterprise JavaBeans, EJBs, vie se ne m ora raditi otkad u EJB3.0 postoje anotacije. A notacije m ogu da zam ene postojee sisteme kao to je X D oet alatka nezavisnog proizvoaa za doclete (videti dodatak na adresi http://M indV iew .net/B ooks/B etterJava) projektovana ba za pravljenje docleta u stilu anotacija. Za razliku od docleta, anotacije su

le re m v M e y e r je d o a o n C rc ste d B u ttc i dve s e d m ic e ra d io sa m n o m n a o v o m p o g la v lju . N jegova p o m o je n e p ro c e n jiv a . N e m a s u m n je d a je n a d a h n u e za o v o b ila sli n a m o g u n o s t je z ik a C #. U C # -u o n a je re z e rv isa n a re iju u p o tr c b u n a m e e p re v o d ila c .a u Javi je a n o ta c ija . D ru g im re im a , k a a n a C # - u re d e fin ie te m e to d u , m o r a te u p o tre b iti re z e rv isa n u re o v errid e, d o k n a Javi u p o tre b a a n o ta c ije ( O verride nije obavezna.

846

Mlsliti na Javi

prave jezike konstrukcije, im aju stru k tu ru , a njihovi tipovi se proveravaju u vrem e prevoenja. Kod je elegantniji i lake se odrava ukoliko su sve inform acije zadrane u izvornom kodu, a ne u kom entarim a. K orienjem i nasleivanjem API-ja i alatki za anotacije, Oi pom ou spoljnih biblioteka za rad s bajtkodom koje ete upoznati u ovom poglavlju, moete obaviti dalekosenu proveru i o brad u izvornog koda i bajtkoda.

Osnovna sintaksa
U donjem prim eru, m etoda p ro b n o Iz v rse n je () im a anotaciju @Test. Sama po sebi ona ne radi nita, ali e prevodilac proveriti im ate li deftniciju anotacije @Test u putanji autom atskog prevoenja i pakovanja (builda). Kao to ete videti u nastavku poglavlja, pom ou refleksije m oete napraviti alatku koja e tu m etodu izvravati.
//: anotacije/MozeSeTestirati.java package anotacije; import net.mindview.atunit.*; public class MozeSeTestirati { public void izvrsi() {

System.out.pri ntln("Izvravam..");

}
@Test void probnoIzvrsenje() { izvrsi(); }

} ///A notirane m etode se ne razlikuju od ostalih m etoda. A notaciju @Test iz prethodnog prim era, moete koristiti u kom binaciji sa svim m odifikatorim a kao to su public ili static ili void. Sintaksiki, anotacije se upotrebljavaju veom a slino m odifikatorim a.

Definisanje anotacije
Sledi definicija gornje anotacije. Videete da definicije anotacija mogo lie na definicije interfejsa. Zapravo, prevodilac od njih pravi datoteke klasa kao od svakog drugog Java interfejsa:
//: net/mi ndview/atunit/Test.java // Oznaka @Test. package net.mindview.atunit; import java.lang.annotation.*; OTarget(ElementType.METHOD) @Retenti on(RetentionPol icy.RUNTIME) public Ointerface Test {} ///:-

Sem simbola definicija oznake @Test lii na prazan interfejs. Definicija anotacije zahteva i m etaanotacijc @Target i @ Retention. @Target definie gde se anotacija moe prim eniti (recimo, ispred m etode ili polja). @ Retention definie da li e anotacija biti dostupna u izvornom kodu (SOURCE), u datotekam a klasa (CLASS) ili u vreme izvravanja (RUNTIME).

Poglavlje 20: Anotacije

847

Anotacije najee sadre elcm enie za zadavanje vrednosti u anotacijama korisnika. Program ili alatka m ogu upotrebljavati te param etre prilikom obrade anotacije korisnika. Elem enti lie na m etode interfejsa, sem to im moete definisati podrazum evane vrednosti. A notaciju bez elem enata, kao to je @Test, nazivam o m arker anotacija. Sledi jednostavna anotacija koja prati sluajeve upotrebe u projektu. Program eri anotiraju svaku m etodu ili skup m etoda koje zadovoljavaju zahteve odreenog sluaja upotrebe. Kad prebroji realizovane sluajeve upotrebe, rukovodilac projekta moe stei uvid u napredovanje projekta, a program eri koji odravaju projekat lako pronalaze sluajeve upotrebe kada treba da auriraju poslovna pravila u sistem u ili da otkriju i otklone greke u njima.
/ / : a n o t a c i j e / S l u c a j l l p o t r e b e . ja v a im p o r t j a v a . l a n g . a n n o t a t i o n . * ; @Target(ElementType.METHOD) @ R e te n tio n ( R e te n tio n P o li c y . RUNTIME) p u b l i c @ in te rfa c e S lu c a jU p o tre b e ( publ i c i n t i d ( ) ; p u b l i c S t r i n g o p i s ( ) d e f a u l t "nema o p i s a " ;

} ///:O bratite panju na to da id i opis lie na deklaracije m etoda. Poto prevodilac proverava tip od id, to je pouzdan nain povezivanja baze podataka za praenje s dokum entom sluaja upotrebe i sa izvornim kodom . Element opis ima podrazum evanu vrednost koju e procesor anotacija upotrebiti ukoliko neka druga vrednost ne bude zaata prilikom anotiranja metode. U sledeoj klasi tri m etode su anotirane kao sluajevi upotrebe:
/ / : a n o ta c ij e /U s lu z n e M e to d e Z a L o z in k e .ja v a im p o r t j a v a . u t i 1 . * ; p u b l i c c la s s UsluzneMetodeZaLozinke { @ SlucajU potrebe(id = 47, o p is = "L o z in k a mora s a d r a t i barem jednu b r o j k u " ) p u b l i c boolean p r o v e r i L o z i n k u ( S t r i n g l o z i n k a ) retu rn (lo z in k a .m a tc h e s ( " \\w * \\d \\w * " )) ;

}
@S1ucajUpotrebe(id = 48) p u b lic S trin g s if r ir a jL o z in k u ( S t r in g lo z in k a ) { r e t u r n new S t r i n g B u i 1d e r ( l o z i n k a ) . r e v e r s e ( ) - t o S t r i n g ( ) ;

}
@S1ucajUpotrebe(id = 49, o p is = "Nova lo z in k a ne moe b i t i je dnaka p re th o d n im ") p u b l i c boolean p ro v e r a N e p o n a v lja n ja L o z in k e ( L i s t < S t r i n g > p re th o d n e Lo z in k e , S t r i n g l o z i n k a ) r e tu rn !p re th o d n e L o z in k e .c o n ta in s (lo z in k a );

} } ///:-

848

Misliti na Javi

V rednosti elem enata anotacije izraene su kao parovi im e-vrednost u zagradam a iza deklaracije anotacije @SlucajUpotrebe. Anotaciji m etode sifrirajLozinku( ) ovde nije bila prosleena vrednost za elem ent opis, pa e se podrazum evana vrednost definisana u @interface SlucajUpotrebe pojaviti kada ta klasa prode kroz procesor anotacija. Ovakav sistem bi m ogao ,,skicirati stru k tu ru vaeg sistema, s tim to bi m u trebalo dodati jo neke funkcije.

Metaanotacije
U jeziku Java tren u tn o postoje sam o tri (prethodno opisane) standardne anotacije i etiri definisane m etaanotacije. Ovo su m etaanotacije za anotiranje anotacija:
Target

Gde se anotacija moe primeniti. Mogui E!ementType argumentijesu: CONSTRUCTOR: deklaracija konstruktora FIELD : deklaracija p o p (ukjjuujui i enum konstante) LO CA L VARIABLE: deklaracija lokalnih promenljivih M ETHOD: deklaracija metoda PACKAGE: deklaracija paketa PARAMETER: deklaracija parametara TYPE: deklaracija klasa, interfejsa (ukljuujui i tip anotacije) ili nabrojanih tipova Koliko dugo se uvaju informacije anotacije. Mogui RetentionPolicy argumenti jesu: SO URCE: anotaciju prevodilac izbacuje. CLASS. prevodilac ostavlja anotaciju u datoteci klase, ali je VM moe izbaciti. RUNTIME VM ostavlja anotaciju u vreme izvravanja. pa ona moe biti proitana pomou refleksije. Uvrsti anotaciju u Javadoc dokumente. Dozvoli potklasama nasledivanje roditeljske anotacije

Retention

@Documented Inherited

Uglavnom ete sami definisati svoje anotacije i pisati sopstvene procesore koji e ih obraivati.

Pisanje procesora anotacija


Bez alatki koje e ih itati, anotacije teko da su korisnije od kom entara. Vaan deo postupka korienja anotacija jeste pisanje i korienje procesora anotacija. Kao pom o za pravljenje tih alatki, u Javi SE5 koriste se proirenja API-ja za refleksiju. Postoji i spoljna alatka ap t za analizu (ralanjivanje) Java izvornog koda sa anotacijam a. Sledi veom a jednostavan procesor anotacija koji ita anotiranu klasu U sluzneM etodeZaLozinke i refleksijom trai oznake @ SlucajU potrebe. Za datu listu id vrednosti, ispisae pronaene sluajeve upotrebe i prijaviti one koji nedostaju:
/ / : a n o t a c ij e / P r a c e n je S lu c a je v a U p o t r e b e . ja v a iraport j a v a . l a n g . r e f l e c t . * ; im p o r t j a v a . u t i l . * ;

Poglavlje 20: Anotacije

849

p u b l i c c la s s P ra ce n je S lu c a je v a llp o tre b e { p u b l i c s t a t i c v o i p r a t i S l u c a j e v e U p o t r e b e ( L i s t < I n t e g e r > s l u c a je v iU p o t r e b e , Class<?> c l ) f o r(M e th o d m : c l.g e t D e c la r e d M e t h o d s ( ) ) { Slu c a jU p o tre b e su = m . g e t A n n o t a t i o n ( S l u c a j U p o t r e b e . c l a s s ) ; i f ( s u != n u l l ) { S y s t e m . o u t . p r in t ln ( ''P r o n a e n s l u a j u p o tr e b e : " + s u . i d ( ) + " " + s u .o p is O ); s lu c a je v iU p o tre b e .re m o v e (n e w I n t e g e r ( s u . i d ( ) ) ) ; {

} }
f o r ( i n t i : s l u c a je v iU p o t r e b e ) { S y s t e m . o u t . p r i n t l n ( " U p o z o r e n j e : n e d o s ta je s l u a j u p o tr e b e - " + i ) ;

} }
p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] a rg s) { L i s t < I n t e g e r > s lu c a je v iU p o t r e b e = new A r r a y L i s t < I n t e g e r > ( ) ; C o l l e c t i o n s . a d d A l l ( s l u c a j e v i U p o t r e b e , 47, 48, 49, 5 0 ); p ra t iS 1 u c a je v e U p o t re b e ( s l u c a je v iU p o t re b e , Usl uzneMetodeZa L o z in k e .c la s s );

}
} / * Is p is : Pronaen s l u a j upo tre b e :47 Pronaden s l u a j u p o tr e b e :4 8 Pronaen s l u a j u p o tr e b e :4 9 Upozore nje : n e d o sta je s l u a j Lozinka mora s a d r a t i barem jednu b r o jk u nema opisa Nova l o z i n k a ne moe b i t i je dnaka p re thodnim u p otrebe-5 0

* ///:U p retho dn om program u koriste se reflektivna m etoda getD ecIaredM ethods( ) i metoda g etA nnotation( ) koja potie iz interfejsa AnnotatedElem ent (koji realizuju klase kao to su Class, M ethod i Field). Ta m etoda vraa objekat anotacije specificiranog tipa, u ovom sluaju tipa SlucajUpotrebe. Ukoliko nem a anotacija tog odredenog tipa za anotiranu m etodu, vraa se null. Vrednosti elem enata se saznaju kada se pozovu m etode i d ( ) i o p is ( ). Ne zaboravite da u anotaciji m etode sifrirajL ozinku( ) nije bilo opisa, pa gornji procesor pronalazi podrazum evanu vrednost nema opisa kada za tu anotaciju pozove m etodu o p is ( ).

Elementi anotacija
O znaka @SlucajUpotrebe definisana u program u SlucajUpotrebe.java sadri int eleinent id i String element opis. Ovo su dozvoljeni tipovi elem enata anotacija: Svi prosti tipovi (int, float, boolean itd.)

String Class
N abrojani tipovi

Anotacije
Nizovi svih prethodnih tipova

850

Misliti na Javi

Prevodilac e prijaviti greku ako pokuate da upotrebite neki rugi tip. Vodite rauna 0 tom e da ne smete koristiti om otake klase, ali zbog autom atskog pakovanja to zapravo 1nije ogranienje. Dozvoljeni su i elem enti koji su sami anotacije. Kao to ete videti malo kasnije, ugnedene anotacije m ogu veom a dobro da poslue.

Ogranienja podrazumevanih vrednosti


Prevodilac je veom a izbirljiv kada se radi o podrazum evanim vrednostim a elemenata. V rednost svakog elem enta m ora biti specificirana. To znai da elementi m oraju imati podrazum evane vrednosti ili vrednosti zadate u klasi koja upotrebljava anotaciju. Postoji jo jedno ogranienje: elem enti neprostih tipova ne smeju poprim iti vrednost null. To vai i za deklaracije u izvornom kodu i za definicije podrazum evanih vrednosti u interfejsu anotacije. Ovo oteava pisanje procesora koji deluje na osnovu postojanja ili nepostojanja odreenog elem enta, poto je svaki elem ent efektivno prisutan u svakoj deklaraciji anotacije. Prevazii ete ovu potekou tako to ete traiti specifine vrednosti, kao to su prazni znakovni nizovi ili negativne vrednosti:
/ / : a n o t a c i j e / S i m u l a c i j a N u l 1 . ja v a im p o rt j a v a . 1a n g .a n n o t a t io n @Target(E1ementType.METHOD) @Retenti o n ( R e t e n t io n P o li c y . RUNTIME) p u b l i c @ in te rfa c e S i m u l a c i j a N u l l { p u b lic i n t id ( ) d e fa u lt -1; p u b l i c S t r i n g o p i s () d e f a u l t

) / / / =Ovaj idiom je tipian za definicije anotacija.

Generisanje spoljnih datoteka


Anotacije su naroito korisne u radu sa strukturam a za razvoj koda za koje je, uz izvorni kod korisnika, potrebna i neka vrsta dodatnih inform acija. Tehnologije kao to su (pre EJB3) bila Enterprise zrna Jave, zahtevaju brojne interfejse i deskriptore realizacije, to je ablonski" kod, jednak za svako zrno. Za Web servise, biblioteke nam enskih oznaka i alatke za preslikavanje objekata na relacione baze podataka, kao to su Toplink i Hibernate, esto su potrebni XML deskriptori koji su spoljni u onosu na kod. Nakon definisanja Java klase, program er m ora da istrpi dosadno ponovno specificiranje informacija kao to su ime, paket itd. - a one ve postoje u originalnoj klasi. Kada koristite spoljnu deskriptor datoteku, im ate dva zasebna izvora inform acija o klasi, to obino prouzrokuje problem e sa sinhronizacijom . Zbog toga program eri koji rade na projektu, pored toga kako se pie Java program , m oraju znati i kako da izmene deskriptor. Pretpostavim o da piete osnovne funkcije za preslikavanje objekata na relacione baze podataka; te funkcije autom atizuju pravljenje tabele baze podataka za skiaditenje zrna Jave. Mogli biste upotrebiti XML datoteku deskriptora koja specificira ime klase, njene lanove i inform acije o njenom preslikavanju u bazi podataka. M eutim , pom ou

Poglavlje 20: Anotacije

851

anotacija, sve te inform acije zadrali biste u izvornoj datoteci zrna Jave. Za to bi vam bile potrebne anotacije koje definiu im e tabele baze podataka pridruene tom zrnu, kolone i SQL tipove za preslikavanje svojstava zrna. Sledi anotacija zrna koja procesoru anotacija kazuje da napravi tabelu baze podataka:
/ / : a n o t a c ije / b a z a p o d a t a k a / T a b e la B P . ja v a package a n o t a c ij e . b a z a p o d a t a k a ; im p o r t j a v a . l a n g . a n n o t a t i o n . * ; @Target(ElementType.TYPE) / / Vai samo za kla se @Retenti o n (R e t e n t i onPoli c y . RUNTIME) p u b l i c @ in te rfa c e TabelaBP { p u b l i c S t r i n g im e () d e f a u l t

} ///= Svaki ElementType koji specificirate u anotaciji @Target jeste ogranienje koje prevodiocu kazuje da se ta anotacija moe prim eniti sam o na taj odreeni tip. Za nabrojani tip ElementType m oete specificirati jednu vrednost ili listu proizvoljnih kom binacija vrednosti razdvojenih zarezima. Ako anotaciju elite da prim enite na svaki ElementType, m oete p o tp u n o da izostavite anotaciju @Target, mada to nije uobiajeno. O bratite panju na to da @TabelaBP im a elem ent im e ( ), tako da anotacija moe dati ime tabele baze podataka koju e procesor napraviti. Evo anotacija za polja ovog zrna Jave:
/ / : a n o ta c ij e / b a z a p o d a t a k a / O g r a n ic e n ja . ja v a package a n o ta c ije . b a z a p o d a t a k a ; im p o r t j a v a . l a n g . a n n o t a t i o n . * ; @Target(ElementType.FIELD) @ R e te n tio n(R e te n ti onPoli c y . RUNTIME) p u b l i c @ in te rfa c e O g ra n ic e n ja { boolean p rim a r n iK L J u c () d e f a u l t f a l s e ; boolean d o z v o li N u l1 () d e f a u l t t r u e ; boolean j e d i n s t v e n o ( ) d e f a u l t f a l s e ;

} ///:/ / : a n o ta c ij e / b a z a p o d a t a k a / S Q L S t r in g . ja v a package a n o t a c i j e . bazapodataka; im p o r t j a v a . l a n g . a n n o t a t i o n . * ; @Target(ElementType.FIELD) @Retenti o n ( R e t e n t i o nPoli c y . RUNTIME) p u b l i c @ in te rfa c e SQLString { i n t v r e d n o s t O d e f a u l t 0; S t r i n g ime() d e f a u l t O g ra n ic e n ja o g r a n i c e n j a ( ) d e f a u l t @0granicenja;

) ///:-

852

Misliti na Javi

/ / : a n o t a c ij e / b a z a p o d a t a k a / S Q L I n t e g e r . ja v a package a n o t a c ije . b a z a p o d a t a k a ; im p o r t j a v a . l a n g . a n n o t a t i o n . * ; @Target( E1ementType. FIELD) @ Retention(RetentionPol icy.RUNTIME) p u b l i c P i n t e r f a c e SQLInteger { S t r i n g ime() d e f a u l t O g ra n ic e n ja o g r a n ic e n ja ( ) d e f a u l t @Ogranicenja;

} III-Anotacija @Ogranicenja om oguuje procesoru da izdvoji metapodatke o tabeli baze podataka. Oni predstavljaju tek mali podskup ogranienja koje baze podataka po pravilu nam eu, ali dovoljan da steknete optu sliku. Elementima prim arniK Ijuc( ), dozvoliN ull() i jedinstveno( ) date su smislene podrazum evane vrednosti, tako da korisnik anotacije u veini sluajeva ne m ora m nogo da kuca. Ostala dva interfejsa (@interface) definiu SQL tipove. I opet, da bi ova struktura bila upotrebljivija, treba da definiete anotaciju za svaki dodatni SQL tip. Ovde e dva tipa biti dovoljna. Svaki od tih tipova ima elem ent im e ( ) i elem ent ogranicenja( ). Taj poslednji u ugnedenoj anotaciji sadri inform acije o ogranienjim a baze podataka za tip kolone. O bratite panju na to da je @Ogranicenja podrazum evana vrednost elem enta ogranicenja( ). Poto nakon ovog tipa anotacije nem a u zagradam a specificiranih vrednosti elemenata, podrazum evana vrednost og ranicenja( ) zapravo je anotacija @Ogranicenja sa sopstvenim skupom podrazum evanih vrednosti. Da biste ugnedenoj anotaciji @Ogranicenja sa jedinstvenou postavljenom na podrazum evanu vrednost true, njen elem ent moete definisati ovako:
/ / : a n o ta c ije / b a z a p o d a t a k a / J e d i n s t v e n o s t . ja v a / / Primer ugneenih a n o t a c i j a package a n o ta c ije . b a z a p o d a t a k a ; p u b l i c @ in te rfa c e J e d in s t v e n o s t { O g ra n ic e n ja o g ra n ic e n ja O d e f a u l t @ O gra nic enja (je di n s t v e n o = t r u e ) ;

} ///:Sledi jednostavno zrno u kojem su upotrebljene prethodne anotacije:


/ / : a n o ta c ij e / b a z a p o d a t a k a / C la n . ja v a package a n o ta c ije . b a z a p o d a t a k a ; @TabelaBP(ime = "CLAN") p u b l i c c la s s Clan { @SQLString(30) S t r i n g li c n o l m e ; @SQLString(50) S t r i n g prezlm e; @SQLInteger I n t e g e r s t a r o s t ; @SQLString(value = 30,

Poglav|je 20: Anotacije

853

o g r a n ic e n ja = @ O g r a n ic e n ja ( p r im a r n iK lju c = t r u e ) ) S trin g id e n t if ik a to r ; s t a t i c i n t b ro jC la n o v a ; p u b lic S trin g d a j I d e n t i f i k a t o r ( ) { re tu rn i d e n t i f i k a t o r ; p u b l i c S t r i n g d a jL ic n o Im e ( ) { r e t u r n l i c n o l m e ; ) p u b l i c S t r i n g d a jP re z Im e () { r e t u r n pre z lm e ; } p u b lic S trin g to S trin g O { re tu rn id e n ti f i ka to r; } p u b lic In te ge r d a jS ta ro s t() { re tu rn s ta ro s t; }

} ///= A notaciji @TabelaBP klase data je vrednost CLAN koja e biti upotrebljena kao im e tabele. Svojstva zrna licnolm e i prezlm e an o tirana su pom ou @SQLStringova i imaju vrednosti 30 odnosno 50. Te anotacije su zanimljive iz dva razloga: prvo, koriste podrazum evanu vrednost ugneene anotacije @Ogranicenja, i drugo, koriste krai oblik pisanja. Ako anotacija im a sam o jedan elem ent i vi m u date im e value, ne m orate da piete parove im e-vrednost; dovoljno je da specificirate sam o vrednost (engl. value) u zagradam a. To vai za sve dozvoljene tipove elem enata. N aravno, tada elem entu m orate dati im e vrednost", ali u gornjem sluaju ipak dobijate sem antiki sm islenu specifikaciju anotacije koja se lako ita:
@SQLStri n g (30)

Procesor e tu vrednost upotrebiti za zadavanje irine SQL kolone koju e napraviti. Koliko god da je sintaksa podrazum evanih vrednosti elegantna, ona brzo postaje sloena. Pogledajmo anotaciju iji je deo polje identifikator. To je anotacija @SQLString koja m ora biti i prim arni klju baze podataka, pa tip elem enta prim arniK ljuc m ora biti specificiran u ugneenoj anotaciji @Ogranicenja. Tu stvari postaju zapetljane. Za tu ugneenu anotaciju m orate upotrebiti opirn u sintaksu im e-vrednost, u kojoj ponovo specificirate ime elem enta i ime interfejsa (@interface). Ali poto posebno imenovani elem ent value vie nije jedini elem ent ija se vrednost zaaje, ne m oem o upotrebiti krai oblik sintakse. Kao to vidite, rezultat ne izgleda ba lepo.

Drugaija reenja
U ovom sluaju im a i drugih naina da se naprave anotacije. Na primer, m oem o napraviti samo jednu klasu anotacije @KolotiaTabele u ijem enum elem entu definiemo vrednosti kao to su STRING, INTEGER, FLOAT itd. O nda ne bism o m orali imati interfejs (@interface) za svaki SQL tip, ali bism o izgubili m ogunost da kvalifikujemo tipove pom ou dodatnih elemenata kao to su velicina ili preciznost , pa je teta verovatno vea od koristi. Stvarni SQL tip m oem o opisati String elem entom , npr. VARCHAR(30) ili INTEGER. Time ne bism o izgubili m ogunost da kvalifikujem o tipove, ali bi preslikavanje }ava tipa u SQL, tip bilo zakljuano u kodu, a to nije dobro. Ne elimo da zbog prom ene baze podataka m oram o ponovo da prevodim o klase; bilo bi elegantnije saoptiti procesoru anotacija da koristim o drugu varijantu" SQL-a i prepustiti njem u da tokom obrade anotacija sam o tom e vodi brigu.

854

Misliti na Javi

Tree reenje bilo bi korienje dva tipa anotacija, @ Ogranicenja i relevantnog SQL tipa (recimo, @SQLInteger) za anotiranje eljenog polja. To jeste malo zapetljano, ali prevodilac dozvoljava proizvoljan broj razliitih anotacija za svako odredite (engl. target). Kada koristite vie anotacija, istu anotaciju ne sm ete u potrebiti dvaput.

Anotacije ne podravaju nasleivanje


Rezervisanu re extends ne smete koristiti uz @interface. To je teta, poto bi elegantno reenje bilo da definiemo anotaciju @KolonaTabeIe (kao to sam predloio gore) uz ugneenu anotaciju tipa @SQLTip. Tako bism o od klase @SQLTip mogli da nasledim o sve SQL tipove, kao to su @SQLInteger i @SQLString. Sintaksa bi bila elegantnija, a m anje bi se kucalo. Nema nagovetaja da e u buduim izdanjim a anotacije podrati nasleivanje, pa izgleda kao da su gornji prim eri ono najvie to se m oe uraditi u datim okolnostima.

Realizacija procesora
Sledi prim er procesora anotacija koji uitava datoteku klase, trai u njoj anotacije baze podataka i generie SQL kom andu za pravljenje takve baze podataka:
/ / : a n o ta c ije / b a z a p o d a t a k a / T v o r a c T a b e le . ja v a / / R e f l e k t i v n i p ro ce so r a n o t a c i j a . / / {Arg um e n ti: a n o ta c ije . b a z a p o d a t a k a . C la n } package a n o t a c ije . b a z a p o d a t a k a ; im p ort j a v a . l a n g . a n n o t a t i o n . * ; im p o rt j a v a . 1a n g . r e f l e c t . * ; im p o rt j a v a . u t i l . * ; p u b l i c c la s s TvoracTabele { p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] args) throvvs E xc e p tio n { i f ( a r g s . 1ength < 1) { S y s te m .o u t.p r in tln ( " a rg u m e n ti: a n o tirane k la s e " ); System.exi t ( 0 ) ;

}
f o r ( S t r i n g imeKlase : a rg s) { Class<?> cl = C la s s .fo rN a m e (im e K la s e ); TabelaBP tabelaBP = c l . g e t A n n o t a t i o n ( T a b e l a B P . c l a s s ) ; i f (tabelaBP == n u l l ) { S y s te m .o u t.p rin tln ( "U k l a s i " + imeKlase + " nema a n o t a c i j a T abela BP "); c o n t i nue;

}
S t r i n g imeTabele = t a b e l a B P . i m e ( ) ; / / Ako j e ime prazno, u p o tr e b i ime Cla ss: i f ( i m e T a b e l e . l e n g t h ( ) < 1) imeTabele = c l .getName() .t o llp p e r C a s e ( ) ; L i s t < S t r i n g > defKolona = new A r r a y L i s t < S t r i n g > ( ) ;

Poglavlje 20: Anotacije

855

f o r ( F ie l d p o lje : cl .g e tO e c la re d F ie ld s O ) { S t r i n g imeKolone = n u l l ; A n n o t a t i o n [ ] anot = p o l j e . g e t D e c l a r e d A n n o t a t i o n s ( ) ; i f ( a n o t . l e n g t h < 1) c o n t in u e ; / / N i j e kolona t a b e l e baze podataka i f ( a n o t [ 0 ] i n s t a n c e o f SQLInteger) { SQLInteger s l n t = (SQLInteger) a n o t [ 0 ] ; / / Ako ime n i j e s p e c i f i c i r a n o , u p o t r e b i ime p o l j a i f ( s l n t . i m e ( ) . l e n g t h ( ) < 1) imeKolone = p o l j e . g e t N a m e ( ) , t o U p p e r C a s e ( ) ; e ls e imeKolone = s l n t . i m e ( ) ; d e fKolo na.a dd(im eK olone + " INT" + d a jO g ra n ic e n ja (s In t.o g ra n ic e n ja () ) ) ;

}
i f ( a n o t [ 0 ] in s t a n c e o f SQLString) { SQLString s S t r i n g = (SQLString) a n o t [ 0 ] ; / / Ako ime n i j e s p e c i f i c i r a n o , u p o tr e b i ime p o l j a . i f ( s S t r i n g . i me( ) . l e n g t h ( ) < 1) imeKolone = p o l j e . g e t N a m e ( ) . t o U p p e r C a s e ( ) ; el se imeKolone = s S t r i n g . i m e ( ) ; d e fKolo na.a dd(im eK olone + " VARCHAR(" + s S trin g .v re d n o s tO + " ) " + daj Ograni cenj a ( s S t r i ng. o g ra n i cenj a ( ) ) ) ;

}
S t r i n g B u i l d e r komandaZaPravljenje = new S t r i n g B u i l d e r ( "NAPRAVI TABELU 1 1 + imeTabele + " ( " ) ; f o r ( S t r i n g defKolone : defKolona) k om an d a Za P ra v lje n je .a p p en d ("\n " + defKolo ne + " , " ) ; / / Uklo ni p r a t e i zarez S t r i n g na p ra viT a be lu = k o m a n d a Z a P ra v lje n je . s u b s t rin g ( 0, k o m a n d a Z a P ra v lje n je . le n g t h f ) - 1) + " ) ; " ; S y s te m .o u t. p r i n t l n ( " S Q L za p r a v l j e n j e t a b e l e za kla su " + imeKlase + " g l a s i : \ n " + n a p r a v i T a b e l u ) ;

p r i v a t e s t a t i c S t r i n g d a jO g r a n ic e n ja ( O g r a n ic e n ja og r) S t r i n g o g ra n ic e n ja = i f ( lo g r.d o z v o liN u l1()) o g r a n ic e n ja += " NIJE NULL"; i f ( o g r . p r i m a r n i K L J u c ()) o g r a n ic e n ja += " PRIMARNI KLJUC"; i f ( o g r . je d in s tv e n o O ) o g r a n ic e n ja += " JEDINSTVENO"; r e t u r n o g r a n ic e n ja ;

856

Misliti na Javi

} /* Isp is:
SQL za p r a v l j e n j e t a b e l e za k la su a n o ta c ije . b a z a p o d a t a k a . C la n NAPRAVI TABELU CLAN( LICNOIME VARCHAR(30)) ; SQL za p r a v l j e n j e t a b e l e za k la s u a n o ta c ije . b a z a p o d a t a k a . C la n NAPRAVI TABELU CLAN( LICNOIME VARCHAR(30), PREZIME VARCHAR(50)) ; SQL za p r a v l j e n j e t a b e l e za k la su a n o ta c ije . b a z a p o d a t a k a . C la n NAPRAVI TABELU CLAN( LICNOIME VARCHAR(30), PREZIME VARCHAR(50), STAROST I N T ) ; SQL za p r a v l j e n j e t a b e l e za kla su a n o ta c ije . b a z a p o d a t a k a . C la n NAPRAVI TABELU CLAN( LICNOIME VARCHAR(30), PREZIME VARCHAR(50), STAROST INT, IDENTIFIKATOR VARCHAR(30) PRIMARNI KLJUC); g la s i:

g la s i:

g la s i:

g la s i:

* ///:M etoda m a in ( ) prolazi kroza sva im ena klasa na kom andnoj liniji. Svaku klasu redom uitava m etoda forN am e( ) i naredbom getAnnotation(TabelaBP.class) proverava im a li anotaciju @TabelaBP. Ukoliko ima, ime te tabele biva pronaeno i uskladiteno. Zatim se sva polja te klase uitavaju i proveravaju m etodom getD eclaredA nnotations( ). O na vraa niz svih anotacija definisanih za datu m etodu. O peratorom instanceof utvrduje se da li su te an o ta je tipova @SQLlnteger odnosno @SQLString, i u svakom od tih sluajeva pravi se relevantni String odlom ak sa im enom kolone tabele. Poto se interfejsi anotacija ne m ogu nasleivati, korienje m etode getD ecIaredA nnotations( ) jedini je nain da se postigne priblino polim orfno ponaanje. Ugneenu anotaciju @Ogranicenja prosleujem o m etodi dajO granicenja( ) koja gradi String to sadri ta SQL ogranienja. Vredi spom enuti da je goreopisana tehnika pom alo naivan nain definisanja preslikavanja objekata na relacione baze podataka. Ako prom enite im e tabele, m oraete iznova da prevodite Java kod, poto anotacija tipa @TabelaBP prim a ime tabele kao param etar. To nije naroito poeljno ponaanje. Ve je m nogo dostupnih struktura (engl. fram ew ork) za preslikavanje objekata na relacione baze podataka, a u sve vie njih koriste se anotacije.

Veba 1: (2) Realizujte vie SQL tipova u prim eru s bazom podataka. Projekat: Izm enite prim er s bazom podataka tako da se pom ou JDBC-a povezuje i radi s pravom bazom podataka. Projekat: Izm enite prim er s bazom podataka tako da um esto pisanja SQL koda pravi odgovarajue XML datoteke.

P ro je k ti su p re d lo z i k o ji se m o g u k o ris titi (re c im o ) za s e m in a rsk e ra d o v e . V odi s re e n jim a n e sad r i re e n ja p ro je k a ta .

Poglavlje 20: Anotac|je

857

apt za obradu anotacija


Alatka za obradu anotacija apt (engl. an n otation processing tool ) prva je Sunova verzija alatke za pom o u obradi anotacija. Poto prestavlja prvi pokuaj, malo je prim itivna, ali ipak m oe da poslui. Kao i javac, apt obrauje izvorne datoteke, a ne prevedene Java datoteke. Kada zavri obradu izvornih datoteka, apt ih podrazum evano prevodi. To je korisno ako autom atski pravite nove izvorne datoteke u sldopu postupka prevoenja i pakovanja (build). U stvari, apt u istom prolazu trai anotacije u novonapravljenim izvornim datotekam a i prevodi ih. Kada procesor anotacija napravi novu izvornu datoteku, anotacije u njoj trai u sledeoj rundi obrade (kako se to naziva u dokumentaciji). Alatka nastavlja obradu rundu za rundom dok ne prestane pravljenje novih izvornih datoteka. Zatim prevodi sve izvorne datoteke. Za svaku anotaciju koju napiete potreban je sopstveni procesor, ali alatka apt m oe da grupie vie procesora. Njoj m oete zadati vie kJasa za obradu, to je m nogo lake nego da sam i iterirate kroz klase File. Moete dodati i oslukivae koji e vas obavestiti kada se svaka ru n d a obrade anotacija zavri. U vrem e pisanja ove knjige, apt nije dostupan kao Ant zadatak (videti dodatak na adresi http://M indV iew .net/B ooks/B ettcrJava). Dok ne postane dostupan, oigledno je da se m oe pokrenuti iz Anta kao spoljni zadatak. Da biste preveli procesore anotacija iz ovog odeljka, u svojoj putanji klasa m orate imati tools.jar; ta biblioteka sadri i interfeise

com.sun.mirror.*. apt radi tako to za svaku anotaciju koju pronae upotrebi AnnotationProcessorFactory za pravljenje odgovarajue vrste procesora anotacija. Kada pokreete apt, zadajte proizvoaku klasu ili putanju klasa gde apt moe nai proizvoake ldase koje su m u potrebne. Ako to ne uradite, apt e se otisnuti na tajnoviti postupak otkrivanja, ije pojedinosti m oete nai u odeljku D evelopingan A n n o ta tio n Processor (Razvojprocesora an o tacija) Sunove dokum entacije. Javinu sposobnost refleksije ne moete koristiti u procesoru anotacija za rad sa alatkom apt, poto radite sa izvornim kodom a ne s prevedenim klasama.4 API m irro r reava taj problem tako to om oguuje da vidite metode, polja i tipove u neprevedenom izvornom kodu. Sledeu anotaciju m oete upotrebiti da izdvojite javne m etode iz klase i pretvorite ih u interfejs:
/ / : a n o ta c ije /Iz d v o jiIn te rfe js .ja v a / / Obrada a n o t a c i j a pomou APT-a. package a n o t a c i j e ; im p o r t j a v a . l a n g . a n n o t a t i o n . * ; @Target(E1ementType.TYPE) @Retenti o n (R e t e n t io n P o li c y . SOURCE) p u b l i c @ in t e rf a c e I z d v o j i I n t e r f e j s { p u b lic S trin g v re d n o st();

) ///:1 M e u tim , n e s ta n d a r d n a o p c ija X a s s e s A s I ) e d s o m o g u u je d a ra d ite sa a n o ta c ija m a koje se n alaze u p re v e d n im k la sa m a . P r o je k ta n ti Jave tim e s u g e ri u d a reflek siju (o d ra z ) n a la z im o u o g le d a lu (e n g l. mirror).

858

Misliti na Javi

M etaanotacija R etentionP olicy im a vrenost SOURCE zato to ovu anotaciju nem a smisla uvati u atoteci klase nakon to iz nje izdvojimo interfejs. Sledea klasa ima javnu m etodu koja m oe postati deo korisnog interfejsa:
/ / : a n o ta c ij e / M n o z a c .j a v a / / Obrada a n o t a c i j a pomou APT-a. package a n o t a c i j e ; @ Iz d v o jiIn te rfe js ("IfM n o z a c a ") p u b l i c c la s s Mnozac { p u b l i c i n t p o m n o z i( in t x , i n t y ) i n t ukupno = 0; f o r ( i n t i = 0 ; i < x ; i++) ukupno = add(ukupno, y ) ; r e t u r n ukupno;

}
p r i v a t e i n t a d d ( i n t x , i n t y) { r e t u r n x + y ; } p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] a rg s) { Mnozac m = new M nozac(); S y s t e m . o u t . p r i n t l n ( " l l * 1 6 = " + m.pomnozi (11, 1 6 ) ) ;

}
} / * Is p is : 11*16 = 176

* ///:Klasa M nozac (koja radi sam o s pozitivnim celim brojevim a) im a m etodu p o m n o z i() koja vie p u ta poziva privatnu m etodu a d d ( ) da bi obavila m noenje. M etoda add( ) nije javna, te stoga nije ni deo interfejsa. A notaciji je data vrednost IfM nozaca, to je ime interfejsa koji treba napraviti. Sada nam treba procesor za izdvajanje:
/ / : a n o ta c ije /P ro c e s o rZ a lz d v a ja n je ln te rfe js a .ja v a / / Obrada a n o t a c i j a pomou APT-a. / / {Exec: a p t - f a c t o r y / / a n o t a c i j e . P r o i z v o a P r o c e s o r a Z a lz d v a ja n je ln t e r f e js a / / Mnozac.java -s . . / a n o t a c i j e } package a n o t a c i j e ; im p o r t com.sun.m ir r o r . a p t . * ; im p o r t c o m . s u n . m i r r o r . d e c l a r a t i o n . * ; im p o r t j a v a . i o . * ; im p o r t j a v a . u t i 1 . * ; p u b l i c c la s s P r o c e s o r Z a l z d v a j a n j e l n t e r f e j s a implements A n n o ta tio n P ro c e s s o r { p r i v a t e f i n a l An n o ta tio n P ro c e ss o rE n vir o n m e n t env; p r i v a t e A r r a y L is t < M e th o d D e c la r a t io n > m e t o d e l n t e r f e j s a = new A r r a y L i s t < M e t h o d D e c l a r a t i o n > ( ) ; p u b lic P ro c e s o rZ a Iz d v a ja n je In te rfe js a ( A n n o ta tio nP ro ce ss o rE n vir on m e n t env) { t h i s . e n v = env; }

Poglavlje 20: Anotacije

859

p u b l i c v o id p ro c e s s ( ) { f o r ( T y p e D e c l a r a t i o n d e k lT ip a : e n v . g e t S p e c if ie d T y p e D e c la r a t io n s O ) I z d v o j i l n t e r f e j s anot =

d e k lT ip a .g e tA n n o ta tio n (Iz d v o jiIn te rfe js .c la s s ); i f ( a n o t == n u l l ) bre ak; f o r ( M e t h o d D e c la r a t io n m : d e k lT ip a . g e t M e t h o d s O ) i f ( m . g e t M o d i f i e r s ( ) . c o n t a in s ( M o d ifie r . P U B L I C ) && ! ( m . g e t M o d i f i e r s O . c o n t a in s ( M o d if ie r .S T A T I C ) )) m eto d e ln te rfe jsa .a d d (m ); i f ( m e t o d e l n t e r f e j s a . s i z e O > 0) { try { P r i n t W r i t e r stampac = e n v . g e t F i 1e r ( ) . c r e a t e S o u r c e F i l e ( a n o t . v r e d n o s t ( ) ) ; s ta m p a c .p rin tln C 'p a k e t " + d e k lT ip a .g e tP a c k a g e O .getQual ifie d N a m e () + " ; " ) ; s ta m p a c .p rin tln ("ja v n i in t e r f e js " + a n o t.v re d n o s t() + " { " ) ; f o r ( M e t h o d D e c la r a t io n m : m e t o d e ln t e r f e js a ) { s ta m p a c .p rin t(" ja v n i " ) ; s ta m p a c . p rin t ( m .g e t R e t u rn T y p e () + " " ) ; s ta m p a c.p ri nt(m .g e tSim p le N a m e() + 1 1 ("); i n t i = 0; f o r ( P a r a m e t e r D e c la r a t io n parm : m .ge tP ara m e te rs ( ) ) { s t a m p a c . p r in t ( p a r m . g e tT y p e ( ) + " " + parm .getS im ple N am eO ); i f ( + + i < m .ge tP ara m e te rs( ) . s i z e ( ) ) stam pac.p r i n t ( " , " ) ;

}
s ta m p a c .p ri n t l n ( " ) ; " ) ;

}
stampac.p r i n t l n ( " } " ) ; s ta m p a c .c lo s e O ; } c a t c h ( IO Exception io e ) { th ro w new R u n tim e E x c e p t io n ( io e ) ;

} ///:Sve se ra d i u m e to d i pro cess( ). P o m o u k lase M ethodDeclaration i n je n e m e to d e getM odifiers( ) id e n tifik u je m o ja v n e m e to d e (i z a n e m a r u je m o o n e k o je su sta ti n e ) o b r a iv a n e klase. U k o lik o ih p r o n a e m o , s k la d i tim o ih u o b je k tu tip a ArrayList i u p o tr e b lja v a m o za p ra v lje n je m e to d a n o v o g in te rfe jsa u d a to te c i .java. O b r a tite p a n ju n a to d a o b je k a t tip a A nnotationProcessorEnvironm ent p ro sle u je m o k o n s t r u k t o r u . T o m o b je k tu m o e te slati u p ite za sve tip o v e (d e fin ic ije k lasa) k o je a la tk a

apt o b r a u je i p o m o u n jeg a m o e te d o b iti o b je k a t tip a Messager i o b je k a t tip a

86 0

Misliti na Javi

Filer. Messager slui za slanje poruka korisniku, npr. greaka koje su nastale tokom obrade i njihovih m esta u izvornom kodu. Filer je neka vrsta PrintW ritera za pravljenje novih datoteka. Umesto obinog PrintW ritera najee se koristi objekat tipa Filer zato to om oguuje alatki apt da prati nove datoteke koje napravite i, ako treba, pronalazi u njim a anotacije i prevodi ih. Videete i da m etoda createSourceFile( ) otvara obian izlazni tok s tanim im enom odgovarajue Java klase ili interfejsa. N em a podrke za pravljenje Javinih jezikih konstrukcija, pa Javin izvorni kod m orate da generiete pom alo prim itivnim m etodam a p r in t( ) i p rin tln ( ). To znai da m orate paziti na to da zatvorite sve zagrade koje ste otvorili i da va kod m ora biti sintaksiki ispravan. M etodu process( ) poziva alatka apt kojoj treba proizvodna m etoda da bi napravila odgovarajui procesor:
/ / : a n o t a c ij e / P r o i z v o d j a c P r o c e s o r a Z a l z d v a j a n j e l n t e r f e j s a . j a v a / / Obrada a n o t a c i j a pomou APT-a. package a n o t a c i j e ; im p o r t c o m . s u n . m i r r o r . a p t . * ; im p o rt c o m . s u n . m i r r o r . d e c l a r a t i o n . * ; im p o r t j a v a . u t i l . * ; p u b l i c c la s s P r o iz v o d j a c P r o c e s o r a Z a l z d v a j a n j e l n t e r f e j s a implements A n n o ta tio n P ro c e s s o r F a c to ry { p u b l i c A n n o ta tio n P ro c e s s o r g e tP ro c e s s o rF o r( Set<AnnotationTypeDec1 a r a t i o n > a t d s , A n n o ta tio n P ro c e s s o r E n v ironment env) { r e t u r n new P r o c e s o r Z a l z d v a j a n j e l n t e r f e j s a ( e n v ) ;

}
p u b l i c C o l l e c t i o n < S t r i n g > s u p p o r te d A n n o ta tio n T y p e s () { return C o lle c tio n s .s in g le to n ("a n o ta c ije .Iz d v o jiIn te rfe js ");

}
p u b l i c C o l l e c t i o n < S t r i n g > s u p p o r te d O p tio n s ( ) r e t u r n C o l1e c t io n s . e m p t y S e t ( ) ; {

} III'-U interfejsu A nnotationProcessorFactory sam o su tri metode. Kao to vidite, procesor pravi m etoda getProcessorFor( ) koja prim a Set (skup) deklaracija tipova (fava klasa koje alatka apt obrauje) i objekat tipa A nnotationProcessorEnvironm ent koji sm o ve proputali kroz procesor. Ostale dve metode, supportedA nnotationTypes( ) i supportedO ptions( ), postoje da biste mogli proveriti imate li procesore za sve anotacije koje je apt pronaao i da li podravate sve opcije specificirane na kom andnoj liniji. Naroito je vana metoda getProcessorFor( ) zato to e vas apt, ako u kolekciji String ne vratite puno ime kJase (tipa) anotacije, upozoriti da nem a relevantnog procesora i izai, a da nita ne uradi. Procesor i proizvodna m etoda su u paketu anotacije, pa je za gornju stru k tu ru direktorijum a kom andna Iinija ugraena u kom entar Exec na poetku program a ProcesorZalzdvajanjelnterfejsa.java. Time se alatki apt kazuje da upotrebi goredefinisanu

Poglav|je 20: Anotacije

861

proizvodnu m etodu i da obradi datoteku Mnozac.java. O pcija -s zadaje da se sve nove datoteke m oraju napraviti u direktorijum u anotacije. Ovako izgleda generisana datoteka IfMnozaca.java, to ste mogli pogoditi proitavi naredbe p rin tln ( ) u gornjem procesoru:
package a n o t a c i j e ; p u b l i c i n t e r f a c e IfMnozaca { p u b l i c i n t pomnozi ( i n t x , i n t y ) ;

I ovu datoteku e prevesti apt, pa e i datoteka IfM nozaca.dass biti u istom direktorijum u.
Veba 2: (3) Program u za izdvajanje interfejsa dodajte podrku za deljenje.

Upotreba obrasca Visitor sa alatkom apt


O brada anotacija um e da postane teka. U gornjem prim eru im am o relativno jednostavan procesor anotacija koji tum ai sam o jednu anotaciju, pa ipak sm o m orali prilino da ga uslonimo. Da se sloenost ne bi preterano poveavala s dodavanjem vie anotacija i vie procesora, API m irror im a klase koje podravaju projektni obrazac Visitor (Posetilac). Visitor je jedan od klasinih projektnih obrazaca iz knjige Design Patterns (Projektni obrasci) koju su napisali G am m a i dr, a opirnije objanjenje nalazi se i u knjizi
T h in kin g in Patterns.

Visitor prolazi kroz struk tu ru podataka ili kolekciju objekata i obavlja oreenu operaciju na svakome od njih. S truktura podataka ne m ora biti ureena, a operacija koju obavljate na svakom objektu m ora biti specifina za njegov tip. Tim e se operacije razdvajaju od sam ih objekata, to znai da m oete dodavati nove operacije, a da ne dodajete m etode u definije klasa. Visitor je podesan za obradu anotacija zato to Java klasu moete zamisliti kao kolekciju objekata tipova kao to su TypeDeclaration, FieldDecIaration, M ethodDeclaration itd. Kada alatku apt upotrebite sa obrascem Visitor; dajete Visitor klasu koja ima m etodu za obradu svakog tipa deklaracije koja e biti poseena. Dakle, moete realizovati odgovarajue ponaanje anotacije za metode, klase, polja itd. Evo nam opet SQL generatora tabela, ali em o ovoga puta upotrebiti proizvodnu m etodu i procesor koji koristi projektni obrazac Visitor.
/ / : a n o t a c i j e / b a z a p o d a t a k a /P r o iz v o d ja c P r o c e s o r a Z a P r a v lj e n je T a b e la . ja v a
/ / Prim er s bazom podataka n a p r a v lje n po p ro je ktn o m obrascu V i s i t o r . / / {Exec: apt - f a c t o r y / / a n o ta c ije . b a z a p o d a t a k a . P r o iz v o d a P ro c e s o r a Z a P r a v lje n je T a b e la / / b a z a p o d a ta k a /C la n .ja v a -s bazapodataka} package a n o ta c ije . b a z a p o d a t a k a ; im p o r t com.sun, m ir r o r . a p t . * ; im p o r t c o m . s u n . m i r r o r . d e c l a r a t i o n . * ; im p o r t c o m . s u n . m i r r o r . u t i 1 . * ; im p o rt j a v a . u t i 1 . * ; im p o r t s t a t i c c o m . s u n . m i r r o r . u t i 1. D e c l a r a t i o n V i s i t o r s . * ;

862

Misliti na Javi

p u b l i c c la s s P ro iz v o d ja c P r o c e s o r a Z a P r a v lje n je T a b e la implements A n n o ta tio n P ro c e s s o r F a c to ry { p u b l i c A n n o ta tio n P ro c e s s o r g e tP ro c e s s o rF o r( Se t< A n n o ta ti onTypeDec1aration> a t d s , Ann o ta tio nP ro c e s s o rE n v ir on m e n t env) { r e t u r n new P r o c e s o r Z a P r a v lje n je T a b e la ( e n v ) ;

}
p u b l i c C o l l e c t i o n < S t r i n g > s u p p o rte d A n n o ta tio n T y p e s () re tu rn A rra y s .a s L is t( " a n o ta c ije .b a z a p o d a ta k a .T a b e la B P " , " a n o t a c ije . b a z a p o d a t a k a . O g r a n ic e n j a " , " a n o t a c ije . b a z a p o d a t a k a . SQLStri n g " , " a n o t a c ije . b a z a p o d a t a k a . S Q L I n t e g e r " ) ; {

}
p u b l i c C o l l e c t i o n < S t r i n g > s u p p o r te d O p tio n s () r e t u r n C o l1e c t i o n s . emptySet( ) ; {

}
p r i v a t e s t a t i c c la s s P ro cesorZ aPravlj e n je T a b e la implements A n n o ta tio n P ro c e s s o r { p r i v a t e f i n a l Annota tio nP ro ce s so rE n vir on m e n t env; p r i v a t e S t r i n g sql = p u b l i c P r o c e s o rZ a P r a v lje n je T a b e la ( Annota tio nP ro c e ss o rE n v ir on m e n t env) { t h i s . e n v = env;

}
p u b l i c v o id p ro c e s s () { f o r ( T y p e D e c la r a t io n d e k lT ip a ; e n v .g e t S p e c if ie d T y p e D e c la r a t i o n s ( ) ) { d e k l Ti p a . a c c e p t ( g e t D e c la r a t i onScanner( new P o s e t i 1a c Z a P r a v l j e n j e T a b e l a ( ) , N0_0P)) ; sql = s q l . s u b s t r i n g ( 0 , sq1 . 1 e n g th ( ) - 1) + " ) ; " ; S y s t e m . o u t . p r in t ln ( " S Q L za p r a v l j e n j e g l a s i ; \ n " + s q l ) ; sql = " " ;

} }
p r i v a t e c la s s P o s e tila c Z a P r a v lj e n je T a b e la extends S i m p l e D e c l a r a t i o n V i s i t o r { p u b l i c v o id v i s i t C l a s s D e c l a r a t i o n ( C 1 a s s D e c la r a t io n d) { TabelaBP tabelaBP = d . g e t A n n o t a t io n ( T a b e la B P . c la s s ) ; if ( t a b e l a B P != n u l l ) { sql += "NAPRAVI TABELU " ; sql += ( t a b e l a B P . i m e ( ) . 1e n g th () < 1) ? d.getS im ple Nam e().toUppe rCase() : t a b e l a B P . im e ( ) ; sql += " ( " ;

} }

Poglavlje 20: Anotacije

863

p u b l i c v o id v i s i t F i e l d D e c l a r a t i o n ( F ie l d D e c l a r a t i o n d) { S t r i n g imeKolone = i f ( d . g e t A n n o t a t io n ( S Q L I n t e g e r . c la s s ) != n u l l ) { SQLInteger s l n t = d . g e t A n n o t a t io n ( S Q L I n t e g e r . c la s s ) ; / / Ako ime n i j e s p e c i f i c i r a n o , u p o tr e b i ime p o l j a . i f ( s l n t . i m e ( ) . l e n g t h ( ) < 1) imeKolone = d .g etS im p le N a m e ().to U p pe rC a s e (); el se imeKolone = s l n t . i m e ( ) ; sql += " \ n " + imeKolone + " INT" + d a jO g ra n ic e n ja (s In t.o g ra n ic e n ja ()) +

}
i f ( d . g e t A n n o t a t i o n ( S Q L S t r i n g . c l a s s ) != n u l l ) { SQLString s S t r in g = d . g e t A n n o t a t io n ( S Q L S tr in g . c la s s ) ; / / Ako ime n i j e s p e c i f i c i r a n o , u p o tr e b i ime p o l j a . i f ( s S t r i n g . i m e ( ) . 1 e n g th () < 1) imeKolone = d.g etS im p le Na m e (). to U p p e r C a s e () ; el se imeKolone = s S t r i n g . i m e ( ) ; sql += " \ n " + imeKolone + " VARCHAR(" + s S trin g .v re d n o s tO + " ) " + d a jO g ra n ic e n ja (s S trin g .o g ra n ic e n ja ()) +

} }
p r i v a t e S t r i n g d a jO g ra n ic e n ja (O g r a n ic e n ja og r) S t r i n g o g ra n ic e n ja = i f ( l o g r . d o z v o l i Nu l1 ( ) ) o g r a n ic e n ja += " NIJE NULL"; i f ( o g r . p r i m a r n i K L J u c()) o g r a n ic e n ja += " PRIMARNI KLJUC"; i f (o g r.je d in s tv e n o O ) o g ra n ic e n ja += " JEDINSTVENO"; } r e t u r n o g ra n ic e n ja ; {

} }

} III-Ispis rezultata je isti kao u prethodnom prim eru TabelaBP. U ovom p rim eru su procesor i posetilac u nutranje klase. O bratite panju na to da metoda p ro cess( ) sam o dodaje klasu posetioca i inicijalizuje SQL znakovni niz. O ba param etra m etode getD eclarationScanner( ) jesu posetioci; prvi biva upotrebljen pre posete svakoj deklaraciji, a drugi posle. O vom procesoru je potreban sam o posetilac od pre posete, pa se NO_OP ne daje kao drugi param etar. To je statino polje interfejsa DeclarationVisitor, to je objekat tipa DeclarationV isitor koji ne radi nita. PosetilacZaPravljenjeTabela nasleduje ldasu Sim pleDeclarationVisitor i redefinie m etode visitC lassD eclaration( ) i visitFieldD ecIaration( ). SimpleDeclarationVisitor

86 4

Misliti na Javi

je adapter koji realizuje sve m etode interfejsa DeclarationVisitor, pa moete da se usredsredite sam o na one koje su vam potrebne. U m etodi visitClassD ecIaration( ) proverava se da li u objektu ClassDeclaration postoji anotacija TabelaBP, i ako postoji, inicijalizuje se prvi deo SQL znakovnog niza za pravljenje. U m etodi visitFieldD eclaration( ) ispituje se da li anotacije polja postoje u deklaraciji polja. Inform acije se izdvajaju kao u prvobitnom prim eru , navedenom u p reth o d n o m delu poglavlja. M oda vam ovo izgleda kao kom plikovaniji nain rada, ali njim e dobijam o skalabil nije reenje. Za sloeniji procesor anotacija, pisanje sam ostalnog procesora na nain prethodnog p rim era ubrzo bi postalo previe kom plikovano.

Veba 3: (2) Program u ProizvodjacProcesoraZaPravljenjeTabela.java dodajte podrku


za vie SQL tipova.

Jedinino testiranje pomou anotacija


Jedinino testiranje (engl. u n it testing) je praksa pravljenja jednog ili vie testova za svaku

m etodu klase, kako bi se pravilno ispitalo da li se delovi klase ispravno ponaaju. Najkorienija alatka za jedinino testiranje u Javi je JUnit; u vrem e pisanja ove knjige, JUnit su aurirali u JU nit verziju 4 da bi se obuhvatile anotacije/' Jedan od glavnih problem a s verzijama JUnita pre anotacija bile su brojne ,,ceremonije neophodne u priprem i i izvravanju JUnit ispitivanja. S vrem enom se to svelo, ali e anotacije testiranje jo pribliiti najjednostavnijem sistemu za jedinino testiranje koji se moe zamisliti". U verzijam a JUnita pre anotacija, m orali sm o da piem o zaset>nu klasu za skladitenje jedininih testova. Uz anotacije, jedinine testove m oem o da ukljuim o u klasu koja se testira i tim e na m inim um svedemo vreme i tru d pri jedininom testiranju. Taj pristup im a i veliku d od atn u prednost to se na taj nain m ogu testirati i privatne metode, jednako lako kao javne. Poto je struktu ra za testiranje u ovom prim eru napravljena pom ou anotacija, nazvao sam je @Unit. Za najjednostavnije testiranje (koje ete verovatno najee koristiti) dovoljno je staviti anotaciju @Test uz m etode koje treba testirati. Ispitne m etode ove strukture im aju opciju da vrate b o o lean koji pokazuje uspeh ili neuspeh ukoliko im se ne zadaju ulazni argum enti. Tim ispitnim m etodam a moete dati proizvoljna imena. Takoe, @Unit ispitnim m etodam a m oete pristupati kako god elite, i privatno. Da biste mogli da koristite @ Unit, treba da uvezete paket net.m in d v iew .atu n it,' odgovarajue m etode i polja oznaite sa @ Unit (o tom e ete vie saznati u narednim prim erim a) i date b u ild sistem u da pokrene @ Unit za dobijenu klasu. Sledi jednostavan primer:
/ / : a n o ta c ije /A tU n itP rim e rl.ja v a package a n o t a c i j e ; im p o r t n e t . m i n d v i e w . a t u n i t . * ; im p o r t n e t . m i n d v i e w . u t i l . * ;

6 7

S p o e tk a s a m p o m i lja o n a to d a n a p i e m b o lji J U n it n a o s n o v u o v d e p rik a z a n o g d iz a jn a. M ed u tim , izg led a d a JU n it4 o b u h v a ta m n o g e o v d e p rik a z a n e id eje, pa m i je lake ovako. Ta b ib lio te k a je d e o m re n o g k o d a ove k n jig e i d o s tu p n a je n a a re si ww w.M indVicw .rwl.

Poglavlje 20: Anotacije

865

p u b l i c c la s s A t U n i t P r i m e r l { p u b l i c S t r i n g metodaJedan() { r e t u r n "Ovo j e metodaJedan";

}
p u b l i c i n t metodaDva() { S y s t e m . o u t . p r i n t l n ( " O v o j e metodaDva"); r e t u r n 2;

}
@Test boolean metodaJedanTest() { r e t u r n m e t o d a Je d a n().eq u a ls ("O v o j e metodaJedan");

}
@Test boolean m2() { r e t u r n metodaDva() == 2 ; } @Test p r i v a t e boolean m3() { r e t u r n t r u e ; } / / P r i k a z u j e r e z u l t a t u s lu a j u g re a ka : @Test boolean neuspesanTest() { r e t u r n f a l s e ; } @Test boolean jo sje danNeuspeh() { r e t u r n f a l s e ; } p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] a rg s ) throws Exce p tio n { OSIzvrenje.kom anda( "ja va n e t.m in d v ie w .a tu n it.A tU n it A tU n itP r im e r l" ) ;

}
} / * Is p is : a n o ta c ije .A tU n itP rim e rl . metodaJedanTest . m2 Ovo j e metodaDva . m3 . neuspesanTest ( f a i l e d ) . josjedanNeuspeh ( f a i l e d ) (5 t e s t s ) > 2 FAILURES < a n o t a c i j e . A t U n i t P r i m e r l : neuspesanTest a n o t a c i j e . A t U n i t P r i m e r l : josjedanNeuspeh

*///-

Klase koje e hiti jedinino testirane (engl. @ U nit tcsted ili utiit tested) m oraju biti sm etene u pakete. A notacija @Test ispred m etoda m etodaJedanTest( ), m 2 ( ), m 3 ( ), neuspesanTest( ) i josjedanN euspeh( ) kazujestruktu ri @Unit da te m etode pokrene kao jedinine testove. S truktura se stara da one ne prim e ulazne argum ente i da vrate boolean ili void. Kada budete sami pisali jedinine testove, sam o utvrdite da li je test uspeo ili ne, tj. da li je vratio true ili false (za m etode koje vraaju boolean). Ako poznajete JUnit, prim etiete i da @Unit daje inform ativnije rezultate - prikazuje se tekui test (onaj koji se trenutno izvrava) pa je njegov rezultat korisniji, a na kraju ispisuje klase i testove koji su prou/rokovali neuspeh.

866

Misliti na Javi

Ukoliko vam to ne odgovara, ispitne m etode ne m orate da ugraujete u svoje klase. Neugraene testove je najlake napraviti nasleivanjem:
/ / : a n o ta c ije /A tU n itS p o ljn iT e s t.ja v a / / P r a v l j e n j e neugraenih t e s t o v a . package a n o t a c i j e ; im p o rt n e t . m i n d v i e w . a t u n i t . * ; im p o rt n e t . m i n d v i e w . u t i l . * ; p u b l i c c la s s A t U n i t S p o l j n i T e s t e xtends A t U n i t P r i m e r l { @Test boolean _metodaJedan() { r e t u r n m e to d a J e d a n ( ) .e q u a ls ( "0 v o j e m eto d a Je d a n");

}
@Test boolean _metodaDva() { r e t u r n metodaDva() == 2 ; } p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] a rg s) thro ws E xc e p tio n { OSIzvrenje.komanda( " ja v a n e t . m i n d v i e w . a t u n i t . A t U n i t A t U n i t S p o l j n i T e s t " ) ;

}
} / * Is p is : a n o ta c ije .A tU n itS p o l jn iT e s t . _metodaJedan . _metodaDva Ovo j e metodaDva OK (2 t e s t s )

* ///:Iz ovog prim era vidi se i vrednost slobodnog davanja im ena (za razliku od obaveznog test na poetku imena svih testova u JU nitu). Ovde je <Test m etodi koja neposredno testira drugu m etodu dato ime te m etode, uz p o tcrtu (_) na poetku. (Ne tvrdim da je to idealno reenje, nego sam o pokazujem jed n u od m ogunosti.) Za pravljenje neugraenih testova m oete upotrebiti i kompoziciju:
/ / : a n o ta c ije /A tU n itK o m p o z ic ija .ja v a / / P r a v l j e n j e neugraenih t e s t o v a . package a n o t a c i j e ; im p o r t n e t . m i n d v i e w . a t u n i t . * ; im p o rt n e t . m i n d v i e w . u t i l . * ; p u b l i c c la s s A t U n it K o m p o z ic ija { A t U n i t P r i m e r l t e s t O b j e k a t = new A t U n i t P r i m e r l ( ) ; @Test boolean _metodaJedan() { return te stO b je k a t.m e to d a J e d a n O . e q u a l s ( 0vo j e m eto d a Je d a n");

}
@Test boolean _metodaDva() { r e t u r n t e s tO b je k a t.m e to d a D v a () == 2;

}
p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] O S I z v r e n je . komanda( a rg s ) thro ws Exce p tio n {

Poglavlje 20: Anotacije

867

" ja v a n e t . m i n d v i e w . a t u n i t . A t U n i t A t U n i t K o m p o z i c i j a " ) ;

)
} / * Is p is : a n o t a c i j e . A t U n i t K o m p o z ic ija . metodaJedan . jn etodaDva Ovo j e metodaDva OK (2 t e s t s )

* ///.Za svaki test pravi se po jedan nov lan testObjekat, poto se po jedan objekat tipa

AtUnitKompozicija pravi za svaki test.


N ema posebnih m etoda za proveru uspenosti testiranja kao u JUnitu, ali drugi oblik m etode @Test om oguuje da vratite tip void (ili boolean, ako i u ovom sluaju elite da vraate tru e ili false). Za testiranje uspenosti m oete koristiti Javine naredbe assert koje treba ukljuiti indikatorom -ea na java kom andnoj liniji, ali ih @Unit ukljuuje autom atski. Kao pokazatelj neuspeha m oete upotrebiti ak i izuzetak. Jedan od ciljeva prilikom projektovanja alatke @Unit bio je da se koliina doatne sintakse svede na najm anju m eru, pa su za prijavljivanje greaka potrebni sam o Javine naredlie assert i izuzeci. Da je test neuspean, pokazuje assert koji vrati negativan rezultat ili izuzetak koji potie iz ispitne m etode, ali @Unit ni u tom sluaju ne prestaje da radi, nego nastavlja ispitivanje dok ne obavi sve testove. Evo prim era:
/ / : a n o ta c ije /A tU n itP rim e r2 .ja v a / / Uz t e s t i r a n j e a la tko m @Test moemo u p o t r e b l j a v a t i / / naredbe a s s e r t i i z u z e t k e . package a n o t a c i j e ; im p o r t j a v a . i o . * ; im p o r t n e t . m i n d v i e w . a t u n i t . * ; im p o r t n e t . m i n d v i e w . u t i l . * ; p u b l i c c la s s A t U n it P r im e r 2 { p u b l i c S t r i n g metodaJedan() { r e t u r n "Ovo j e metodaJedan";

}
p u b l i c i n t metodaDvaO { S y s te m .o u t. p r i n t l n ( Ovo j e metodaDva"); r e t u r n 2;

}
OTest vo id a s s e r t P r i m e r ( ) { a s s e r t metodaJedan( ) . e q u a ls ( " 0 v o j e m eto d a Je d a n");

}
@Test v o id ass ertP rim erN euspehaO { a s s e r t 1 == 2: "Kojeg l i iz n e n a e n ja ! " ;

}
@Test v o id p r i m e r l z u z e t k a ( ) throws I0 E x c e p tio n { new F i l e I n p u t S t r e a m ( " n i j e d a t o t e k a . t x t " ) ; / / Baca

}
OTest boolean a s s e r t l R e t u r n () {

868

Mlsliti na Javi

/ / Provera u spenosti uz poruku: a s s e r t metodaDva() == 2: metodaDva mora d a t i 2 " ; r e t u r n m eto d a Je d a n().eq u a ls ("O vo j e m eto d a Je d a n");

}
p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] a rg s) throws Exceptio n { OSIzvrenje.komanda( " ja v a n e t . m i n d v i e w . a t u n i t . A t l l n i t A t U n i t P r i m e r 2 " ) ;

}
} / * Is p is : a nota cije.A tU n itP rim e r2 . a s s e r t P r im e r . assertPrimerNeuspeha j a v a . l a n g . A s s e r t i o n E r r o r : Kojeg l i (fa ile d )

iz n e n a e n ja !

. p r i m e r lz u z e tk a ja v a . i o . F i l e N o t F o u n d E x c e p t i o n : n i j e d a t o t e k a . t x t (The system cannot f i n d th e f i l e s p e c i f i e d ) (fa ile d ) . a s s e r t lR e t u r n Ovo j e metodaDva (4 t e s t s ) > 2 FAILURES < a n o t a c i j e . A t U n i t P r i m e r 2 : assertP rim erNeuspeha a n o t a c ij e . A t U n i tP r im e r 2 : p r i m e r lz u z e t k a

* ///:U narednom prim eru, jednostavnim neugraenim testovim a ija se uspenost proverava naredbam a assert, ispitujem o klasu java.util.HashSet:
/ / : a n o ta c ij e / H a s h S e t T e s t .j a v a package a n o t a c i j e ; im p o r t j a v a . u t i l . * ; im p o r t n e t . m i n d v i e w . a t u n i t . * ; im p o r t n e t . m i n d v i e w . u t i l . * ; p u b l i c c la s s HashSetTest { HashSet<String> t e s t O b j e k a t = new H a s h S e t < S t r in g > ( ) ; OTest v o id i n i c i j a l i z a c i j a ( ) { assert te stO bjeka t.isE m p ty( ) ;

}
@Test v o id _ c o n t a i n s ( ) { te s tO b je k a t.a d d ("je d a n "); asse rt te s tO b je k a t.c o n ta in s ( " j e d a n " ) ;

}
@Test v o id _remove() { te s tO b je k a t.a d d ("je d a n "); te s tO b je k a t.re m o v e ("je d a n "); assert te s tO b je k a t.is E m p ty ();

}
p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] a rg s) throws Exce p tio n { OSIzvrenje.komanda( " ja v a n e t .m in d v ie w . a t u n i t . A t U n i t H a s h S e tT e s t" ) ;

Poglavjje 20: Anotaclje

869

}
} / * Is p is : a n o ta c ije .H a s h S e tT e s t . in ic ija liz a c ija . _remove . _ c o n ta in s OK (3 t e s t s )

* ///:Ukoliko nem a rugih ogranienja, izgleda da je nain s nasleivanjem jednostavniji.

Veba 4: (3) Proverite da li se nov testObjekat pravi pre svakog testa. Veba 5: (1) Izmenite gornji p rim er korienjem p ristupa s nasleivanjem . Veba 6: (1) Testirajte LinkedList na nain prikazan u program u HashSetTest.java. Veba 7: ( 1) Izmenite prethodni prim er korienjem pristupa s nasleivanjem. Za svako jedinino testiranje, @Unit podrazum evanim k o n stru k to ro m pravi objekat
klase koju testira. Poziva se test za taj objekat, koji se zatim odbacuje da bi se izbegli uticaji na druga jedinina testiranja. Za pravljenje objekata koristi se podrazum evani konstruktor. Ukoliko nem ate podrazum evani konstruktor ili vam je po treb n a sofisticiranija konstrukcija objekata, napravite statinu m etodu za graenje objekata i prikaite za nju anotaciju @TestObjectCreate, na ovaj nain:
/ / : a n o ta c ije /A tU n itP rim e r3 .ja v a package a n o t a c i j e ; im p o r t n e t . m i n d v i e w . a t u n i t . * ; im p o rt n e t . m i n d v i e w . u t i 1 . * ; p u b l i c c la s s A tU n itP rim e r3 { p r i v a t e i n t n; p u b l i c A t U n i t P r i m e r 3 ( i n t n) { t h i s . n = n; } p u b l i c i n t getN() { r e t u r n n; } p u b l i c S t r i n g metodaJedan() { r e t u r n "Ovo j e metodaJedan";

}
p u b l i c i n t metodaDva() { S y s t e m . o u t . p r i n t l n("Ovo j e metodaDva"); r e t u r n 2;

}
@TestObjectCreate s t a t i c A tU n it P rim e r3 n a p r a v i () r e t u r n new A t U n i t P r i m e r 3 ( 4 7 ) ; {

}
@Test boolean i n i c i j a l i z a c i j a ( ) { r e t u r n n == 47; } @Test boolean metodaJedanTest() { r e t u r n m etodaJedan(). equals("Ovo j e m etodaJedan");

}
@Test boolean m2() { r e t u r n metodaDva() == 2; } p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] a rg s) throws E x ce p tio n {

870

Misliti na Javi

OSI zvrenj e . komanda( ja v a n e t . m i n d v i e w . a t u n i t . A t l l n i t A t U n i t P r i m e r 3 ' ' ) ;

}
} / * Is p is : a n o ta c ije .A tU n itP rim e r3 . in ic ija liz a c ija . metodaJedanTest . m2 Ovo j e metodaDva OK (3 t e s t s )

* ///:M etoda @TestObjectCreate m ora biti statina i vratiti objekat tipa koji testirate - program @Unit e se postarati da to bude ispunjeno. Katkada e vam zatrebati dodatna polja za podrku jedininom testiranju. A notaciju @TestProperty moete upotrebiti za oznaavanje polja koja se upotrebljavaju sam o za jedinino testiranje (da biste mogli da ih uklonite pre isporuke program a klijentu). U narednom p rim eru itam o vrednosti iz znakovnog niza ralanjenog m etodom S tring.split( ). Taj ulaz se upotrebljava za proizvodnju test objekata:
/ / : a n o t a c i j e / A t U n i t P r i m e r 4 . ja v a package a n o t a c i j e ; im p o r t j a v a . u t i l . * ; im p o r t n e t . m i n d v i e w . a t u n i t . * ; im p o r t n e t . m i n d v i e w . u t i l . * ; im p o r t s t a t i c n e t . m i n d v i e w . u t i l . P r i n t . * ; p u b l i c c la s s A tU n itP rim e r4 { s t a t i c S t r i n g t e o r i j a = " A l l b ro n to sa u ru s e s " + " a r e t h i n a t one end, much M UCH t h i c k e r i n th e " + " m id d le , and then t h i n again a t the f a r e n d ." ; p r i v a t e S trin g rec; / / I n i c i j a l i z a c i j a g e n e r a to ra s l u a j n i h b ro je v a k o ja z a v i s i od vremena p r i v a t e Random s lu c a ja n = new Random(); p u b l i c A t U n i t P r i m e r 4 ( S t r i n g re c ) { t h i s . r e c = r e c ; } p u b l i c S t r i n g dajR ec() { r e t u r n r e c ; } p u b l i c S t r i n g is p r e t u r a j R e c ( ) { L i s t< C h ara c te r> znakovi = new A r r a y L i s t < C h a r a c t e r > ( ) ; fo r(C h a ra cte r z : rec.toC harA rray( ) ) z n a k o v i.a d d (z ); C o l1e c t i o n s . s h u f f l e ( z n a k o v i , s l u c a j a n ) ; S t r i n g B u i 1der r e z u l t a t = new S t r i n g B u i l d e r ( ) ; f o r ( c h a r zn : zna k o vi) re z u lta t.a p p e n d (z n ); retu rn re z u lt a t . t o S tr in g ( );

}
@TestProperty s t a t i c L i s t < S t r i n g > u la z = A rra y s .a s L is t(te o rija .s p lit(" " ) ) ; @TestProperty s t a t ic Ite ra to r< S trin g > reci = u l az. i t e r a t o r ( ) ;

Poglavlje 20: Anotacije

871

@TestObjectCreate s t a t i c A tU n it P rim e r4 n a p r a v i( ) if(re c i,h a s N e x t()) r e t u r n new A t U n i t P r i m e r 4 ( r e c i . n e x t ( ) ) ; el se retu rn n u l l ;

}
@Test boolean r e c i ( ) ( p r i n t ( ..... + d a jR ec() + ..... ) ; retu rn d a jR e c ().e q u a ls ("a re ");

}
@Test boolean i s p r e t u r a j l ( ) ( / / Prelazimo na odreeno seme (g e n e r a to r a s l u a j n i h / / b r o je v a ) da bismo m ogli da proveravamo r e z u l t a t e : s lu c a ja n = new Random(47); p r i n t ( ..... + dajR ec() + ..... ) ; S trin g is p returano = is p r e tu r a jR e c ( ) ; p ri n t ( i spreturano); retu rn is p re tu ra n o .e q u a ls ("lA l" ) ;

}
@Test boolean i s p r e t u r a j 2 ( ) { s lu c a ja n = new Random(74); p r i n t ( ..... + dajRecO + ..... ) ; S t r i n g is p r e t u r a n o = i s p r e t u r a j R e c ( ) ; p r i n t ( i spreturano); r e tu rn is p re tu ra n o .e q u a ls ("ts a e b o ro rn u s s u ");

}
p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] arg s) throws Exceptio n { S y s te m .o u t. p r i n t l n ( k r e e " ) ; OSIzvrenje.komanda( " ja v a n e t . m i n d v i e w . a t u n i t . A t U n i t A t U n i t P r i m e r 4 " ) ;

}
} / * Is p is : kree a n o t a c i j e . A t U n i tP rim er4 . i s p r e t u r a j 1 ' A11 ' 1A1 . is p re tu ra j2 ts a e bororn ussu . reci 'a re ' ' b r o n t o s a u ru s e s '

OK (3 t e s t s )

* ///:@ TestProperty mocte upotrebiti i za oznaavanje m etoda koje se tokom testiranja koriste, ali same nisu testovi. Vodite rauna o tom e da uspenost ovog program a zavisi od redosleda izvravanja testova, to po pravilu nije dobro.

872

Misliti na Javi

Ukoliko tokom inicijalizacije objekata za testiranje uradite neto to kasnije treba poistiti (engl. clean up ), m oete dodati statinu m etodu oznaenu sa @ TestObjectC leanup koja obavlja ienje kada zavrite testiranje objekta. U sledeem prim eru, @ TestO bjectCreate otvara datoteku rad i pravljenja svakog objekta za testiranje, pa se ta datoteka m ora zatvoriti pre odbacivanja sam og objekta testiranja:
/ / : a n o ta c ije /A tlln itP rim e r5 .ja v a package a n o t a c i j e ; im p o rt j a v a . i o . * ; im p o r t n e t . m i n d v i e w . a t u n i t . * ; im p o r t n e t . m i n d v i e w . u t i l . * ; p u b l i c c la s s A tU n itP rim e r5 { p riv a te S trin g te k s t; p u b l i c A t U n it P r im e r 5 ( S t r in g t e k s t ) { t h i s . t e k s t = t e k s t ; p u b lic S trin g to S trin g () { re tu rn t e k s t; } @TestProperty s t a t i c P r i n t W r i t e r o u t p u t ; @TestProperty s t a t i c i n t b r o j a c ; PT estO bje ctCreate s t a t i c A t U n it P r im e r 5 n a p r a v i () { S t r i n g id = I n t e g e r . t o S t r i n g ( b r o j a c + + ) ; try { i z l a z = new P r i n t W r i t e r ( " T e s t " + i d + " . t x t " ) ; } ca tc h (IO E x c e p tio n e) { th ro w new R u n tim e E x c e p tio n ( e ) ;

}
r e t u r n new A t U n i t P r i m e r 5 ( i d ) ;

}
PTestObjectCleanup s t a t i c v o id c le a n u p ( A tU n itP rim e r5 t o b j ) { S y s t e m . o u t . p r in t ln ( " P o k r e e m metodu c l e a n u p " ) ; iz la z .c lo s e O ;

}
@Test boolean t e s t l ( ) { iz la z .p rin t(" te s tl" ); retu rn tru e ;

}
OTest boolean t e s t 2 ( ) { iz la z .p rin t( " te s t2 " ) ; retu rn tru e ;

}
@Test boolean t e s t 3 ( ) { iz la z .p rin t("te s t3 "); retu rn tru e ;

}
p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] a rg s ) throws Exce p tio n { OSIzvrenje.komanda( " ja v a n e t . m i n d v i e w . a t u n i t . A t U n i t A t U n i t P r i m e r 5 " ) ;

Poglavlje 20: Anotacije

873

} / * Is p is : a n o ta c i j e . A t l l n i t P r i m e r 5 . te s tl Pokreem cleanup . test2 Pokreem cleanup . te st3 Pokreem cleanup OK (3 t e s t s )

* ///Iz ispisa rezultata vidite da se m etoda za ienje autom atski pokree nakon svakog testiranja.

Testiranje generikih tipova alatkom @Unit


G eneriki tipovi predstavljaju poseban problem , zato to ne moete testirati generiki" (uopteno). Testira se odreeni param etar tipa ili skup param etara. Reenje je jednostavno: izvedite klasu za testiranje iz speficirane verzije generike klase. O vo je jednostavna realizacija steka:
/ / : a n o t a c i j e / S t e k L . ja v a / / Stek n a p r a v lje n od ulanane l i s t e . package a n o t a c i j e ; im p o r t j a v a . u t i l . * ; p u b l i c c la s s StekL<T> { p r i v a t e L in k e d L is t< T > l i s t = new L i n k e d L i s t < T > ( ) ; p u b l i c v o id push(T v) { l i s t . a d d F i r s t ( v ) ; } p u b l i c T t o p ( ) { r e t u r n 1i s t . g g e t F i r s t ( ) ; } p u b l i c T pop() { r e t u r n 1i s t . r e m o v e F ir s t ( ) ; }

} ///:Izveemo klasu za testiranje iz S tekL <String> da bism o testirali S tring verziju:


/ / : a n o ta c ije /S te k L S trin g T e s t.ja v a / / Primena a l a t k e (?Unit na g e n e r i k e t i p o v e . package a n o t a c i j e ; im p o r t n e t . m i n d v i e w . a t u n i t . * ; im p o r t n e t . m i n d v i e w . u t i l . * ; p u b l i c c la s s S t e k L S t r in g T e s t extends S te k L < S tr in g > { PTest v o id _p u sh() { p u s h ("je d a n "); a s s e r t t o p ( ) , e q u a ls ( " j e d a n " ) ; p u sh ("d va "); assert t o p ( ) .e q u a ls ("d v a ");

}
PTest v o id _pop() p u s h ("je d a n "); {

874

Misliti na Javi

push ("d va "); a s s e r t pop( ) . e q u a l s ( " d v a " ) ; a s s e r t pop( ) , e q u a l s ( " j e d a n " ) ;

}
@Test v o id _ t o p ( ) { p ush("A "); p u s h C 'B ") ; assert to p ().e q u a ls (" B ) ; assert to p ( ) .e q u a ls ( " B " ) ;

}
p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] a rg s) throws Exce p tio n { OSIzvrenje.komanda( " ja v a n e t . m i n d v i e w . a t u n i t . A t U n i t S t e k L S t r i n g T e s t " ) ;

}
} / * Is p is : a n o ta cije.S tekL S trin g T est . _push _Pop . _to p OK (3 t e s t s )

* ///:Jeini potencijalni nedostatak nasleivanja jeste to to gubim o m ogunost pristupanja privatnim m etodam a klase koja se testira. Ukoliko to predstavlja problem , dodajte toj metodi m odifikator protected ili napiite neprivatnu @TestProperty m etodu koja poziva tu privatnu m etodu (potom e <TestProperty m etoda alatkom AtUnitRemover biti izbaena iz koda za isporuku, i to u prikazati u nastavku poglavlja).

Veba 8: (2) Napravite privatnu m etodu i dodajte joj neprivatnu @TestProperty metodu, kao to je prethodno opisano. Pozovite tu m etodu u svom kodu za testiranje.

Veba 9: (2) Napiite osnovne @Unit testove za HashMap. Veba 10: (2) Izaberite neki prim er iz drugih delova knjige i doajte m u @Unit testove.

Svite" nisu potrebne


Jedna od velikih prednosti alatke @Unit nad JU nitom jeste to to ,,svite nisu potrebne. JUnitu m orate saoptiti ta elite da testirate, i zato se testovi grupiu u ,,svite da bi JUnit mogao da ih pronade i pokrene. @Unit jednostavno trai datoteke klasa koje sadre odgovarajue anotacije i zatim izvrava @Test m etode. Jedan od m ojih glavnih ciljeva prilikom projektovanja sistema za testiranje @Unit bio je da on bude neverovatno jasan, kako bi Ijudi mogli poeti da ga koriste jednostavnim dodavanjem @Test m etoda, bez ikakvog specijalnog koda ili predznanja kakvo je neophodno za JUnit i m noge druge strukture za testiranje. Pisanje testova je ovoljno teko bez ikakvih dodatnih prepreka, pa pom ou @Unit moe postati m nogo lake. Zbog toga su vee anse da ete zaista poeti da piete testove.

Poglavlje 20: Anotacije

875

Realizacija interfejsa @Unit


Prvo m oram o da definiemo sve tipove anotacija. To su obine oznake bez polja. Oznaka @Test je bila definisana na poetku poglavlja, a ovde su ostale anotacije:
/ / : n e t/m in d v ie w /a tu n it/T e s tO b je c tC re a te .ja v a / / @Unit oznaka @TestObjectCreate. package n e t . m i n d v i e w . a t u n i t ; im p o r t j a v a . l a n g . a n n o t a t i o n . * ; PTarget(E1ementType.METHOD) @Re tention(Re tentionPolicy.RUNT IME) p u b l i c @ in te rfa c e T e s tO b je c tC re a te {} / / / : / / : n e t / m in d v i e w / a t u n i t / T e s t O b j e c t C l e a n u p . j a v a / / PU n it oznaka PT estO bjectCleanup. package n e t . m i n d v i e w . a t u n i t ; im p o r t j a v a . l a n g . a n n o t a t i o n . * ; O Ta rg e t(E1ementType.METHOD) @ R e t e n tio n ( R e te n tio n P o li c y . RUNTIME) p u b l i c (Pin te rfa ce TestO bje ctCle anup {} / / / : / / : n e t/m i ndvi e w /a tu n i t / T e s t P r o p e r t y . j a v a / / @Unit oznaka @TestProperty. package n e t . m i n d v i e w . a t u n i t ; im p o r t j a v a . l a n g . a n n o t a t i o n . * ; / / I p o l j a i metode mogu b i t i oznaeni sa @T estProperty: @Target({ElementType.FIELD, E 1 ementType.METHOD}) @Retenti o n ( R e t e n t io n P o li c y . RUNTIME) p u b l i c @ in te rfa c e T e s t P r o p e r t y {} / / / :

Svi testovi imaju RUNTIME kao vrednost m etaanotacije @ Retention, zato to sistem @Unit testove m ora da pronalazi u prevedenom kodu. l/.vojiemo anotacije pom ou refleksije da bism o realizovali sistem koji izvrava testiranje. Na osnovu tih informacija program odluuje kako da napravi test objekte i kako da ih testira. Zbog anotacija, to se postie iznenaujue malim i jednostavnim programom:
/ / : n e t / m i n d v i e w / a t u n i t / A t U n i t . ja v a / / S t r u k t u r a za j e d i n i n o t e s t i r a n j e i j u osnovu in e a n o t a c i j e . / / {RunByHand} package n e t . m i n d v i e w . a t u n i t ; im p o r t j a v a . l a n g . r e f l e c t . * ; im p o r t j a v a . i o . * ; im p o r t j a v a . u t i 1. * ; im p o r t n e t . m i n d v i e w . u t i l . * ; im p o r t s t a t i c n e t . m i n d v i e w . u t i l . P r i n t . * ;

87 6

Misliti na Javi

p u b l i c c la s s A t U n i t implements P r o c e s s F ile s . S t r a t e g y { s t a t i c Class<?> t e s t C l a s s ; s t a t i c L i s t < S t r i n g > f a i l e d T e s t s = new A r r a y L i s t < S t r i n g > ( ) ; s t a t i c long testsRun = 0; s t a t i c long f a i l u r e s = 0; p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] a rg s) throws Exce p tio n { C la ssL o a d er.g e tSyste m C la ssL o a d e r() . s e t D e f a u l t A s s e r t i o n S t a t u s ( t r u e ) ; / / D ozvoli naredbe a s s e r t new P r o ce s sF ile s (n e w A t U n i t ( ) , " c l a s s " ) . s t a r t ( a r g s ) ; i f ( f a i l u r e s == 0) p r i n t ( " 0 K ( " + testsRun + " t e s t s ) " ) ; e ls e { p r i n t ( " ( " + te stsRun + " t e s t s ) " ) ; p r i n t ( " \ n > " + f a i l u r e s + " FAILURE" + ( f a i l u r e s > 1 ? "S" : " " ) + " < " ) ; fo r ( S t r in g f a ile d : fa ile d T e s ts ) p rin t(" " + fa ile d );

} }
p u b l i c v o id p r o c e s s ( F i l e c F i l e ) { try { S t r i n g cName = C la s s N a m e F in d e r.th is C la s s ( B in a ry F ile .re a d (c F ile )); r e t u r n ; / / Zanemari k la s e iz va n paketa i f ( ! c N a m e . c o n t a in s ( " . " ) ) t e s t C la s s = Class.forName(cName); } c a t c h ( E x c e p t io n e) { th ro w new R u n t im e E x c e p t io n ( e ) ;

}
TestMethods te stM e th o d s = new T e s tM e th o d s ( ) ; Method c r e a t o r = n u l l ; Method cleanup = n u l l ; fo r(M e th o d m : t e s t C la s s . g e t e c la r e d M e t h o d s O ) t e s t M e t h o d s . a d d lf T e s t M e t h o d ( m ) ; i f ( c r e a t o r == n u l l ) c r e a t o r = ch e ckForCre atorM ethod(m ); i f ( c l e a n u p == n u l l ) cleanup = checkForCleanupMethod(m);

}
i f ( t e s t M e t h o d s . s i z e ( ) > 0) { i f ( c r e a t o r == n u l 1) try { if( !M o d ifie r .is P u b lic ( te s tC la s s ,g e tD e c la re d C o n s tru c to r(),g e tM o d ifie rs ())) p r i n t ( " E r r o r : " + t e s t C la s s + " d e f a u l t c o n s t r u c t o r must be p u b l i c " ) ; S y s te m .e x it(l);

}
} catch(NoSuchMethodException e) {

Poglavjje 20: Anotacije

877

/ / N a p ra v lje n podrazumevani k o n s t r u k t o r ; 0K

1
p rin t(te s tC la s s .g e tN a m e ());

}
f o r( M e th o d m : te stM e th o d s) { p rin tn b (" . " + m.getName() + " " ) ; try { O b je ct t e s t O b j e k a t = c r e a t e T e s t O b j e c t ( c r e a t o r ) ; boolean success = f a l s e ; try { if(m .g e tR e turn T y p e ().e q u a ls (bo o le a n .c la s s )) success = ( B o o le a n ) m . in v o k e ( t e s t O b je k a t ) ; el se { m .i n v o k e ( t e s t O b j e k a t ) ; success = t r u e ; / / Ako sve naredbe a s s e r t uspeju

}
} ca tc h (In v o c a tio n T a rg e tE x c e p t" io n e) { / / S t v a r n i iz u z e t a k j e u n u ta r e: p rin t(e .g e tC a u s e O );

}
p rint(success ? "" : " ( f a i l e d ) " ) ; testsRu n++; if(is u c c e s s ) { fa ilu re s + + ; f a i le d T e s t s . a d d (t e s t C l ass.getNameO + " : " + m.getName()) ;

}
i f ( c l e a n u p != n u l l ) c l e a n up .i n v o k e ( t e s t O b je k a t , t e s t O b j e k a t ) ; } c a t c h (E x c e p t io n e) { th ro w new R u n tiirte E x ce ptio n (e );

} } }
s t a t i c c la s s TestMethods extends A rray L is t< M e th o d > { v o id addIfTestMethod(Method m) { i f ( m . g e t A n n o t a t i o n ( T e s t . c l ass) == n u l l ) retu rn ; i f ( ! ( m . g e t R e t u r n T y p e ( ) .e q u a ls ( b o o le a n . c la s s ) | | m .g e t R e t u r n T y p e ( ) . e q u a ls ( v o i d . c l a s s ) )) th ro w new RuntimeException("@Test method" + " must r e t u r n boolean o r v o i d " ) ; m .s e tA c c e s s ib le (tru e ); / / lik o lik o j e p riv a tn a i t d . add(m );

} }
p r i v a t e s t a t i c Method checkForCreatorMethod(Method m) { if ( m . g e t A n n o t a t i o n ( T e s t O b j e c t C r e a t e . c l a s s ) == n u l l ) retu rn n u l1 ; i f ( !m .g e t R e t u rn T y p e ( ) . e q u a l s ( t e s t C l a s s ) )

878

Misliti na Javi

th ro w new Runtim eExceptio n("@ TestO bje ctC re ate " + "must r e t u r n in s t a n c e o f Class t o be t e s t e d " ) ; if((m .g e tM o d ifie rs () & j a v a . l a n g . r e f l e c t . M o d i f i e r . S T A T I C ) < 1) th ro w new Runtim eE xceptio n("@ TestO bje ctC re ate " + "must be s t a t i c . " ) ; m .s e tA c c e s s ib le (tru e ); r e t u r n m;

}
p r i v a t e s t a t i c Method checkForCleanupMethod(Method m) { i f ( m . g e t A n n o t a t io n ( T e s t O b je c t C le a n u p . c la s s ) == n u l l ) r e t u r n n u l 1; i f ( ! m .g e t R e t u r n T y p e ( ) . e q u a l s ( v o i d . c l a s s ) ) th ro w new RuntimeException("@ TestO bjectCleanup " + "must r e t u r n v o i d " ) ; i f ( ( m . g e t M o d i f i e r s () & j a v a . l a n g . r e f l e c t . M o d i f i e r . S T A T I C ) < 1) th ro w new RuntimeException("@ TestO bjectCleanup " + "must be s t a t i c . " ) ; if ( m . g e t P a r a m e t e r T y p e s ( ) . le n g t h == 0 || m .getP aramete rTypes() != t e s t C la s s ) th ro w new R u n tim e E x c e p tio n ("O T e s tO b je c tC leanup " + "must ta k e an argument o f th e te s t e d t y p e . " ) ; m .s e tA c c e s s ib le (tru e ); r e t u r n m;

}
p r i v a t e s t a t i c O b je ct c re a te T e s tO b je c t(M e th o d c r e a t o r ) i f ( c r e a t o r != n u l l ) { try { retu rn c r e a t o r . in v o k e (te s tC la s s ); } c a tc h ( E x c e p t io n e) { th ro w new R u n tim e E xc e p tio n ( "Could n ' t run " + "OTestO bject ( c r e a t o r ) m e t h o d . " ) ; {

}
} e ls e { / / U p otre bi podrazumevani k o n s t r u k t o r : try { re tu rn t e s t C lass,n e w ln s ta n c e (); } c a t c h (E x c e p t io n e) { th ro w new R u n tim e E x ce p tio n ( " C o u ld n ' t c r e a t e a " + " t e s t o b j e c t . Try u s in g a OTestObject m e t h o d . " ) ;

} } } } ///:-

ProcessFiles iz paketa net.mindview.util. Klasa AtUProcessFiles.Strategy k o ji o b u h v a t a m e t o d u process( ). N a taj n a in se in sta n ca o d AtUnit m o e p r o s l e d i t i ProcessFiles k o n s t r u k t o r u . D r u g i a r g u m e n t k o n s t r u k t o r a saoptava ala tci ProcessFiles da tra i d a tote ke s n a s ta v k o m im e n a class.
u p o t r e b lja v a a la t k u

AtUnit.java

nit

re a li z u je in t e rf e js

Poglavlje 20: Anotacije

879

Ako ne zadate argum ent s kom andne linije, program e pretraiti stablo tekueg direktorijum a. M oete zadati i vie argum enata, bilo class datoteka (s nastavkom .class ili bez njega) bilo direktorijum a. Poto @Unit autom atski pronalazi klase i m etode koji se m ogu testirati, nije potreban m ehanizam ,,svita.8 Jedan od problem a koje AtUnit.java m ora da rei kada pronae class datoteku jeste taj to se p u n o im e klase (koje obuhvata i ime njenog paketa) ne m oe saznati iz im ena same class datoteke. Zato se class datoteka m ora analizirati, to nije lako, ali ni nem ogue.9 Dalde, nakon pronalaska .class datoteke ona se prvo otvara, njen binarni sadraj uitava i predaje m etodi ClassNam eFinder.thisClass( ). Potom prelazim o u dom en inenjeringa bajtkoda, poto zapravo analiziram o sadraj class datoteke:
/ / : n e t/m i n d v i e w / a t u n i t / C l assNameFin d e r . ja v a package n e t . m i n d v i e w . a t u n i t ; im port j a v a . i o . * ; im p o r t j a v a . u t i l . * ; im p o r t n e t . m i n d v i e w . u t i l . * ; im p o r t s t a t i c n e t . m i n d v i e w . u t i l . P r i n t . * ; p u b l i c c la s s ClassNameFinder { p u b l i c s t a t i c S t r i n g t h i s C la s s ( byt e [ ] c la s s B y te s ) { M ap<Integer,Integer> o ffs e tT a b le = new H a s h M a p < I n t e g e r , I n t e g e r > ( ) ; M a p < I n t e g e r , S t r in g > classNameTable = new H a s h M a p < I n t e g e r , S t r in g > ( ) ; try { Da ta ln p utS tre a m data = new Da ta InputStream( new B y t e A r r a y I n p u t S t r e a m ( c la s s B y t e s ) ) ; i n t magic = d a t a . r e a d l n t ( ) ; / / Oxcafebabe i n t m in o rV e rsio n = d a t a . r e a d S h o r t ( ) ; i n t m a jo rV e rsio n = d a t a . re a d S h o r t ( ) ; i n t c o n s t a n t_ p o o l_ c o u n t = d a t a . r e a d S h o r t ( ) ; i n t [ ] c o n s tan t_ p o o l = new i n t [ c o n s t a n t _ p o o l _ c o u n t ] ; f o r ( i n t i = 1; i < c o n s ta n t_ p o o l_ c o u n t; i+ + ) { i n t tag = d a t a . r e a d f ) ; in t ta b le S iz e ; s w itc h (ta g ) { case 1: / / UTF i n t le n g t h = d a t a . r e a d S h o r t ( ) ; c h a r [ ] bytes = new c h a r [ l e n g t h ] ; f o r ( i n t k = 0; k < b y t e s . l e n g t h ; k++) b yte s[k] = (c h a r)d a ta .re a d (); S t r i n g imeKlase = new S t r i n g ( b y t e s ) ; classNameTable . p u t ( i , im e K la s e ); b re ak;

8 v

N ije ja s n o z a to p o d ra z .u m e v a n i k o n s tr u k to r k lase k o ja se te stira m o r a b iti ja v a n , a li ak o n ije , p o z iv m e to d e n e w l n s t a n c e ( ) p ro u z ro k u je z a m rz a v a n je (n e g e n e rie iz u z e ta k ). Jerem v M e y e ru i m e n i tre b a lo je sk o ro ceo d a n d a to sm islim o .

880

Misliti na Javi

case 5: / / LONG case 6: / / DOUBLE d a t a . r e a d L o n g O ; / / o d b a c i t i 8 b a jt o v a i+ + ; / / Potreban j e poseban skok b re ak; case 7: / / CLASS in t o ffs e t = d a ta .re a d S h o rt(); o ffs e tT a b le .p u t(i, o ffs e t) ; break; case 8: / / STRING d a ta .re a d S h o rt(); / / o d b a c iti 2 b a jta bre ak; case 3: / / INTEGER case 4: / / FLOAT case 9: / / FIELD_REF case 10: / / METHOD_REF case 11: / / INTERFACE_METHOD_REF case 12: / / NAME_AND_TYPE d a ta .re a d ln t( ) ; / / o d b a c iti 4 b a jta ; break; d e fa u lt: throw new R untim eE xceptio n("B ad tag " + t a g ) ;

} i
s h o r t a c c e s s _ fl ag s = d a t a . r e a d S h o r t ( ) ; i n t th is _ c la s s = d a ta .r e a d S h o rt( ) ; i n t s uper_ cla ss = d a t a . r e a d S h o r t ( ) ; r e t u r n cla ssNam eT able.get( o f f s e t T a b l e . g e t ( t h i s _ c l a s s ) ) . r e p l a c e ( 1/ 1, 1. 1) ; } c a t c h (E x c e p t io n e) { th ro w new R u n t im e E x c e p t io n ( e ) ;

} }
/ / P r ik a z : p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] a rg s) throws Exceptio n { i f ( a r g s . l e n g t h > 0) { f o r ( S t r i n g arg : args) p r i n t ( t h i s C l a s s ( B i n a r y F i 1e .re ad(new Fi 1e ( a r g ) ) ) ) ; } el se / / Prola zak kroz c e lo s t a b l o : f o r ( F i l e kla ss : D i r e c t o r y . w a l k ( " . " , " . * \ \ . c l a s s " ) ) p rin t(th is C la s s (B in a ry F ile .re a d (k la s s )));

}
} lll--~

lako ovde ne m oem o ulaziti u sve pojedinosti, svaka class datoteka im a odredeni form at, a ja sam pariim a podataka izvadenih iz toka B yteA rray In p u tS tream pokuao da dam smislena imena. Veliinu svakog pareta moete saznati na osnovu duine uitanog ulaznog toka. Prim era radi, prva 32 bita svake class datoteke uvek zauzim a magini broj

Poglavlje 20: Anotacije

881

Oxcafebabe,1 0 a sledea dva shorta su inform acija o verziji. Skladite (engl. pool) konstanti sadri konstante za program i zato je prom enljive veliine; sledei short kazuje
koliki je, da bi m u mogao biti dodeljen niz odgovarajue veliine. Svaka stavka skladita konstanti moe biti vrednost fiksne ili prom enljive veliine, pa m oram o da ispitam o oznaku kojom stavka poinje da bism o shvatili ta s njom da radim o - to je naredba switch. Ovde nism o pokuavaii da tano analiziram o sve podatke u class datoteci, nego sam o da proem o kroz sve vane parie i da ih uskladitim o, pa se nem ojte uditi to se dobar deo podataka odbacuje. Inform acije o klasam a skladite se u tabeli classNameTable i tabeli offsetTable. Nakon uitavanja skladita konstanti m oe se pronai inform acija this_dass, to je indeks tabele offsetTable koja proizvodi indeks za classNameTable koja proizvodi ime klase. V ratim o se program u AtUnit.java. M etoda pro cess( ) sada im a ime klase i m oe u njoj p o tra iti., to znai da je deo nekog paketa. Klase izvan paketa se zanem aruju. Ukoliko je klasa u paketu, standardni uitava klasa uitava klasu m etodom Class.forN am e( ). Sada se u klasi m ogu potraiti @Unit anotacije. Traim o samo tri stvari: @Test m etode koje su uskladitene u listi TestMethods i metode oznaene sa @TestObjectCreate ili @TestObjectCIeanup. Njih pronalazim o tako to pozivam o pridruene m etode koje trae anotacije. Ako se pronae neka @Test m etoda, ime odgovarajue klase se tam pa da bi posm atra znao ta se dogaa i zatim se izvrava svaki test. To znai da se tam pa ime metode, zatim poziva createTestO bject( ) koja e pozvati m etodu @TestObjectCreate ako takva postoji ili e u protivnom pozvati podrazum evani konstruktor. Nakon pravljcnja test objekta poziva se m etoda koja ga testira. Ako test vrati vrednost tipa boolean, taj rezultat se pam ti. U suprotnom , pretpostavljam o da je test uspean ukoliko se nije pojavio izuzetak (to bi se desilo u sluaju neuspene naredbe assert ili bilo koje druge vrste izuzetka). Ukoliko izuzetak bude generisan, tam paju se pripadajue inform acije da bi korisnik video uzrok. U svim sluajevima neuspeha (engl. failure) poveava se sadraj brojaa neuspeha, a ime neuspene klase i m etode dodaju se listi failedTests da bi bile prijavljene na kraju testiranja. Veba 11: (5) Alatki @Unit dodajte anotaciju @TestNote; ona oznaava pratee napom ene (engl. notes) koje se ispisuju tokom testiranja.

Uklanjanje koda za testiranje


M ada u m nogim projektim a nee sm etati ako ko za testiranje ostavite u program u koji se isporuuje (naroito ako sve ispitne m etode oznaite kao privatne, to m oete ako elite), u nekim sluajevima m oraete taj kod da izbacite kako ga ne biste otkrili kupcu ili da bi verzija koja se isporuuje bila mala. Za to je potreban inenjering bajtkoda sofisticiraniji od onog koji se moe lako napisati. M eutim , biblioteka otvorenog koda Javassist1 1 svodi inenjering bajtkoda u dom en m ogueg. N aredni program prim a opcioni indikator -r kao svoj prvi argum ent; ako zadate taj
1 1 1 O z n a e n ju tih b a jto v a p o s to je ra z n e le g e n d e , ali p o to su Javu p ra v ili lju d i z a lu e n i ra u n a r im a , v ero v a tn o su p ri to m u k afiu m a tali o n ek o j eni. 1 Z a h v a lju je m d r S h ig e ru u C h ib a i to je n a p ra v io o v u b ib lio te k u i za sv u n je g o v u p o m o u p is a n ju p ro g ra m a A tU n itR e m o v e r.ja v a .

882

Misliti na Javi

za

indikator, on e ukloniti @Test anotacije, a ako to ne uinite, on e ih prikazati. I ovde se prolazak kroz datoteke i direktorijum e koje odaberete upotrebljava ProcessFiles:
/ / : n e t / m in d v ie w / a t u n i t / A t U n it R e n io v e r . ja v a / / P r ik a z u je @Unit a n o t a c i j e u prevednim datotekama k la s a . Ako j e / / p r v i argument " - r " , @Unit a n o t a c i j e e b i t i u k lo n je n e . / / { A rg s: . . } / / {Neophodno za r a d : j a v a s s i s t . b y t e c o d e . C l a s s F i l e ; / / Morate i n s t a l i r a t i b i b l i o t e k u J a v a s s i s t sa / / h ttp ://s o u rc e fo rg e .n e t/p ro je c ts /jb o s s / } package n e t . m i n d v i e w . a t u n i t ; im p o r t j a v a s s i s t . * ; im p o rt j a v a s s i s t . e x p r . * ; im p o r t j a v a s s i s t . b y t e c o d e . * ; im p o r t j a v a s s i s t . b y t e c o d e . a n n o t a t i o n . * ; im p o r t j a v a . i o . * ; im p o rt j a v a . u t i l . * ; im p o r t n e t . m i n d v i e w . u t i l . * ; im p o rt s t a t i c n e t . m i n d v i e w . u t i l . P r i n t . * ; p u b l i c c la s s AtUnitRemover implements P r o c e s s F ile s . S t r a t e g y { p r i v a t e s t a t i c boolean u k lo n i = f a l s e ; p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] args) throws Exce p tio n { i f ( a r g s . l e n g t h > 0 && a r g s , e q u a l s ( - r " ) ) { u k lo n i = t r u e ; S t r i n g [ ] b r o j a r g = new S t r i n g [ a r g s . l e n g t h - 1 ] ; S y s te m .a rr a y c o p y ( a rg s , 1, b r o j a r g , 0, b r o j a r g . 1e n g t h ) ; args = b r o j a r g ;

}
new P ro c e s s F i1e s ( new A tU n itR e m o v e r( ) , " c l a s s " ) . s t a r t ( a r g s ) ;

}
p u b l i c v o id p r o c e s s ( F i l e cDatka) { boolean m o d if ik o v a n a = f a l s e ; try { S t r i n g clme = C la s sN a m e F in d e r.th is C la s s( B i n a r y F i 1e . r e a d ( c D a t k a ) ) ; if(!c Im e .c o n ta in s (".")) r e t u r n ; / / Klase iz v a n paketa se zanemaruju ClassPool cPool = C l a s s P o o l . g e t D e f a u l t ( ) ; CtClass c t C la s s = c P o o l . g e t ( c l m e ) ; fo r(C tM e th o d metoda : c t C l a s s . g e tD e c l a r e d M e t h o d s f ) ) { M eth o d ln fo mi = m e to d a .g e tM e th o d ln fo O ; A n n o ta tio n s A ttrib u te a t t r = (A n n o ta tio n s A ttrib u te ) m i.g e tA ttrib u te (A n n o ta tio n s A ttrib u te . vi s ib le T a g ); i f ( a t t r == n u l l ) c o n t in u e ; f o r ( A n n o t a t i o n ann : a t t r . g e t A n n o t a t i o n s O ) { if (ann.getTypeNam e()

Poglavjje 20: Anotacije

883

.s ta rts W ith ("n e t.m in d v ie w .a tu n it")) { p r i n t ( c t C la s s . g e t N a m e ( ) + " Method: " + mi .getName() + 1 1 " + an n ); if(u k lo n i) { c t C l a s s . removeMethod(metoda); m o d if ik o v a n a = t r u e ;

) ) } }
/ / Ova v e r z i j a ne u k l a n j a p o l j a ( v i d e t i t e k s t ) . if ( m o d i f i kovana) c t C l a s s . toBytecode(new DataOutputStream( new F ile O u t p u t S t r e a m ( c D a t k a ) ) ) ; c tC la s s .d e ta c h O ; } c a t c h (E x c e p t io n e) { th ro w new R u n tim e E x c e p tio n ( e ) ;

} } } ///:-

ClassPool je neka vrsta slike svih klasa u sistem u koji m odifikujete. O n jem i doslednost obrade svih m odifikovanih klasa. Svaku CtClass m oram o izvaditi iz skladita ClassPool, slino kao to uitava klasa (engl. class loadcr) i m etoda Class.forN am e( ) uitavaju klase u JVM. CtClass sadri bajtkodove objekta klase i om oguuje proizvodnju inform acija o toj klasi i rad s nienim kodom . Ovde sam iz svakog CtM ethod objekta pozvao m etodu getD eclaredM ethods( ) (kao Javin m ehanizam refleksije) i dobio M ethodlnfo. U njim a se trae anotacije. Ukoliko m etoda ima anotaciju iz paketa net.m indview.atunit, ta metoda biva uklonjena. Ako je klasa bila modifikovana, originalna klasa e biti zam enjena novom. U vrem e pisanja ove knjige, funkcije za ,,uldanjanje su tek bile dodate u Javassist,1 2i otkrili sm o da je uklanjanje @TestProperty polja ispalo sloenije od uklanjanja m etoda. Polja nije dovoljno prosto ukloniti, poto u vezi s njim a m oe biti statinih operacija inicijalizaje. Zato gornja verzija koda uklanja sam o @Unit m etode. Redovno na Web lokaciji biblioteke Javassist traite nove verzije; trebalo bi da i uklanjanje polja jed n o m bude mogue. U m euvrem enu, imajte u vidu da spoljna m etoda za testiranje prikazana u program u AtUnitSpoljniTest.java om oguuje uklanjanje svih testova tako to se obrie class datoteka koju pravi kod 7.a testiranje.

Saetak
Anotacije su dobrodoao dodatak Javi. O ne su stru k tu riran nain dodavanja m etapodataka kodu, zajemeno bezbedan u pogledu tipova, a pri tom kod ne ini neitljivim i zapetljanim . O ne mogu pom oi da se smanji dosadno pisanje deskriptora prim ene i drugih
l: D r S iiigerii C h ib a je v e o m a lju b a z n o , n a n a z ah tev , b ib lio te c i d o d a o C t C l a s s .r e m o v e M e th o d ( ).

88 4

Misliti na Javi

generisanih datoteka. injenica da je Javadoc oznaka @deprecated zam enjena anotacijom @Deprecated sam o je jedan od pokazatelja koliko su anotacije prikladnije od kom entara za opisivanje inform acija o klasama. Java SE5 im a tek aku ugraenih anotacija. To znai da ete sam i pisati anotacije i njim a pridruenu logiku, ukoliko ne p ronaete neku biblioteku s gotovim anotacijam a. A latkom ap t m oete u jedn om koraku prevesti novogenerisane datoteke i olakati proces autom atskog prevoenja i pakovanja ( b uilda ), ali tren u tn o u API-ju m irror im a malo vie od osnovnih funkcija za identifikovanje elem enata definicija Java klasa. Kao to ste videli, za inenjering bajtkoda m oe se upotrebiti Javassist ili kod koji ete eventualno sami napisati. Situacija e se svakako poboljati u ovom pogledu i proizvoai API-ja i struktura poee da isporuuju anotacije kao deo svojih kom pleta alatki. Kao to ste mogli da zakljuite nakon upoznavanja sa sistem om @Unit, veom a je verovatno da e anotacije znatno prom eniti na doivljaj program iranja na Javi. Reenja odabranih vebi data su u elektronskom dokumentu The Thinking in Java Annotatcd Solution Guide, koji se moe kupiti na lokaciji www.MindVicw.com.

Paralelno izvravanje
D osad ste uili o sekvencijalnom program iranju. U p ro g ra m u se sve odvija ko ra k p o korak.
S e k v e n c ija l n im
p r o g r a m ir a n je m m oem o r e Si t i v e l ik i p o d s k u p

PROGRAMERSKIH

zadataka. M edutim , neke problem e je podesnije ili ak neophodno reavati paralelnim izvravanjem vie delova program a, tako da izgleda da se ti delovi izvravaju istovrem eno ili to zaista i jeste tako, ukoliko raunar ima vie procesora ili procesor im a vie jezgara. Paralelnim program iranjem m oe se znaajno ubrzati izvravanje program a, dobiti laki m odel za projektovanje odreenih vrsta program a, ili i jedno i drugo. M eutim , izvetiti se u teoriji i tehnikam a paralelnog program iranja predstavlja vii stepen sposobnosti o d svega to ste dosad saznali u ovoj knjizi. To je tem a za srednji ili napredni nivo strunosti. Ovo dugako poglavlje moe posluiti sam o kao uvod, pa nikako nem ojte sm atrati da je tem eljno ovladavanje znanjem iz ovog poglavlja dovoljno za pisanje dobrih paralelnih program a. Kao to ete videti, pravi problem pri paralelnom izvravanju nastaje kada zadaci koji se paralelno izvravaju ponu m edusobno da se om etaju. To se moe desiti na tako suptilan i sluajan nain da je verovatno poteno rei kako je paralelno izvravanje ,,u teoriji determ inistiko, ali u praksi stohastiko. D rugim reima, ukoliko se paljivo radi i pregleda kod, m ogue je napisati program e za paralelno izvravanje koji rade ispravno. M eutim , u praksi je m nogo lake pisati program e za paralelno izvravanje koji sam o naizgled rade, a u odreenim okolnostim a zakazuju. Te okolnosti se ne m oraju dogoditi ili se m ogu dogaati toliko retko da ih tokom testiranja uopte ne vidite. Zapravo, m oda ne m oete da napiete ko za testiranje koji generie okolnosti u kojim a va program za paralelno izvravanje otkazuje. Otkazivanja se esto deavaju tek povrem eno i zato se o njim a sazna tek iz albi korisnika. To je jedan od najjaih razloga za prouavanje paralelnog izvravanja: ako ga preskoite, verovatno e vam se osvetiti. Dakle, paralelno izvravanje je prepuno opasnosti, i ako vas to plai, tako i treba. Iako je Java SE5 onela znatna poboljanja u paralelnom izvravaniu, i dalje nem a zatite koja bi vam ukazala na greku, kao to su provera u vreme prevoenja ili izuzeci koji se proveravaju. S paralelnim izvravanjem m orate sami da se nosite, i pouzdan vienitni kod moete da piete na Javi sam o ako ste istovrem eno sumnjiavi i agresivni. Ima miljenja da je paralelno izvravanje preteka tem a za knjigu iji je struni nivo poetni. Ti ljudi sm atraju da je paralelno izvravanje zasebna tem a koja se moe obraditi nezavisno, a nekoliko sluajeva koji se pojavljuju u svakodnevnom program iranju (kao to su grafika korisnika okruenja) mogu biti obraeni posebnim idiom im a. Zato nainjati toliko sloenu tem u ako se to moe izbei? Eh, kada bi to bila istina. Naalost, ne biram o mi kada e se u naim Java program im a pojaviti niti. To to sami niste otpoeli nit ne znai da moete izbei pisanje vienitnog koda. Na prim er, na Javi se najee piu Web sistemi, a osnovna klasa Web biblioteke, servlet, po prirodi je vienitna - i m ora da bude poto Web serveri esto imaju vie procesora, a paralelno izvravanje je idealan nain upotrebe tih procesora. Koliko god da servlet izgleda jednostavno, m orate razum eti paralelno izvravanje da biste servlete ispravno koristili.

886

Misliti na Javi

Isto vai i za program iranje grafikih korisnikih okruenja, kao to ete videti u sledeem poglavlju. Iako biblioteke Swing i SWT im aju m ehanizm e za bezbednost niti, teko je shvatiti kako da ih ispravno upotrebljavate ako ne razum ete paralelno izvravanje. Java je vienitni jezik i problem i s paralelnim izvravanjem postoje, znali vi to ili ne. Zato postoji m nogo Java program a koji rade ili sluajnim sticajem okolnosti ili tek vei deo vrem ena, i povrem eno m isteriozno zakazuju zbog neotkrivenih greaka u paralelnom izvravanju. Katkada su ta zakazivanja dobroudna, ali um eju da prouzrokuju gubljenje vrednih podataka, i ako barem niste uli za problem e s paralelnim izvravanjem, misliete da je problem u svemu pre nego u vaem softveru. Ta vrsta problem a m oe izroniti ili se pojaati kada se program prem esti na vieprocesorski raunar. Nakon to prouite ovo poglavlje, trebalo bi da znate kako prividno ispravni program i mogu zbog paralelnog izvravanja da ispolje neispravno ponaanje. Kada ponete da se bavite paralelnim program iranjem , kao da ste otili u stranu zemlju da uite njen jezik ili barem p o tp u n o nov skup jezikih pojm ova. Nauiti paralelno program iranje jednako je zahtevno kao nauiti objektno orijentisano program iranje. Ako se potrudite, shvatiete osnovni m ehanizam , ali za pravo ovladavanje tim predm etom po pravilu je potrebno dobro zagrejati stolicu. Iz ovog poglavlja stei ete osnovno znanje o paralelnom izvravanju, pa ete razum eti pojmove i moi ete da piete um ereno sloene vienitne program e. Budite svesni da je veoma lako stei neosnovano veliko poverenje u svoje sposobnosti za paralelno program iranje. Moraete da proitate knjige posveene iskljuivo toj temi ukoliko se odluite za pisanje iole sloenijih vienitnih programa.

Svi aspekti paralelnog izvravanja


Paralelno program iranje je teko shvatljivo zato to se njim e istovrem eno m ora reiti vie problem a. Uz to, postoji vie naina realizacije paralelnog rada, a da nam nije jasno koji problem se reava kojim pristupom . (esto su nejasne i granice izm eu njih.) Zato morate da shvatite sve problem e i specijalne sluajeve da biste efikasno upotrebljavali paralelno izvravanje. Probleme koje reavamo paralelnim izvravanjem grubo delim o na brzinu izvravanja i upravljivost projekta.

Bre izvravanje
Spoetka problem zvui jednostavno: ako elite da se program izvrava bre, podelite ga na niti koje izvravaju zasebni procesori. Paralelno izvravanje je osnovna alatka vieprocesorskog program iranja. Poto M urov zakon sve ree deluje (barem za obina integrisana kola), sredstvo za poveanje brzine postaju procesori s vie jezgara, a ne bra integrisana kola. Da biste naterali program e da rade bre, m oraete nauiti da iskoristite te dodatne procesore, i to je jedna od stvari koje e vam paralelno izvravanje omoguiti. Ako imate raunar s vie procesora, njim a se moe dodeliti vie zadataka (na istovrem eno izvravanje), to um e znatno da pobolja protok. Tako esto rade snani vieprocesorski Web serveri; oni procesorim a rasporeuju veliki broj korisnikih zahteva u program u koji dodeljuje jednu nit po zahtevu.

Poglavlje 21: Paraielno izvravanje

887

M eutim , paralelno izvravanje esto poboljava perform anse program a koji se izvravaju na je d n o m procesoru. Ovo zvui neverovatno ili barem neoekivano. Razmislite: paralelni program koji se izvrava na jednom procesoru trebalo bi da im a vee reijske trokove (slabije perform anse) nego u sluaju da se svi delovi program a izvravaju sekvencijalno, jer se m o ra platiti dodatno prebacivanje konteksta (prelazak s jednog zadatka na drugi). Pri povrnom razm atranju, reklo bi se da je jeftinije sve delove program a izvravati kao jedan zadatak i utedeti trokove prebacivanja konteksta. Ali uzm ite u obzir blokiranje. Ukoliko se jedan zadatak u vaem program u ne izvrava zbog neke okolnosti izvan kontrole program a (obino je to U/I), kaem o da taj zadatak ili nit blokira izvravanje program a. Ako nem a paralelnog izvravanja, ceo program se zaustavlja dok se spoljne okolnosti ne prom ene. S druge strane, ako je program napisan za paralelno izvravanje, ostali zadaci u program u m ogu se izvravati dok je jedan zadatak blokiran, pa program u celini nastavlja rad. Zapravo, sa stanovita perform ansi, nem a smisla pisati program za paralelno izvravanje na jednoprocesorskom raunaru ukoliko nijedan od njegovih zadataka ne m oe da zablokira izvravanje program a. Perform anse jenoprocesorskih raunara esto poboljavamo program im a kojitna upravljaju dogaaji. Vie niti se koristi ba zato da bi se napravilo korisniko okruenje koje brzo reaguje. Ukoliko program obavlja neke dugotrajne operacije i stoga zanem aruje ono to unosi korisnik, on sporo reaguje na sve to korisnik radi. D obar prim er za to je dugm e ,,izlaz - ne elite da u svakom elu koda u nutar celog program a ispitujete da li je korisnik pritisnuo to dugm e. Time se dobija nezgrapan kod, bez ikakvog jemstva da program er u nekom delu program a nee zaboraviti da proveri ta radi korisnik. Ako nema paralelnog izvravanja, korisniko okruenje koje brzo reaguje ostvaruje se sam o kada svi zadaci periodino proveravaju korisnikov unos. O dreenu brzinu reagovanja program jem i tako to pravi zasebnu nit izvravanja koja reaguje na korisnikov unos, iako e ta nit gotovo neprestano biti blokirana. Program treba da nastavi izvravanje svojih operacija i istovrem eno da vrati kontrolu korisnikom okruenju kako bi mogao da reaguje na radnje korisnika. Ali obina m etoda ne moe da se izvrava i da istovrem eno vrati kontrolu nad procesorom ostatku programa. To zvui nem ogue - kao da procesor treba da bude na dva mesta u isto vreme, ali upravo to i jeste iluzija koju prua rad s vie niti. (U sluaju vieprocesorskih sistema, to i nije iluzija.) Veoma jednostavan nain da se na nivou operativnog sistema realizuje paralelno izvravanje jeste upotreba procesa. Proces je svaki sam ostalan program koji se izvrava i ima vlastiti adresni prostor. Vieprogramski (engl. m ultitasking) operativni sistem moe da izvrava vie procesa (program a) istovremeno, a da pri tom izgleda kao da se svaki izvrava sam za sebe; to se postie raspodelom procesorskog vrem ena na sve tekue procese. Procesi su veoma privlani zato to ih operativni sistem najee izoluje jedne od drugih da se ne bi uzajam no om etali, pa je program iranje s njim a relativno lako. Za razliku od toga, sistemi za paraleino izvravanje - kao to je Javin - dele resurse (m em oriju i U/I) m eu nitim a, pa je osnovna potekoa pisanja vienitnih program a koordinacija upotrebe tih resursa u razliitim zadacima voenim nitim a, kako bi im u svakom trenutku pristupao sam o jedan zadatak.

888

Misliti na Javi

Evo jednostavnog prim era procesa u operativnom sistemu. D ok piem knjigu, redovno pravim vie redundantnih rezervnih kopija tekueg stanja knjige. Jednu kopiju napravim u lokalnom direktorijum u, dru gu na fle ureaju, treu na Zip disku i etvrtu na udaljenoj FTP lokaciji. Za autom atizaciju tog po stu p k a napisao sam m ali program (na Pythonu, ali koncepti su isti) koji kom prim uje knjigu u datoteku ije im e sadri broj verzije i zatim kopira. Spoetka sam sve kopije pravio sekvencijalno (ekao da se prethodno kopiranje zavri pa da otponem sledee). Potom sam shvatio da operacije kopiranja ne traju jednako, jer se U/I brzine m edijum a razlikuju. Poto sam im ao vieprogram ski operativni sistem, svaku operaciju kopiranja m ogao sam da zaponem kao zaseban proces i pustim ih da se izvravaju paralelno - tako se ubrzavalo izvravanje celog program a. D ok je jedan proces blokiran, drugi m ogu da rade. To je idealan prim er paralelnog izvravanja. Svaki zadatak se izvrava kao proces u sopstvenom adresnom prostoru, p a zadaci ne m ogu da utiu jedni na druge. Jo je vanije to to zadaci nem aju potrebe da kom uniciraju, poto su p o tp u n o nezavisni. O perativni sistem se stara o svim pojedinostim a koje obezbeuju ispravno kopiranje datoteka. Stoga nem a rizika i dobijam o bri program , zapravo besplatno. Neki zagovaraju procese kao jedini razum an p ristu p paralelnom izvravanju,1 ali je naalost broj procesa po pravilu ogranien, a njihovi reijski trokovi veliki. Zato se prim enom procesa ne mogu reiti svi zadaci iz spektra paralelnog izvravanja. Neki program ski jezici m eusobno izoluju zadatke koji se izvravaju paralelno. O bino ih nazivamo fu n kcijski jezici, jer u njim a poziv funkcije ne prouzrokuje sporedne uticaje (te ne moe ni da utie na druge funkcije) i stoga se m oe izvravati kao zaseban zadatak. Jedan od takvih jezika je Erlang, a on obuhvata m ehanizam sefa za kom unikaciju jednog zadatka s drugim. Ukoliko shvatite da u delu vaeg program a m ora intenzivno da se koristi paralelno izvravanje, a pri pisanju tog dela nailazite na velike problem e, razmislite o ideji da taj deo program a napiete na nam enskom jeziku za paralelno izvravanje kao to je Erlang. U Javi je pristup tradicionalniji - podrka za vienitni rad dodata je na gotov sekvencijalni jezik.2 Umesto da se ,,love spoljni procesi u vieprogram skom operativnom sistemu, niti prave zadatke u n u ta r istog procesa koji predstavlja program koji se izvrava. Jedna od prednosti koje se pri tom e postiu jeste nezavisnost od operativnog sistema, to je bio vaan cilj prilikom projektovanja Jave. Na prim er, pre verzije OS X operativnog sistema M acintosh (prilino vana publika za prve verzije Jave) nisu podravale vieprogramski rad. Da vienitni rad nije dodat u Javu, Java program i za paralelno izvravanje ne bi mogli da se izvravaju na M acintoshu i njem u slinim platform am a, im e bi se naruio zahtev napii jednom/izvravaj svuda.3

1 '

P r im e ra ra d i, E ric R a y m o n d n a v o d i ja k e ra z lo g e za to u sv ojoj kn jiz i Tlie A rt of U N IX Programming (A d d iso n -W e sle y , 2004). N eki s m a tra ju d a je svaki p o k u a j d a se p a ra le ln o iz v rav a n je p rih e fta uz s e k v e n c ija ln i je z ik o s u e n n a n e u s p e h , p a vi sam i z ak lju ite ta je istin a . O v a j z a h te v n ik a d a n ije b io p o tp u n o is p u n je n i S u n ga vie ne istie ta k o g la sn o . D a iro n ija b u d e vea, je d a n o d ra z lo g a to k o n c e p t n a p ii je d n o m /iz v r a v a j p o s v u d a " ni je p o tp u n o u s p e o m o d a je p o sled ica p ro b le m a u siste m u n iti - ko ji e m o d a b iti re e n i u Javi SE5.

Poglavlje 2 1: Paralelno izvravanje

889

Poboljan dizajn koda


Program koji izvrava vie zadataka na raunaru s jednim procesorom ipak u svakom tren utku radi sam o jedan posao, pa teorijski m ora biti m ogue d a se isti program napie bez svih tih zadataka. M eutim , paralelno program iranje daje vanu organizacionu prednost: dizajn program a se m oe znatno pojednostaviti. Neke vrste zadataka, kao to je sim ulacija, teko je reiti bez paralelnog program iranja. Veina ljudi je videla barem jedan oblik simulacije, bilo kao raunarsku igricu ili raunarski generisanu anim aciju u filmovima. Simulacije po pravilu obuhvataju m nogo elem enata u interakciji, svaki sa svojom pam eu. M ada se svako m oe uveriti da u raunaru s jednim procesorom svaki elem ent sim ulacije izvrava taj jedini procesor, sa stanovita program iranja m nogo je lake praviti se da svaki elem ent sim ulacije im a sopstveni procesor i da predstavlja nezavisan zadatak. Potpuna simulacija esto sadri veom a veliki broj zadataka, zato to svaki elem ent sim ulacije m oe da dejstvuje nezavisno - to vai i za vrata i stene, ne sam o za duhove i arobnjake. Vienitni sistemi esto imaju relativno mali broj d o stu p n ih niti, katkada reda veliine nekoliko desetina ili stotina. Taj broj m oe da se m enja nezavisno od kontrole program a - m oe zavisiti od platform e, ili u sluaju Jave, od verzije JVM-a. Kad koristite Javu, obino nioete pretpostaviti da broj dostupnih niti nee biti dovoljan da svakom elem entu velike simulacije date sopstvenu nit. Ovaj problem se obino reava koopcrativrtim vienitnim program iranjem (engl. cooperative m ultithreading). Java upotrebljava niti predupredno (engl. p rcem p tive ), to znai da mehanizam rasporeivanja svim nitim a dodeljuje procesorsko vreme, periodino prekida svaku nit i prebacuje kontekst na drugu nit, tako da svaka nit dobija um erenu koliinu vremena za izvravanje svog zadatka. U kooperativnom sistemu, svaki zadatak dobrovoljno predaje kontrolu sistemu, zbog ega program er u svaki zadatak m ora da um etne neku vrstu naredbe za preputanje kontrole. Prednost kooperativnog sistema je dvostruka: prebacivanje konteksta je obino mnogo jeftinije nego u preduprednom sistemu, i teorijski nema ogranienja broja nezavisnih zaataka koji se m ogu izvravati istovremeno. Kada simulacija ima m nogo elemenata, ovo moe da bude idealno reenje. Imajte u vidu da neki kooperativni sistemi ne rasporeuju zadatke po procesorim a, to je veliko ogranienje. S druge strane, paralelno izvravanje je veom a koristan m odel - zato to se ono upravo i deava - u radu sa savrem enim sistemima koji razm enjuju p o ru ke i m ogu obuhvatati m nogo nezavisnih raunara razbacanih u mrei. U tom sluaju, svi procesi se odvijaju nezavisno jedan od drugog i nem a m ogunosti ak ni za deljenje resursa. M eutim , i dalje m orate sinhronizovati prenos inform acija izm edu procesa, da ceo sistem za razm enu poruka ne bi izgubio inform aciju ili je usvojio u neodgovarajue vreme. ak i ako nem ate nam eru da u neposrednoj budunosti m nogo upotrebljavate paralelno program iranje, dobro je upoznati ga kako biste mogli da shvatite arhitekture sistema za razm enu poruka koji postaju dom inantni nain pravljenja distribuiranih sistema. Paralelno program iranje nam ee trokove, m eu kojim a i trokove zbog sloenosti, ali njih po pravilu opravdavaju poboljanja 11 dizajnu program a, uravnoteenom korienju resursa i lakoi upotrebe. U opte uzev, niti om oguuju pravljenje labavije povezane strukture program a, inae bi delovi program a m orali stalno da obraaju panju na zadatke koje obavljaju niti.

89 0

Misliti na Javi

Osnove vienitnog programiranja


Paralelno program iranje om oguuje deljenje program a na zasebne delove koji se izvravaju nezavisno. U vienitnom program u, svaki o d tih zasebnih zadataka (nazivaju ih i podzadaci) izvrava jedna n it izvravanja (engl. thread o f execution). N it je pojedinaan sekvencijalni tok kontrole u nutar nekog procesa. Proces m oe da sadri vie zadataka koji se paralelno izvravaju, ali program se pie kao da svaki zadatak im a CPU sam o za sebe. Postoji pozadinski m ehanizam koji svaki as prebacuje procesor s jednog zadatka na drugi, ali vi o tom e po pravilu ne treba da brinete. M odel niti je olakica za program iranje koja pojednostavljuje istovrem eni rad s nekoliko operacija u istom program u. Niti om oguavaju da procesor ne b ude trajno zauzet jednim procesom, nego da svakoj niti posveti malo vrem ena.4 Svaka nit misli da koristi procesor sama - u stvari, procesorsko vreme je podeljeno. Izuzetak od ovog pravila je izvravanje program a na raunaru s vie procesora. Ali, jedna od velikih prednosti korienja niti lei ba u tom e da ne m orate misliti o hardveru na kom e e se program izvravati, pa u kodu ne treba praviti varijante za jedan ili vie procesora. Stoga su niti sredstvo za pravljenje prenosivih program a koji se sami prilagouju platform i - ako se program izvrava presporo, lako ete ga ubrzati dodavanjem procesora u raunar. Upotreba vieprogram skog (engl. m ultitasking) operativnog sistema i istovrem eno izvravanje vie niti unutar jednog program a (engl. m ultithreading) predstavlja najbolji nain da iskoristite vieprocesorski raunar.

Definisanje zadataka
Nit izvrava jedan zadatak, pa m oram o imati naina da taj zadatak opiemo. Tome slui interfejs R unnable. Zadatak ete efinisati kada realizujete R unnable i napiete rnetodu r u n ( ) koja obavlja ono to zadatak treba da uradi. Prim era radi, naredni zadatak L ansiranje prikazuje odbrojavanje pre lansiranja:
/ / : p a ra le ln o /L a n s ira n je .ja v a / / Prim er i n t e r f e j s a Runnable. p u b l i c c la s s L a n s ir a n je implements Runnable { p ro t e c t e d i n t o d b ro ja v a n je = 10; / / Podrazumevana p r i v a t e s t a t i c i n t b ro jZ a d a tk a = 0; p r i v a t e f i n a l i n t id = bro jZ a d a tk a + + ; p u b l i c L a n s i r a n j e ( ) {) p u b l i c L a n s i r a n j e ( i n t o d b ro ja v a n je ) { t h i s . o d b ro ja v a n je = o d b r o ja v a n je ;

}
p u b lic S trin g s ta tu s () { r e t u r n "#" + i d + " ( " + ( o d b r o ja v a n je > 0 ? o d b ro ja v a n je : " L a n s i r a n j e ! ") + " ) ,
1 1

";

O v o vai k a d a siste m d e li p ro c e so rsk o v re m e (ta k o ra d i W in d o w s, na p r im e r ) . S olaris ra d i p o F IF O m o d e lu p a ra le ln o g izv rav an ja: u k o lik o se n e p ro b u d i n it s v i im p r io r ite to m , te k u a n it se izvrava sve d o k se n e z a b lo k ira ili se n e zav ri. To z n a i d a se o s ta le n iti isto g p r io r ite ta n e izv rav aju sve d o k im te k u a n it n e p re p u s ti p ro c e so r.

Poglavlje 2 1: Paralelno izvravanje

891

}
p u b l i c v o i r u n ( ) { w h i l e ( o d b r o j a v a n j e - - > 0) { S y s te m .o u t.p rin t(s ta tu s ()); T h re a d .y ie ld ();

} } } ///:Instance zadatka se ra/.likuju po pripadajuem identifikatoru id. Uz njega je rezervisana re final zato to ne oekujem o da e se p rom eniti nakon to bude inicijalizovan. M etoda r u n ( ) obino im a neku vrstu petlje koja se izvrava sve dok zadatak ne postane nepotreban, pa m orate da utvrdite uslov za izlazak iz nje. Jedna m ogunost bi bila da jednostavno izaete (vratite se) iz m etode r u n ( ). M etodu r u n ( ) esto piu u obliku beskonane petlje, to znai da e se ona izvravati u nedogled ako je neto ne prekine. (U nastavku poglavlja saznaete kako da bezbedno okonate zadatak.) Poziv statike m etode T hread.yield( ) u n u tar m etode r u n ( ) predstavlja sugestiju m eh a n izm u za raspodelu procesorskog vrem ena nitim a , engl. thread schedular (koji prebacuje procesor s jedne niti na drugu) koja kazuje: Obavio sam vane delove svog ciklusa i sada je vrem e za prebacivanje na drugi zadatak. M etoda je neobavezna (opciona), a ovde sam je upotrebio zato to u navedenim prim erim a daje zanimljivije rezultate: ona poveava verovatnou da e doi do prebacivanja s jednog zadatka na drugi. U narednom prim eru, zadatkovu m etodu r u n ( ) ne izvrava zasebna nit; nju jednostavno poziva m etoda m a in ( ). Zapravo, to jeste zasebna nit: ona koja se uvek dodeljuje metodi m a in ( ):
//: p a ra le ln o /G la v n a N it.ja v a

p u b l i c c la s s G la vn a N it { p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] a rg s) { L a n s ir a n je l a n s i r a j = new L a n s ir a n j e ( ) ; 1ansi r a j . r u n ( ) ;

}
} / * Is p is : # 0 (9 ), # 0 (8 ), # 0(7), # 0(6), #0(5), # 0 (4 ), # 0 (3 ), # 0 (2 ), # 0 ( 0 , #0(Lansi r a n j e ! ) ,

* ///:Svaka klasa izvedena iz interfejsa Runnable m ora da im a m etodu r u n ( ), ali to nije nita osobito - tim e se ne dobija naroita sposobnost vienitnog rada. N ju ete postii eksplicitnim dodeljivanjem zadatka nekoj niti.

Klasa Thread
Runnable objekat se pretvara u zadatak koji se izvrava najee tako to objekat prosledite konstruktoru klase Thread. U sledeem prim eru em o pom ou klase Thread pokrenuti izvravanje objekta tipa Lansiranje:

892

Misliti na Javi

//:

p a ra le ln o /O s n o v e N iti.ja v a

/ / N a j j e d n o s t a v n i j a u p o tr e b a k la s e Thread. p u b l i c c la s s O snoveN iti { p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] a rg s ) { Thread t = new Thread(new L a n s i r a n j e O ) ; t.s ta rtO ; S y s te m . o u t . p r in t ln ( " e k a m o na L a n s i r a n j e " ) ;

}
} / * I s p i s : (90% podudaranja) ekamo na L a n s ir a n je # 0 (9 ), # 0 (8 ), # 0 (7 ), # 0 (6 ), # 0 (5 ), # 0 (4 ), # 0 (3 ), # 0 (2 ), # 0(1), # 0 (L a n s ira n je !),

* ///:K onstruktoru klase Thread treba sam o objekat tipa Runnable. N it se inicijali/.uje pozivom m etode s ta rt( ) objekta tipa Thread, a zatim se pozivom m etode r u n ( ) objekta tipa Runnable pokree zadatak u novoj niti. Mada izgleda kao da s ta r t( ) poziva neku m etodu koja se izvrava dugo, iz ispisa rezultata moete videti - poruka ekamo na Lansiranje ispisuje se pre dovrenja odbrojavanja - da se m etoda s ta r t( ) vraa brzo. U stvari, pozvana je m etoda L ansiranje.run( ) koja se jo nije zavrila, ali poto se Lansiranje.run( ) izvrava u drugoj niti, u metodi m a in ( ) i dalje m oem o da izvravamo ostale operacije. (Ta sposobnost nije ograniena na nit m etode m a in ( ) - svaka nit m oe da pokrene druge niti.) DakJe, program izvrava dve m etode istovrem eno - m a in ( ) i Lansiranje.run( ). Kod m e tode r u n ( ) izvrava se istovrem eno sa ostalim nitim a program a. Lako je dodati jo niti za izvravanje drugih zadataka. U narednom prim eru vidite sve zadatke u uzajam no usklaenom izvravanju:5
/ / : p a r a l e l n o / J o s O s n o v a N i t i . ja v a / / Dodavanje n i t i . p u b l i c c la s s JosO sn ovaNiti { p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] a rg s) { f o r ( i n t i = 0 ; i < 5; i+ + ) new Thread(new L a n s i r a n j e ( ) ) . s t a r t ( ) ; S y s t e m . o u t . p r ir t ln ( " e k a m o na L a n s i r a n j e " ) ;

}
} / * I s p i s : ( p r im e r) ekamo na L a n s ir a n je # 0 (9 ), # 1 (9 ), # 2 (9 ), # 3 (9 ), # 4 (9 ), # 0 (8 ), # 1 (8 ), # 2(8), # 3 (8 ), #4(8), #0(7), # 1 ( 7 ) , # 2 ( 7 ) , # 3 ( 7 ) , # 4 ( 7 ) , # 0 ( 6 ) , # 1 ( 6 ) , # 2 ( 6 ) , # 3 ( 6 ) , # 4 ( 6 ) , # 0 ( 5 ) , # 1 (5 ) , # 2 (5 ), # 3 (5 ), # 4(5), # 0 (4 ), # 1 (4 ), # 2 (4 ), # 3 (4 ), # 4 (4 ), # 0 (3 ), # 1 (3 ), #2(3), # 3 (3 ), # 4 (3 ), # 0(2), # 1 (2 ), # 2 (2 ), # 3 (2 ), # 4 (2 ), # 0(1), # 1 (1 ), #2(1), # 3 (1 ), # 4 ( 1 ) , # 0 ( L a n s ir a n j e ! ) , # 1 ( L a n s i r a n j e ! ) , # 2 ( L a n s i r a n j e ! ) , # 3 ( L a n s i r a n j e ! ) , # 4(L ansi r a n j e ! ) ,

* ///:U o v o m s lu a ju , je d n a n it - m a in ( ) - p ra v i sve n iti za Lansiranje. M e u tim , u k o lik o im a te vie n iti k o je p ra v e n iti za Lansiranje, v ie o b je k a ta tip a Lansiranje n io e in ia ti isti id. R azlog za to saz n a e te u n a s ta v k u p o g lav lja.

Poglavlje 21: Paralelno izvravanje

893

Ispis rezultata pokazuje da je izvravanje raznih zadataka izmeano kako se procesor prebacuje s jedne niti na drugu. To prebacivanje autom atski kontrolie m ehanizam za raspodelu vrem ena nitim a. Ako raunar im a vie procesora, m ehanizam za raspodelu e te niti (za korisnika nevidljivo) raspodeliti procesorim a.6 Rezultati jednog izvravanja ovog program a nee biti isti kao rezultati drugog, poto m ehanizam za raspodelu procesorskog vrem ena nitim a nije determ inistiki. U stvari, razliite verzije JDK prave velike razlike u rezultatim a ovog jednostavnog program a. Na prim er, jedna od ranijih JDK nije prebacivala procesor ba esto, pa se deavalo da nit 1 zavri svoju petlju, zatim nit 2 proe kroz sve svoje petlje itd. To je bilo isto kao poziv rutine koja sekvencijalno izvrava jednu petlju za drugom , sem to je pokretanje niti skuplje. Kasnije su JDK bolje delile procesorsko vreme, pa su sve niti bivale redovnije opsluivane. Sun po pravilu nije ni spom injao takve prom ene u ponaanju JDK, pa ne moete raunati na bilo kakvo dosledno ponaanje vienitnog m ehanizm a. Najbolje e biti da prilikom pisanja vienitnog koda ne pretpostavljate nita o ponaanju niti. Kada funkcija m a in ( ) napravi niti (objekte klase T h read ), ona nigde ne p am ti njihove reference. O bini objekti bi bili proglaeni za smee, ali to ne vai za niti. Svaka nit se ,,registruje tako da negde postoji njena referenca i skuplja smea ne moe da je ukloni dok zadatak ne izae iz svoje m etode r u n ( ) i ne um re. Iz ispisa rezultata vidite da se zadaci zaista izvravaju sve do svog zavretka. Dakle, svaka nit pravi zasebnu nit izvravanja koja postoji i nakon zavretka m etode s t a r t ( ). Veba 1: (2) Realizujte interfejs R unnable. U m etodi r u n ( ) ispiite neku poru k u i zatim pozovite y ie ld ( ). Ponovite to trip u t i zatim izaite (vratite se) iz m etode r u n ( ). U konstruktor stavite p o ru ku koja se ispisuje prilikom pokretanja, a odgovarajuu poruku ispiite i kada se zadatak izvri. Napravite vie takvih zadataka i izvrite ih pom ou niti. Veba 2: (2) Po uzoru na program genericki/Fibonacci.java, napiite zadatak koji proizvodi sekvencu od n Fibonaccijevih brojeva, pri em u se n prosleuje konstruktoru zadatka. N apravite vie takvih zadataka i izvrite ih pom ou niti.

Upotreba izvrilaca (interfejsa Executor)


Java SE5 u paketu jav a.u til.co n c u rren t sadri klasu Exccutors , tj. izvrioce koji pojednostavljuju paralelno program iranje tako to iniciraju izvravanje niti (objekata tipa T h read ) i upravljaju njime. Izvrioci obezbeuju sloj indirekcije izm eu klijenta i izvravanja zadatka; um esto da klijent izvrava zadatak neposredno, njega izvrava posredniki objekat, tj. izvrilac (engl. executor). Klasa E xecutors om oguuje da upravljam o izvravanjem asinhronih zadataka, a da ne m oram o eksplicitno da upravljam o ivotnim ciklusom niti. Upravo su izvrioci preporueni nain pokretanja zadataka u Javi SE5/6. U m esto da u program u JosO snovaN iti.java niti pravim o eksplicitno, m oem o pustiti izvrioca da se stara o svemu. Objekat tipa L ansiranje um e da izvri odreeni zadatak; kao i projektni obrazac C o m m a n d (Kom anda), on eksponira sam o jednu m etodu za izvravanje. Interfejs ExecutorService (koji proiruje interfejs E xecutor tako to dodaje m etode za ceo ivotni ciklus usluge, to znai da zna npr. i da se ugasi - engl. shutdow n) um e da napravi kontekst podesan za izvravanje R unnable objekata. U narednom
U p rv irn v e rz ija m a Jave n ije b ilo tak o .

894

Misliti na Javi

prim cru, klasa C achedT hreadP ool pravi po jed n u nit za svaki zadatak. O bratite panju na to da se objekat tipa ExecutorService pravi statikom m etodom klase Executors; ona odreuje koja e vrsta izvrioca biti napravljena:
/ / : p a ra le ln o /C a c h e d T h re a d P o o l. ja v a im p o r t j a v a . u t i l . c o n c u r r e n t . * ; p u b l i c c la s s CachedThreadPool { p u b l i c s t a t l c vo id m a i n ( S t r i n g [ ] a rg s ) { E x e cu to rS e rv ic e exec = Executors.newCach edThreadPool( ) ; f o r ( i n t i = 0 ; i < 5 ; i+ +) exe c.execute(new L a n s i r a n j e ( ) ) ; exec.shutdown();

}
} / * I s p i s : ( p r im e r) # 0 (9 ), # 0 (8 ), # 1 (9 ), # 2 (9 ), # 3 (9 ), # 4 (9 ), # 0 (7 ), # 1 (8 ), # 2 (8 ), # 3 (8 ), # 4(8), # 0 (6 ), # 1 (7 ), # 2 (7 ), # 3 (7 ), # 4 (7 ), # 0 (5 ), # 1 (6 ), # 2 (6 ), # 3 (6 ), # 4 (6 ), # 0(4), # 1 (5 ), # 2 (5 ), # 3 (5 ), # 4 (5 ), # 0 (3 ), # 1 (4 ), # 2 (4 ), # 3 (4 ), # 4(4), # 0 (2 ), # 1 (3 ), # 2(3), # 3 (3 ), # 4 (3 ), # 0 (1 ), # 1 (2 ), # 2 (2 ), # 3 (2 ), # 4 (2 ), # 0 (L a n s ir a n je !) , # 1(1), #2(1), # 3 (1 ), # 4 (1 ), # l ( L a n s i r a n j e ! ) , # 2 (L a n s ira n je !) , # 3 (L a n s ira n je !), # 4 (L a n s ira n je !) ,

* ///:Veoma esto, sam o jednim izvriocem m oete da napravite sve zadatke u sistemu i da svima njim a upravljate. Poziv m etode s h u td o w n () spreava dodeljivanje novih zadataka tom izvriocu. Tekua nit - u ovom sluaju, ona koja izvrava m a in ( ) - nastavie da izvrava sve zadatke prijavljene pre poziva m etode s h u td o w n ( ). Program e prekinuti izvravanje im se zavre svi zadaci u tom izvriocu. C achedT hreadP ool iz prethodnog prim era lako m oete zam eniti nekom drugom vrstom izvrioca. Za izvravanje prijavljenih zadataka, F ixedT hreadPool koristi ogranien skup niti:
/ / : p a r a le ln o / F i x e d T h r e a d P o o l. j a v a im p o rt j a v a . u t i l . c o n c u r r e n t . * ; p u b l i c c la s s FixedThreadPool { p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] a rg s) { / / Argument k o n s t r u k t o r a j e b r o j n i t i : E x e c u to rS e rv ic e exec = E x e c u to rs.n e w F ix e d T h rea d P oo l( 5 ) ; f o r ( i n t i = 0; i < 5; i++) e xec.execute(new L a n s i r a n j e O ) ; exec.shutdow n();

}
} / * Is p is : ( p r im e r)

Poglavlje 21: Paralelno izvravanje

895

# 0 (9 ), # 0(8), # 1(9), # 0(6), # 1(7), # 2 (7 ), # 1 ( 5 ) , # 2 (5 ) , # 3 ( 5 ) , # 2 ( 3 ) , # 3 (3 ) , # 4 ( 3 ) , # 1 ( 1 ) , # 2 (1 ) , # 3 ( 1 ) , # 4 (L a n s ira n je !),

# 2(9), # 3(7), #4(5), # 0(1), # 4(1),

# 3 ( 9 ) , # 4 ( 9 ) , # 0 ( 7 ) , # 1 ( 8 ) , # 2 (8 ) , # 3 ( 8 ) , # 4 (8 ) , # 4 ( 7 ) , # 0 ( 5 ) , # 1 ( 6 ) , # 2 ( 6 ) , # 3 ( 6 ) , # 4 ( 6 ) , # 0 (4 ) , # 0 ( 3 ) , # 1 ( 4 ) , # 2 ( 4 ) , # 3 ( 4 ) , # 4 ( 4 ) , # 0 (2 ) , # 1 (3 ) , # 1(2), #2(2), # 3 (2 ), # 4 (2 ), # 0 (L a n s ira n je !), # l( L a n s ir a n je !) , # 2 (L a n s ira n je !) , # 3 (L a n sira nje !),

* ///:Za FixedThreadPool se skupo dodeljivanje niti obavlja samo jednom na poetku - im e se broj niti ograniava. tedi se vreme, zato to se niti ne prave stalno za svaki zadatak. Pored toga, u sistem u voenom dogaajim a, rukovaoci dogaajem (engl. even t handlers) kojim a zatrebaju niti m ogu biti odm ah opslueni tako to jednostavno uzim aju niti iz grupe (engl. pool). Ne m oete preterati s korienjem resursa zato to FixedThreadPool upotrebljava ogranien broj objekata tipa Thread. Im ajte u vidu da se postojee niti u svim grupam a autom atski ponovo upotrebljavaju im to postane mogue. Iako u ja u ovoj knjizi upotrebljavati neograniene grupe niti (CachedThreadPooI), razmislite o korienju ogranienih grupa (FixedThreadPooI) u kodu koji se isporuuje kupcim a. CachedThreadPool obino pravi onoliko niti koliko m u je potrebno tokom izvravanja program a, a zatim prestaje da pravi nove niti zato to reciklira stare; stoga je najbolje da on bude izabran kao prvi izvrilac. Na FixedThreadPool treba da preete tek ako prethodni pristup napravi problem e. SingleThreadExecutor je kao FixedThreadPool ija je veliina ograniena na jednu nit. To je podesno za kontinualne (dugotrajne) zadatke, kao to je zadatak koji oslukuje prikljuke ulaznih utinica. Prikladno je i za kratke zadatke koje elite da izvrite u niti - recimo, male zadatke koji auriraju lokalni ili udaljeni dnevnik (engl. log) ili za nit koja rasporeuje dogaaje. Kada se izvriocu tipa SingIeThreadExecutor prijavi vie zadataka, oni se smetaju u red za ekanje i svaki zadatak se poziva tek kada prethodni p o tp u n o zavri rad, a svi koriste istu nit. U narednom prim eru, videete da svaki zadatak, po redosledu prijavljivanja, biva dovren pre otpoinjanja sledeeg. Dakle, SingleThreadExecutor serijalizuje zadatke koji m u se prijave i odrava sopstveni (skriveni) red zadataka koji ekaju na izvrenje.
//: paralelno/Si ng l e T h r e a d E x e c u t o r .java import ja v a . u t i 1 .c o n c u r r e n t .*; public class SingleThreadExecutor { public static void m a i n ( S t r i n g [] args) ExecutorService exec = Executors,newSi n g l e T h re ad Ex ec ut or (); for(int i = 0; i < 5; i++) exec.execute(new Lansir an je ()); e x e c . s hu td ow n( ); {

Icm i i n e to to o sta li iz v rio c i n e m o g u - n e m o e se d e siti d a d v a z a d a tk a p o z o v e n a p a ra le ln o iz v rav a n je . T im e se m e n ja ju z ah te v i za z a k lju a v a n je z a d a ta k a (o k o jim a u g o v o riti u n a sta v k u p o g la v lja ).

896

Misliti na Javi

}
} / * Is p is : # 0 (9 ), # 0(8), #0(7), # 0 (6 ), # 0 (5 ), # 0 (4 ), # 0 (3 ), # 0 (2 ), # 0 (1 ), # 0 (L a n s ir a n je !) , # 1(9), # 1 (8 ), # 1 (7 ), # 1 (6 ), # 1 (5 ), # 1 (4 ), # 1 (3 ), #1(1), # l( L a n s ir a n je !) , # 2 (9 ), # 2 (8 ), # 2 (7 ), # 2 (6 ), # 2 (5 ), # 2 (4 ), # 2 (2 ), # 2(1), # 2 ( L a n s ir a n je !) , # 3 (9 ), # 3 (8 ), # 3 (7 ), # 3 (6 ), # 3 (5 ), # 3 (3 ), # 3(2), #3(1), # 3 ( L a n s ir a n je ! ) , # 4 (9 ), # 4 (8 ), # 4 (7 ), # 4 (6 ), # 4(4), # 4(3), # 4(2), # 4 (1 ), # 4 (L a n s ir a n je !) ,

# 1(2), # 2(3), # 3(4), #4(5),

* ///:Kao drugi primer, pretpostavim o da vie niti izvrava zadatke koji koriste sistem datoteka. Za te zadatke m oete u potrebiti izvrilac tipa SingleThreadExecutor koji jem i da se u svakom trenutku u svakoj niti izvrava sam o po jedan zadatak. U tom sluaju ne m orate da se bavite sinhronizovanjem deljenog resursa (a ni sistem datoteka neete zabrljati u m eduvrem enu). Ponekad je bolje reenje sinhronizovanje resursa (o em u ete vie saznati u nastavku poglavlja), ali SingleThreadExecutor om oguuje da preskoite pravilnu koordinaciju kada elite sam o da napravite neki prototip. Serijalizacijom zadataka moete da uklonite potrebu za serijalizacijom objekata.

Veba3: (1) Ponovite vebu 1 s raznim vrstam a izvrilaca predstavljenim u ovom odeljku. Veba 4: ( 1) Ponovite vebu 2 s raznim vrstam a izvrilaca predstavljenim u ovom odeljku.

Dobijanje povratnih vrednosti od zadataka


Zadatak koji realizuje interfejs Runnable moe da obavi posao, ali ne m oe da vrati rezultat. Ukoliko elite da zadatak vrati neki rezultat kada zavri s radom , realizujte interfejs Callable, a ne Runnable. Callable je novina u Javi SE5; to je generiki interfejs iji param etar tipa predstavlja povratnu vrednost m etode c a ll( ) - a ne r u n ( ). M orate ga pozvati m etodom su b m it( ) interfejsa ExecutorService. Sledi jednostavan prim er:
/ / : p a r a l e l no/Prim erZaCal1a b l e . j a v a im p o rt j a v a . u t i l . c o n c u r r e n t . * ; im p o rt j a v a . u t i l . * ; c la s s ZaatakSRezultatom implements C a n a b l e < S t r i n g > { p riv a te in t id ; p u b l i c Z ad a ta kS R e zu lt a to m (in t i d ) { t h is . id = id ;

}
p u b l i c S t r i n g c a l l () { r e t u r n " r e z u l t a t ZadatkaSRezultatom " + i d ;

} }
p u b l i c c la s s PrimerZaCal1a b le { p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] args) { E x e cu to rS e rv ic e exec = Executors.newCachedThreadPool( ) ; A rra y L is t< F u tu re < S trin g r e z u lt a t i =

Poglavlje 2 1: Paralelno izvravanje

897

new A r r a y L i s t < F u t u r e < S t r i n g ( ) ; f o r ( i n t i = 0 ; i < 10; i+ + ) rezu lta ti.a d d (e x e c .s u b m it(n e w Z ad a ta kS R e zu lta to m (i))); fo r(F u tu re < S trin g > fs : r e z u l t a t i ) try { / / g e t ( ) b l o k i r a do d o v r e n ja : S y s te m .o u t.p rin tln (fs .g e t()); } c a t c h ( I n t e r r u p t e d E x c e p t i o n e) { S y s te m .o u t.p rin tln (e ); retu rn ; } c a tc h (E x e c u t io n E x c e p t io n e) { S y s te m .o u t.p rin tln (e ); } fin a lly { exec.shutdown();

} }
} / * Is p is : r e z u l t a t ZadatkaSRezultatom r e z u l t a t ZadatkaSRezultatom r e z u l t a t ZadatkaSRezultatom r e z u l t a t ZadatkaSRezultatom r e z u l t a t ZadatkaSRezultatom r e z u l t a t ZadatkaSRezultatom r e z u l t a t ZadatkaSRezultatom r e z u l t a t ZadatkaSRezultatom r e z u l t a t ZadatkaSRezultatom r e z u l t a t ZadatkaSRezultatom 0 1 2 3 4 5 6 7 8 9

* ///:M etoda s u b m it( ) proizvodi objekat tipa Future, param etrizovan za odreeni tip rezultata koji vraa taj objekat tipa Callable. O bjekat tipa Future moete ispitivati metodom isD o n e( ) kako biste videli da li je zavrio rad. Kada zadatak zavri s radom i ima rezultat, pozovite m etodu g e t( ) da biste ga saznali. Ukoliko g e t( ) pozovete bez prethodne provere zavrenosti m etodom isD o ne( ), g e t( ) e se blokirati sve dok rezultat ne bude sprem an. g e t( ) m oete pozvati i uz vrem ensko odlaganje (engl. tim eout), ili pozovite isD o n e( ) kako biste videli da li je zadatak zavrio s radom pre nego to pozovete g e t( ) da pribavi njegov rezultat. Preklopljena m etoda Executors.caIlable( ) prim a objekat koji realizuje interfejs Runnable i proizvodi objekat tipa Callable. ExecutorService ima ,,pozivajue m etode koje izvravaju kolekcije Callable objekata.

Veba 5: (2) Izmenite vebu 2 tako da zadatak realizuje interfejs Callable i sabira sve Fibonaccijeve brojeve. Napravite nekoliko zadataka i prikaite rezultate.

Spavanje
M etodom sleep ( ) prekidate (blokirate) izvravanje zadatka na odredeno vreme. Ako u klasi Lansiranje zam enite poziv m etode y ie ld ( ) pozivom m etode sleep( ), dobiete ovo:

898

Misliti na Javi

/ / : p a r a le ln o / Z a d a t a k K o jiS p a v a . ja v a / / Prekidam iz v r a v a n je zadatka / / na odreeno vreme metodom s l e e p ( ) . im p o rt j a v a . u t i l . c o n c u r r e n t . * ; p u b l i c c la s s ZadatakKojiS pava extends L a n s ir a n je { p u b l i c v o id r u n ( ) { try { w h i l e ( o d b r o j a v a n j e - - > 0) { S y s te m .o u t.p rin t(s ta tu s ()); / / S t a r i n a in : / / T h r e a d .s le e p ( lO O ) ; / / Nain Jave SE5/6: TimeUnit.MILLISECONDS.sleep(lOO);

}
} c a t c h ( I n t e r r u p t e d E x c e p t i o n e) { Syste m .e rr.p ri n tln (" P re k i n u t " ) ;

} }
p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] a rg s) { E x e c u to rS e rv ic e exec = Executors.newCachedThreadPool( ) ; f o r ( i n t i = 0; i < 5; i+ + ) e xec,execute(new ZadatakKoj i Spava( ) ) ; exec.shutdow n();

}
} / * Is p is : # 0 (9 ), # 1 (9 ), # 2 (9 ), # 3 (9 ), # 4 (9 ), # 0(8), # 1 (7 ), # 2 (7 ), # 3(7), # 4(7), # 0 (6 ), # 1 (6 ), # 2(5), # 3(5), #4(5), # 0(4), # 1 (4 ), # 2 (4 ), # 3 (3 ), # 4 (3 ), # 0(2), # 1 (2 ), # 2 (2 ), # 3 (2 ), # 4 (1 ), # 0 (L a n s ira n je !) , #1( L a n s ir a n je !) , #4(Lansi r a n j e ! ) , # 1(8), # 2 (8 ), # 3 (8 ), # 4(8), # 0(7), # 2 (6 ), # 3 (6 ), # 4 (6 ), # 0 (5 ), #1(5), # 3(4), # 4 (4 ), # 0(3), # 1(3), #2(3), # 4(2), # 0 (1 ), # 1(1), # 2(1), #3(1), #2(L a n s ira n je !) , # 3 (L a n s ira n je !),

* ///:Poziv m etode s le e p () m oe da baci izuzetak Interru p ted E x cep tio n koji se hvata u m etodi r u n ( ). Poto se izuzeci ne prostiru preko granica niti do m etode n ia in ( ), m orate lokalno da obradite sve izuzetke koji nastanu u nu tar zadatka. Java SE5 je donela i eksplicitniju verziju m etode sle e p () kao deo klase Tim eU nit, to je prikazano u prethodnom prim eru. O na poboljava itljivost tako to om oguuje zadavanje vrem enskih jedinica za m etodu s le e p (). T im eU nit se moe upotrebiti i za konverzije, to ete videti u nastavku poglavlja. U zavisnosti od platform e, m oda ete videti da se zaaci izvravaju po savrenom redosledu - nulti do etvrtog, pa opet nulti. To ima smisla zato to nakon svake naredbe p r in t svaki zadatak ide na spavanje (blokira se), a to m ehanizm u za raspodelu procesorskog vrem ena nitim a daje priliku da procesor prebaci na drugu nit koja izvrava neki drugi zadatak. To sekvencijalno ponaanje om oguuje pripadni m ehanizam vienitnog rada koji se m enja u zavisnosti od operativnog sistema, pa na to ne moete da raunate. Ukoliko m orate da kontroliete redosle izvravanja zadataka, najbolje ete proi ako

Poglavlje 21: Paralelno izvravanje

899

upotrebite kontrole za sinhronizovanje (opisane u nastavku) ili, u nekim sluajevima, ukoliko uopte ne budete upotrebljavali niti, nego sami napiete ru tin e koje jedna drugoj preputaju kontrolu po zadatom redosledu. Veba 6: (2) N apravite zadatak koji spava tokom nasum ino odabranog vrem ena dugakog izm eu 1 i 10 sekundi, a zatim prikazuje to vreme spavanja i prekida rad. N apravite i pokrenite vie takvih zadataka. (Neka se njihov broj zadaje na kom andnoj liniji.)

Prioritet
P rioritet niti pokazuje koliko je ona vana m ehanizm u za raspodelu procesorskog vrem e-

na. Iako je redosled kojim CPU izvrava skup niti neodreen, m ehanizam za raspodelu procesorskog vrem ena daje prednost niti najvieg p rioriteta koja eka na izvravanje. To ne znai da se niti nieg prioriteta uopte ne izvravaju. (Dakle, prioriteti ne m ogu da prouzrokuju zastoj u izvravanju). M ehanizam za raspodelu procesorskog vrem ena ree pokree niti nieg prioriteta i to je sva razlika. Gotovo uvek bi sve niti trebalo da se izvravaju s podrazum evanim prioritetom . Runo m enjanje prioriteta najee nije opravdano. Sledi prim er s razliitim nivoim a prioriteta. Prioritet postojee niti itam o m etodom g e tP rio rity ( ), a zadajemo (kad god poelim o) m etodom setP rio rity ( ).
/ / : p a r a l e l n o / P r o s t i P r i o r i t e t i . ja v a / / Prim e r k o r i e n ja p r i o r i t e t a n i t i . im p o r t j a v a . u t i l . c o n c u r r e n t . * ; p u b l i c c la s s P r o s t i P r i o r i t e t i implements Runnable { p r i v a t e i n t o d b ro ja v a n je = 5; p r i v a t e v o l a t i l e double d; / / Bez o p t i m i z a c i j e p riv a te in t p r i o r it e t ; p u b lic P r o s t i P r i o r i t e t i ( i nt p r i o r i t e t ) { th is .p rio rite t = p rio rite t;

}
p u b lic S trin g to S tr in g ( ) { r e t u r n T h r e a d .c u r r e n t T h re a d () + " : " + o d b r o ja v a n je ;

}
p u b l i c v o id r u n ( ) { T h re a d .c u rre n tT h re a d ().s e tP rio rity (p rio rite t); w h ile (tru e ) { / / Skupa o p e r a c ija koja se moe p r e k i n u t i : f o r ( i n t i = 1; i < 100000; i+ + ) { d += (Math.PI + Math.E) / ( d o u b l e ) i ; i f ( i % 1000 == 0) Thread, y i e l d ( ) ;

}
S y s te m .o u t.p rin tln (th i s ) ; i f ( - - o d b r o j a v a n j e == 0) r e t u r n ;

} }
p u b l i c s t a t i c v o id m a i n ( S t r i n g [ ] args) {

900

Misliti na Javi

ExecutorService exec = Executors.newCachedThreadPool(); for(int i = 0 ; i < 5 ; i++) exec.execute( new ProstiPrioriteti(Thread.MIN_PRIORITY)); exec.execute( new ProstiPrioriteti(Thread.MAX_PRIORITV)); exec.shutdown();

}
} /* Ispis: (70% podudaranja) Thread[pool-l-thread-6,10,main]: 5 Thread[pool-l-thread-6,10,main]: 4 Thread[pool-l-thread-6,10,main]: 3 Thread[pool-l~thread-6,10,main]: 2 Thread[pool-l-thread-6,10,main]: 1 Thread[pool-l-thread-3,l,main]: 5 Thread[pool-l-thread-2,l,main]: 5 Thread[pool-l-thread-l,l,main]: 5 Thread[pool-l-thread-5,l,main]: 5 Thread[pool-l-thread-4,l,main]: 5

* ///:M etoda to S trin g ( ) je preklopljena tako da poziva T hread.toString( ) koja ispisuje ime niti, njen nivo prioriteta i gru p u kojoj nit pripada. Ime niti moete i sami zaati preko konstruktora; ovde se ono autom atski generie u obliku po o l-l-th read -l, pool-1-thread-2 itd. Preklopljena m etoda to S trin g ( ) prikazuje i iznos odbrojavanja za zadatak. Vodite rauna o tom e da m etodom T h read.currentT hread( ) iz tog zadatka moete dobiti referencu niti (objekta tipa Thread) koja izvrava zadatak. Iz rezultata vidite da je nivo prioriteta poslednje niti najvii i da su sve ostale niti na najniem nivou. O bratite panju na to da se prioritet postavlja na poetku izvravanja m etode r u n ( ); nem a srnisla postavljati ga u konstruktoru, poto Executor (izvrilac) u tom trenutku jo nije otpoeo zadatak. U metodi r u n ( ) 100 000 puta se obavlja prilino skupo izraunavanje u form atu pokretnog zareza koje obuhvata sabiranje i deljenje tipa double. Promenljiva d je dobila modifikator volatiJe da prevoilac ne bi sprovodio optimizaciju. Ne bism o videli uticaj zadavanja nivoa prioriteta da nem a tog izraunavanja. (Probajte: pretvorite u kom entar petlju for koja sadri double izraunavanja.) Uz izraunavanje, vidite da je mehanizam za raspodelu procesorskog vrem ena ee pozivao nit najveeg prioriteta (MAX_PRIORITY). (Tako se ponaao barcm na Windows XP raunaru.) Iako je i ispisivanje na konzoli skupa operacija, ono ne om oguuje da vidim o uticaj razliitih nivoa prioriteta, poto se ono ne prekida (u protivnom bi se konzolni ispis pokvario pri prebacivanju s jene niti na drugu), dok se matem atika izraunavanja m ogu prekinuti. Izraunavanje traje dovoljno dugo da m e hanizam za raspodelu procesorskog vrem ena uskae, zamenjuje zadatke i pri tom pazi na prioritete, tako da se niti visokog prioriteta izvode ee. Metoda yield( ) se redovno poziva da bi se izazvalo prebacivanje konteksta. JDK im a 10 nivoa prioriteta, ali se oni ne preslikavaju dobro u svim operativnim sistem im a. Prim era radi, W indows ima 7 nivoa prioriteta koji nisu fiksni, pa je preslikavanje

Poglavlje 2 1: Paralelno izvravanje

901

(onih 10 na ovih 7) neodreeno. Sunov Solaris im a 231nivoa. Jedini pristup koji je prenosiv na sve platform e jeste drati se MAX_PRIORITY, NORM_PRIORITY i MIN_PRIORITY prilikom podeavanja nivoa prioriteta.

Preputanje
Ukoliko znate da ste uradili ono to je trebalo tokom jednog prolaska kroz petlju u m etodi r u n ( ), m ehanizm u za raspoelu procesorskog vrem ena nitim a m oete saoptiti da je tekua n it uradila dovoljno i predloiti m u da procesor dodeli nekom drugom zadatku. Taj predlog (to i jeste sam o predlog, poto nem a jem stva da e ga realizacija posluati) im a oblik m etode y ie ld ( ). Kada pozovete y ie ld ( ), predlaete prelazak na izvravanje drugih niti istog prioriteta. Lansiranje.java m etodom y ield ( ) dobro raspodeljuje obradu na razne zadatke program a Lansiranje. U m etodi Lansiranje. r u n ( ) pozive Thread.yield( ) pretvorite u kom entare, pa ete videti razliku. M eutim , za ozbiljnu kontrolu ili podeavanje aplikacije po pravilu se ne moete osloniti na y ield ( ). ta vie, esto se y ield( ) pogreno upotrebljava.

Servisne niti
Servisne niti (engl. daem on threads) obezbeuju neke usluge nam enjene program u u celini i izvravaju se u pozadini tokom rada program a, ali ne spadaju u njegove sutinske delove. Zato se program zavrava im zavre s radom sve obine niti. Vai i obratno: ako jo ima obinih niti koje se izvravaju, program nastavlja rad - na prim er, ako postoji nit koja izvrava m a in ( ).
//: paralelno/JednostavneServisneNiti.java // Serv is ne niti ne spreavaju da se program okona. import ja v a . u t i 1 .concurrent import static n e t .mindview.uti1 .P r i n t .*;

public class JednostavneServisneNiti implements Runnable { public void run() { try { while(true) { Timellni t .MILLISECONDS .sl eep(lOO); print(Thread.currentThread() + " " + this);

)
} catch(InterruptedException e) { print("sleep() prekinuta");

} }
public static void main(Strin g [] args) throws Exception { for(int i = 0; i < 10; i++) { Thread servisnanit = new Thread(new JednostavneServisneNiti()); servisnanit.setDaemon(true); // Mora biti pozvana pre metode start() servi snani t .start ();

}
print(Sve servisne niti pokrenute");

902

Misliti na Javi

T imeUn it .MILLIS ECONDS.sl eep(175);

}
} /* Ispis: (primer) Sve servisne riiti pokrenute Thread[Thread-0,5,main] JednostavneServisneNiti@530daa Thread[Thread-l,5,main] JednostavneServisneNiti@a62fc3 Thread[Thread-2,5,main] JednostavneServisneNiti@89ae9e Thread[Thread-3,5,main] JednostavneServisneNiti@1270b73 Thread[Thread-4,5,main] JednostavneServisneNiti@60aeb0 Thread[Thread-5,5,main] Thread[Thread-6,5,main] Thread[Thread-7,5,main] Thread[Thread-8,5,main] Thread[Thread-9,5,main] JednostavneServisneNiti@16caf43 JednostavneServisneNiti066848c JednostavneServisneNiti08813f2 JednostavneServisneNiti@ld58aae JednostavneServisneNiti@83cc67

*///:-

Pre pokretanja m etodom s ta r t( ), nit m orate m etodom setD aem on( ) najpre pretvoriti u servisnu (naterati je da se izvrava u pozadini). Program nem a ta da radi kada m a in ( ) zavri svoj posao, jer se tada ne izvrava nita drugo sem servisnih niti. Stavio sam m a in ( ) nakratko na spavanje da biste videli posledice pokretanja svih servisnih niti. Bez toga biste videli samo neke rezultate pravljenja niti koje se izvravaju u pozadini. (Isprobajte pozive m etode sleep ( ) raznih duina da biste videli to ponaanje.) Program JednostavneServisneNiti.java pravi eksplicitne Thread olijekte kako bi mogao da ih pretvori u servisne postavljanjem odgovarajueg indikatora. A tribute niti (da li je servisna, njen prioritet, ime) napravljenih pom ou izvrioca moete prilagoditi pisanjem nam enske klase ThreadFactory:
//: net/mi ndview/uti1/DaemonThreadFactory.java package net.mindview.util; import java.uti1 .concurrent.*; public class DaemonThreadFactory implements ThreadFactory { public Thread newThread(Runnable r) { Thread t = new Thread(r); t.setDaemon(true); return t;

} } / / / =' Jedina razlika od obine ThreadFactory (proizvodne niti) jeste to to ova zadaje vrednost tru e za status servisne niti. Nov objekat tipa D aemonThreadFactory sada m oem o proslediti kao argum ent m etodi Executors.newCachedThreadPool( ):
//: paralelno/DaemonThreadFactory.java // Pravljenje servisnih niti pomou ThreadFactory. import java.uti1 .concurrent.*; import net.mindview.util.*;

Poglavlj'e 2 1 : Paralelno izvravanje

903

import static net.mindview.util.Print.*; public class DaemonThreadFactory implements Runnable { public void run() { try { while(true) { TimeUnit.MILLISECONDS.sl eep(lOO); print(Thread.currentThread() + " " + this);

}
} catch(InterruptedException e) { pri nt(Preki nuto");

} }
public static void main(String[] args) throws Exception { ExecutorService exec = Executors.newCachedThreadPool( new DaemonThreadFactory()); for(int i = 0; i < 10; i++) exec.execute(new DaemonThreadFactory()); print("Sve servisne niti pokrenute"); TimeUnit.MILLISECONDS.sleep(500); // Neka radi neko vreme

}
} /* (Pokrenite da biste videli rezultat) *///:-

Svaka od statikih ExecutorService m etoda za pravljenje preklopljena je tako da prima objekat tipa ThreadFactory pom ou kojega e praviti nove niti. M oem o to podii za jedan stepen vie i napraviti uslunog izvrioca DaemonThreadPooIExecutor za pravljenje servisnih niti:
//: net/mi ndview/uti1/DaemonThreadPoolExecutor.java package net.mindview.util; import java.uti1 .concurrent.*; public class DaemonThreadPoolExecutor extends ThreadPoolExecutor { public DaemonThreadPoolExecutor() { super(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>(), new DaemoriThreadFactory ());

} } ///:A rgum ente za poziv konstruktora osnovne klase pronaao sam u izvornom kodu klase

Executors.java.
Pozivanjem m etode isD aem on( ) saznaete da li je data nit servisna (da li se izvrava u pozadini). Ako se neka nit izvrava u pozadini, to autom atski vai i za sve niti koje ona napravi, kao to se vidi iz sledeeg prim era:
//: paralelno/ServisneNiti.java // Servisna nit raa druge servisne niti.

904

Misliti na Javi

import java.util.concurrent.*; import static net.mindview.util.Print.*; class ServisnaNit implements Runnable { private Thread[] t = new Thread[10]; public void run() { for(int i = 0; i < t.length; i++) { t[i] = new Thread(new MajkaServisnihNiti()); t[i].start(); printnb("MajkaServisnihNiti " + i + " pokrenuta, ");

}
for(int i = 0; i < t.length; i++) printnb("t[" + i + "].isDaemon() = " + t[i] .isDaemon() + ", "); while(true) Thread.yield();

} }
class MajkaServisnihNiti implements Runnable { public void run() { while(true) Thread.yield();

} }
public class ServisneNiti { public static void main(String[] args) throws Exception { Thread d = new Thread(new S e r v i s n a N i t O ) ; d.setDaemon(true); d.start(); printnb("d.isDaemon() = " + d.isDaemon() + ", "); // Pusti servisne niti da dovre svoje // procese pokretanja: TimeUnit.SECONDS.sleep(l);

}
} /* Ispis: (primer) d.isDaemon() = true, MajkaServisnihNiti 0 pokrenuta, MajkaServisnihNiti 1 pokrenuta, MajkaServisnihNiti 2 pokrenuta, MajkaServisnihNiti 3 pokrenuta, MajkaServisnihNiti 4 pokrenuta, MajkaServisnihNiti 5 pokrenuta, MajkaServisnihNiti 6 pokrenuta, MajkaServisnihNiti 7 pokrenuta, MajkaServisnihNiti 8 pokrenuta, MajkaServisnihNiti 9 pokrenuta, t [0].isDaemon() = true, t[l],isDaemon() = true, t[2] .isDaemon() = true, t[3].isDaemon() = true, t [4].isDaemon() = true, t [5].isDaemon() = true, t [6].isDaemon() = true, t [7].isDaemon() = true, t[8].isDaemon() = true, t[9].isDaemon() = true,

* ///:-

Poglavlje 21: Paralelno izvravanje

905

ServisnaNit ukljuuje svoj indikator servisne niti i zatim pravi gom ilu drugih niti kako bi pokazala da su i one servisne, iako nisn eksplicitno pretvorene u servisne. Zatim ServisnaNit ide u beskonanu petlju koja poziva m etodu y ield ( ) da bi predala kontrolu drugim procesima. Im ajte u vidu injenicu da servisne niti ne izvravaju odredbe finally prilikom okonanja svojih m etoda r u n ( ):
//: pa ra le lno/ServisneNitiNeIzvrsavajuOdrebeFinally.java // Servisne niti ne izvravaju odredbe finally import java.util.concurrent.*; import static ne t. mi nd vi ew .u ti1 . P r i n t .*; class ServisnaNitA implements Runnable { public void run() try { pri nt("Kree Servi sn aN it A" ); TimeLlnit.SECONDS.sleep(l); } catch(InterruptedException e) { p r i n t ("Iz1azak pomou In te rr up te dE xc ep ti on" ); } finally { print("0vo treba stalno da se izvrava?"); {

public class Se rv is neNitiNelzvrsavajuOdredbeFinally { public static void m a i n (S tr in g[] args) throws Exception {

Thread t = new Thread(new ServisnaNitA()); t .setDaemon(true); t .start();

}
} /* Ispis: Kree ServisnaNitA

* ///:Kada pokrenete ovaj program , videete da se odredba finally ne izvrava, ali ako poziv m etode s e tD a e m o n () pretvorite u kom entar, videete da se odredba finally izvrava. Takvo ponaanje je ispravno, iako ga m oda ne oekujete na osnovu prethodnih obeanja u vezi sa odredbom finally. Servisne niti bivaju naglo prekinute kada prestane izvravanje poslednje neservisne niti. Dakle, im m a in ( ) izade, JVM odm ah prekida rad svih servisnih niti, bez ikakvih form alnosti koje ste m oda oekivali. Poto se servisne niti ne m ogu lepo prekinuti, one su retko kada podesne. Po pravilu su neservisni izvrioci bolji, poto se svi zadaci koje izvrilac kontrolie m ogu prekinuti odjednom . Kao to ete videti u nastavku poglavlja, u tom sluaju se gaenje odvija pravilno. Veba 7: (2) Eksperim entiite s razliitim vrem enim a spavanja u program u ServisneN iti.java da biste videli ta se deava.

906

Misliti na Javi

Veba 8: (1) Izmenite program JosOsnovaNiti.java tako da sve niti b u d u servisne i proverite da li program prekida rad im m etoda m a in ( ) dobije priliku da izae. Veba 9: (3) Izmenite program ProstiPrioriteti.java tako da nam enski proizvoa niti (klasa ThreadFactory) postavlja prioritete niti.

Varijante programiranja
U dosadanjim prim erim a su sve klase zadataka realizovale interfejs Runnable. U veoma jednostavnim sluajevima m ogu je i drugaiji pristup, nasleivanje neposredno od klase Thread, to se radi ovako:
//: paralelno/JednostavnaNit.java // Neposredno nasleivanje od klase Thread. public class JednostavnaNit extends Thread { private int odbrojavanje = 5; private static int brojacNiti = 0; public JednostavnaNit() { // Sauvaj ime niti: super(Integer.toString(++brojacNi ti)); start();

}
public String toString() { return "#" + getName() + "(" + odbrojavanje + "), ";

}
public void run() { vvhile(true) { System.out.pri nt(this); if(--odbrojavanje == 0) return;

} }
public static void main(String[] args) { for(int i = 0; i < 5; i++) new JednostavnaNit();

}
} /* Ispis: #1(5), #1(4), #1(3), #1(2), #1(1), #2(5), #2(4), #2(3), #2(2), #2(1), #3(5), #3(4), #3(3), #3(2), #3(1), #4(5), #4(4), #4(3), #4(2), #4(1), #5(5), #5(4), #5(3), #5(2), #5(1),

* ///:Nitima (objektima klase T h read ) dajemo imena tako to pozivamo odgovarajue T hread konstruktore. To ime ispisuje to S trin g () nakon to ga dobije od m etode getN am e(). M oda ete sresti i idiom objekta tipa R unnable koji sam sobom upravlja:
//: paralelno/Samoupravno.java // Objekat tipa Runnable koji sadri sopstvenu nit koja ga izvrava.

Poglavlje 2 1: Paralelno izvravanje

907

public class Samoupravno implements Runnable { private int odbrojavanje = 5; private Thread t = new Thread(this); public Samoupravno() { t.start(); } public String toStringO { return Thread.currentThread().getName() + "{" + odbrojavanje + "),
J

public void run() { while(true) { System.out.print(th1s); if(--odbrojavanje == 0) return;

} }
public static void main(String[] args) { for(int i = 0; i < 5; i++) new S amo u p r a v n o O ;

}
} /* Ispis: Thread-0{5), Thread-1(4), Thread-2(3), Thread-3(2), Thread-4(1), Thread-0(4), Thread-1(3), Thread-2(2), Thread-3(1), Thread-0(3), Thread-1(2), Thread-2(1), Thread-4(5), Thread-0(2), Thread-l(l), Thread-3(5), Thread-4(4), Thread-O(l), Thread-2(5), Thread-3(4), Thread-4(3), Thread-1(5), Thread-2(4), Thread-3(3), Thread-4(2),

*///-

Ovo se ne razlikuje m nogo od nasleivanja od klase Thread, sem to je sintaksa neto neobinija. M eutim , realizacija interfejsa om oguuje nasledivanje od druge kiase, dok nasledivanje od klase Thread to ne om oguuje. O bratite panju na to da je m etoda s ta r t( ) pozvana iz konstruktora. Prim er je krajnje jednostavan i zato verovatno bezbedan; ipak, m orate biti svesni da pokretanje niti iz konstruktora um e da bude veoma problem atino, poto bi m oglo poeti izvravanje nekog drugog zadatka pre nego to ko nstruktor zavri svoj posao, to znai da taj zadatak moe da pristupi objektu koji je u nestabilnom stanju. I zbog toga je korienje izvrilaca bolje od eksplicitnog pravljenja niti (objekata tipa Thread). Ponekad kod za pravljenje niti treba sakriti u klasi p om ou unutranje klase, kao to u sada uraditi:
//: paralelno/VarijanteNiti.java // Pravljenje niti pomou unutranjih klasa. import java.util.concurrent.*; import static net.mindview.uti1 .Print.*; // Upotrebiu imenovanu unutranju klasu: class UnutrasnjaNitl { private int odbrojavanje = 5; private Unutrasnja unutrasnja; private class Unutrasnja extends Thread {

908

Misliti na Javi

Unutrasnja(String ime) { super(ime); start();

}
public void run() { try { while(true) { print(this); if( odbrojavanje == 0) return; sleep(lO);

}
} catch(InterruptedException e) { print("prekinuto");

} }
public String toStringO { return getName() + ": " + odbrojavanje;

} }
public UnutrasnjaNitl(String ime) { unutrasnja = new Unutrasnja(ime);

} }
// Upotrebiu anonimnu unutranju klasu: class UnutrasnjaNit2 { private int odbrojavanje = 5; private Thread t; public UnutrasnjaNit2(String ime) { t = new Thread(ime) { public void run() { try { while(true) { print(this); if(--odbrojavanje == 0) return; sleep(10);

}
} catch(InterruptedException e) { print("sleep() prekinuta");

} }
public String toStringO { return getName() + ": " + odbrojavanje;

} };
t.start();

Poglavlje 2 1: Paralelno izvravanje

909

// Upotrebiu imenovanu realizaciju interfejsa Runnable: class UnutrasnjaRunnablel { private int odbrojavanje = 5; private Unutrasnja unutrasnja; private class Unutrasnja implements Runnable { Thread t; Unutrasnja(String ime) { t = new Thread(this, ime); t.start();

}
public void run() { try { while(true) { print(this);

if(--odbrojavanje == 0) return; TimeUnit.MILLISECONDS.sleep(lO);

}
} catch(InterruptedException e) { print("sleep() preki nuta1 ');

} }
public String toStringO { return t.getName() + ": 1 1 + odbrojavanje;

} }
public UnutrasnjaRunnablel(String ime) { unutrasnja = new Unutrasnja(ime);

}
// Upotrebiu anonimnu realizaciju interfejsa Runnable: class UnutrasnjaRunnable2 { private int odbrojavanje = 5; private Thread t; public UnutrasnjaRunnable2(String ime) { t = new Thread(new RunnableO { public void run() { try { while(true) { print(this); if(--odbrojavanje == 0) return; TimeUnit.MILLISECONDS.sleep(lO);

}
} catch(InterruptedException e) { print ("sleep() prekinuta");

} }
public String toString() { return Thread.currentThread().getName() + ": " + odbrojavanje;

910

Misliti na Javi

}, ime); t.start();

} }
// Zasebna metoda koja e neki kod izvravati kao zadatak: class MetodaNit { private int odbrojavanje = 5; private Thread t; private String ime; public MetodaNit(String ime) { this.ime = ime; } public void runTask() { if(t == null) { t = new Thread(ime) { public void run() { try { while(true) { pri nt(this); if(--odbrojavanje == 0) return; sleep(lO);

}
} catch(InterruptedException e) { print("sleep() prekinuta");

} }
public String toStringO { return getName() + ": " + odbrojavanje;

} };
t.start();

} } }
public class VarijanteNiti { public static void main(String[] args) { new UnutrasnjaNitl("UnutrasnjaNitl"); new UnutrasnjaNi t2("UnutrasnjaNit2"); new UnutrasnjaRunnable l ("UnutrasnjaRunnablel"); new UnutrasnjaRunnable2("UnutrasnjaRunnable2"); new MetodaNitC'MetodaNit") . r u n T a s k O ;

}
} /* (Pokrenite da biste videli rezultat) * / //:-

U nutrasnjaN itl pravi im enovanu unutranju klasu koja nasleuje Thread i u konstruktoru pravi instancu te unutranje klase. To ima smisla ako unutranja klasa ima posebne sposobnosti (nove m etode) potrebne u drugim m etodam a. M edutim , nit obino pravim o sam o zbog sposobnosti klase Thread, pa nije n eophodno da pravim o imenovanu unutranju klasu. U nutrasnjaN it pokazuje drugaiji pristup: u konstruktoru se pravi ano nim na unutranja potklasa od Thread i svodi navie na Thread referencu t. Ukoliko

Poglavlje 2 1: Paralelno izvravanje

911

ostalim m etodam a te klase zatreba pristup objektu na koji upuuje t, m ogu to uiniti preko interfejsa T hread i bez poznavanja stvarnog tipa tog objekta. Trea i etvrta klasa u prim eru ponavljaju prve dve klase, ali koriste interfejse Runnable, a ne klasu Thread. Klasa M etodaNit pokazuje pravljenje niti u n u tar m etode. M etodu pozivate kada ste sprem ni da pokrenete tu nit, i m etoda vraa svoj rezultat nakon to nit otpone s radom . Ukoliko nit obavlja sam o pom one, a ne i osnovne operacije klase, to je verovatno korisniji i prikladniji pristup nego to je pokretanje niti u n u tar konstruktora klase.

Veba 10: (4) Izm enite vebu 5 po uzoru na prim er s klasom MetodaNit, tako da m etoda ru n T ask ( ) p rim a kao argum ent broj Fibonaccijevih brojeva koje treba sabrati, i svaki p u t kada pozovete run T ask ( ), ona vraa objekat tipa Future koji proizvodi poziv m etode s u b m it().

Terminologija
Kao to se vidi iz prethodnog odeljka, paralelni program i se u Javi m ogu pisati na vie naina i to program era m oe da zbuni. esto problem nastaje zbog term inologije kojom je opisan program za paralelno izvravanje, naroito tam o gde su u pitanju niti. Dosad je trebalo da uvidite kako postoji razlika izm eu zadatka i niti koja taj zadatak izvrava; ta razlika je naroito jasna u Javinim bibliotekam a, poto nad klasom Thread zapravo nem am o nikakvu kontrolu (a jo je jasnija kada se radi o izvriocima koji um esto nas prave niti i upravljaju njim a). Program er pie zaatak i na neki nain m u dodeljuje nit koja e ga izvravati. U Javi, nit (klasa Thread) sam a po sebi ne radi nita. O na izvrava zadatak koji joj damo. Pa ipak, u literaturi o vienitnom program iranju uvek se kae ,,nit radi ovo ili ono. D obija se utisak da nit je s te zadatak. Kada sam upoznao Javine niti, utisak je bio toliko jak da sam video jasnu relaciju JE, koja mi je govorila kako zadatak oigledno treba da izvedem iz klase Thread. Dodajte tom e loe odabrano ime interfejsa Runnable (koji se moe izvravati); po m om miljenju, trebalo je da se zove Task (zadatak). Ukoliko interfejs oigledno nije nita vie od generikog kapsuliranja svojih m etoda, onda je im enovanje po kalupum oe da uradi lo i to odgovarajue, ali ako treba da izrazi vii pojam - kao to je Zadatak - o nda m u treba dati ime tog pojma. Problem potie od meanja nivoa apstrakcije. Na nivou koncepta, elimo da napravim o zadatak koji se izvrava nezavisno od drugih zadataka, pa bi trebalo da je mogue definisati zadatak i rei ,,radi, bez petljanja s detaljima. Ali fiziki, pravljenje niti je skupo, pa m oram o da ih tedim o i upravljam o njim a. Zato sa stanovita realizacije im a smisla odvojiti zadatke od niti. Sem toga, Javine niti su napravljene na osnovu nekakvih p-niti koje potiu jo iz C-a, a u tom jeziku program er je opkoljen detaljim a i m ora poznavati do u sitnicu sve to se dogaa. Delimino je takav pristup preao i u realizaciju Jave, pa da bism o ostali na visokom nivou apstrakcije, m oram o biti disciplinovani pri pisanju programa. (Potrudiu se da u ovom poglavlju sam pokaem tu disciplinu.) Da bi razm atranje bilo jasnije, term inom zadatak opisivau ono to se radi, a term inom nit - m ehanizam koji izvrava zadatak. Dakle, ako sistem razm atram o na konceptualnom nivou, m oem o upotrebiti term in zadatak, a da uopte ne spom injem o m ehanizam izvravanja.

912

Misliti na Javi

Kako se pridruiti postojeoj niti


Jedna nit m oe pozvati m etod u jo in ( ) za neku drug u nit; u to m sluaju, prva n it eka da druga zavri rad pa tek onda nastavlja izvravanje. Ukoliko n it pozove n .jo in ( ) za drugu nit t kao argum ent, pozivajua nit e biti zaustavljena sve dok odredina nit t ne zavri s radom - dok t.isAlive( ) ne po p rim i vrednost false. M etodu jo in ( ) moete pozvati i s vrem enskim argum entom (izraenim u m ilisekundam a ili m ilisekundam a i nanosekundam a); u tom sluaju se izvravanje m etode jo in ( ) zavrava ukoliko se odredina n it u tom vrem enu ne zavri. Poto poziv m etode jo in ( ) m oe biti prekinut pozivom m etode in te rru p t( ) za pozivajuu nit, obavezna je upotreba odredbe try-catch. Sve te operacije se izvravaju u sJedeem prim eru:
//: paralelno/Joining.java // Objanjenje metode join(). import static net.mindview.util.Print.*; class Spavac extends Thread { private int trajanje; public Spavac(String ime, int vremeSpavanja) { super(ime); trajanje = vremeSpavanja; start();

}
public void run() { try { sleep(trajanje); } catch(InterruptedException e) { print(getName() + " je bila prekinuta. " + "isInterruptedO : " + islnterrupted()); return;

}
print(getName() + " je probuena");

} }
class Joiner extends Thread { private Spavac spavac; public Joiner(String ime, Spavac spavac) { super(ime); this.spavac = spavac; start();

}
public void run() { try { spavac.join(); } catch(InterruptedException e) { print("Prekinuto");

Poglavlje 2 1: Paralelno izvravanje

913

print(getName() +

ekanje zavreno");

} }
public class Joining { public static void main(String[] args) { Spavac pospana = new Spavac("Pospana", 1500), gundjalo = new Spavac("Gundjalo", 1500); Joiner omamljena = new Joiner("0mamljena", pospana), doktor = new Joiner("Doktor", gundjalo); gundjalo.interrupt();
1 /

} /* Ispis: Gundjalo je bila prekinuta. islnterrupted(): false Doktor: ekanje zavreno Pospana je probuena Omamljena: ekanje zavreno

* ///:-

Spavac je nit koja odlazi na spavanje za vrem e specificirano u njenom konstruktoru. U nutar nietode r u n ( ), m etoda sleep ( ) moe zavriti s rad o m kada to vrem e istekne, ali moe takode biti i prekinuta u izvravanju. Prekid se prijavljuje u n u tar odredbe catch, kao i rezultat m etode isln te rru p te d ( ). Kada neka druga nit pozove in te rru p t( ) za ovu nit, postavlja se indikator da je nit prekinuta u izvravanju. M eutim , taj indikator biva obrisan prilikom hvatanja izuzetka, pa e u n u tar odredbe catch ispitivanje vrednosti te m etode uvek dati rezultat false. Taj indikator se koristi u drugim situacijam a u kojima nit moe da ispita svoje prekinuto stanje, a u kojima se ne generie izuzetak. Joiner je zadatak koji eka da se Spavac probudi; ekanje se postie pozivom m etode jo in ( ) za objekat tipa Spavac. U m etodi m a in ( ) svaki Spavac im a po jedan objekat tipa Joiner, i iz rezultata vidite da Joiner zavrava svoje izvravanje zajedno s tim objektom tipa Spavac, bez obzira na to da li je Spavac bio prekinut ili je norm alno okonao svoje izvravanje. Imajte u vidu da Java SE5 biblioteke java.util.concurrent sadre alatke. M eu njim a je CyclicBarrier (opisau je u nastavku poglavlja); ona m oe biti podesnija od m etode jo in ( ) koja je bila deo prvobitne biblioteke za vienitno program iranje.

Korisniko okruenje koje brzo reaguje


Ve sam rekao da je jedan od motiva za vienitno program iranje potreba da se dobiju korisnika okruenja koja brzo reaguju. Iako sve do poglavlja Grafika korisniika okruenja neem o imati posla s grafikim okruenjim a, naredni prim er je jednostavna skica konzolnog korisnikog okruenja. Prim er im a dve verzije: jednu koja stalno neto rauna i zato ne stie da proita ulaz s konzole, i drugu u kojoj je izraunavanje stavljeno u zaseban zadatak, pa pored njega program stie da oslukuje / ulaz s konzole.

914

Misliti na Javi

//: paralelno/BrzoKorisnickoOkruzenje.java // Brzina reagovanja korisnikog okruenja. // {RunByHand) class SporoKorisnickoOkruzenje { private volatile double d = 1; public SporoKorisnickoOkruzenje() throws Exception { while(d > 0) d = d + (Math.PI + Math.E) / d; System.in.read{); // Ovo se nikada ne izvrava

} }
public class BrzoKorisnickoOkruzenje extends Thread { private static volatile double d = 1; public BrzoKorisnickoOkruzenjeO { setDaemon(true); start();

}
public void run() { while(true) { d = d + (Math.PI + Math.E) / d;

} }
public static void main(String[] args) throws Exception { //! new SporoKorisnickoOkruzenjeO ; // Ovaj proces moramo ubiti new BrzoKorisnickoOkruzenjeO ; System.in.read(); System.out.println(d); // Pokazuje stepen napredovanja

} } ///:S poroK orisnickoO kruzenje obavlja izraunavanje u n u tar beskonane petlje while, te oigledno ne moe da dopre do reda u kojem se ita ulaz s konzole (uslov vvhile ini da se prevodilac prevari i oceni da je taj red dostian). Ukoliko red u kojem se pravi novo SporoK o risnicko O kru zenje pretvorite u naredbu koja se izvrava, m oraete taj proces runo da ubijete da biste izali iz program a. Program e brzo reagovati ukoliko izraunavanje stavite u n u tar m etode r u n ( ) i tako om oguite da ono bude predupreeno. Kada pritisnete taster Enter, videete da se izraunavanje odvija u pozadini dok program oslukuje ulaz korisnika.

Grupe niti
G rupa niti sadri kolekciju niti. V rednost grupa niti saeo je Joshua Bioch,8 projektant softvera koji je (izm edu ostalog), dok je radio za Sun, popravio i znatno poboljao biblioteku Java kolekcija u JDK 1.2: G rupe niti je najbolje sm atrati neuspenim eksperim entom i zaboraviti da postoje.
8

Efikasno programiranjc na Javi, au to r Joshua Bloch (M ikro knjiga, 2004), s. 211.

Poglavlje 2 1: Paralelno izvravanje

915

Ukoliko ste (kao ja) potroili vreme i energiju pokuavajui da shvatite vrednost grupa niti, m oda se pitate zato Sun o toj tem i nikada nije dao zvanino saoptenje. Isto vai i za brojne druge prom ene sprovedene u Javi tokom godina. Izgleda da bism o mogli prim eniti Teoriju rastueg angaovanjd' - njen tvorac je Joseph Stiglitz (dobitnik Nobelove nagrade). Cenu nastavljanja greaka plaaju drugi, dok cenu priznavanja greaka plaam o sami.

Hvatanje izuzetaka
Izuzetak koji je pobegao iz niti ne m oete uhvatiti - takva je njihova priroda. Kada izuzetak izae iz m etode r u n ( ) zadatka, prodree do konzole ukoliko ne preduzm ete posebne m ere za hvatanje takvih zabludelih greaka. Pre Jave SE5, za hvatanje takvih izuzetaka koristili sm o grupe niti, ali sada problem m oem o reiti izvriocima, te o grupam a niti vie ne m oram o nita da znam o (sem onoliko koliko je po trebno da bism o razum eli stari kod; pojedinosti o grupam a niti proitajte u knjizi T h in kin g in Java, 2 nE dition, koju m oete preuzeti sa adrese w w w .M indV iew .net). Sledei zadatak uvek baca izuzetak koji izlazi iz njegove m etode r u n ( ), a m a in ( ) pokazuje ta se deava kada ga pokrenete:
//: paralelno/Nitlzuzetka.java // {ThrowsException} import java.uti1 .concurrent.*; public class Nitlzuzetka implements Runnable { public void run() { throw new RuntimeException();

}
public static void main(String[] args) { ExecutorService exec = Executors.newCachedThreadPool(); exec.execute(new Nitlzuzetka());

} } ///:Rezultat program a je (nakon uklanjanja nekih odrednica da bi stalo):


java.1ang.RuntimeException at Nitlzuzetka.run(Nitlzuzetka.java:7) at ThreadPoolExecutor$Worker.runTask(Unknown Source) at ThreadPoolExecutor$Worker.run(Unknown Source) at java.1ang.Thread.run(Unknown Source)

Nita ne pom ae ako telo m etode m a in ( ) stavim o u blok try-catch:


//: paralelno/NaivnaObradalzuzetka.java // {ThrowsException} import java.uti1 .concurrent.*;

1 na nckoliko drugih m esta u celoj lavi. Zato da se o g ran iim o sam o na Javu? Bio sam k o n su ltan t u vie projekata gde se pokazalo da je ta tv rdnja tana.

916

Misliti na Javi

public class NaivnaObradalzuzetka { public static void main(String[] args) { try { ExecutorService exec = Executors.newCachedThreadPool(); exec.execute(new N i t l z u z e t k a O ) ; } catch(RuntimeException ue) { // Ova naredba se NEE izvriti! System.out.println("Izuzetak je obraen!");

} } } ///= Rezultat je isti kao u p rethodnom prim eru: izuzetak koji nije bio uhvaen. Problem emo reiti prom enom naina na koji izvrilac (Executor) pravi niti. Java SE5 im a nov interfejs po im enu Thread.UncaughtExceptionHandler; pom ou njega svakoj niti moete da pridruite njen program za obradu. Neposredno pre nego to bi nit um rla zato to njen izuzetak nije bio uhvaen, autom atski se poziva Thread.UncaughtExceptionHandIer.uncaughtException( ). Da bism o mogli da ga upotrebimo, napraviem o nov tip interfejsa ThreadFactory koji svakoj niti koju napravi pridruuje Thread.UncaughtExceptionHandler. Tu proizvodnu m etodu proslediemo metodi klase Executors koja pravi nov ExecutorService:
//: paralelno/HvatanjeNeuhvacenihlzuzetaka.java import java.uti1 .concurrent. class ExceptionThread2 implements Runnable { public void run() { Thread t = Thread.currentThread(); System.out.println("izvrava nit " + t); System.out.pri ntln( "eh = + t .getUncaughtExceptionHandler()); throw new RuntimeException();

class MojBlokZaObraduNeuhvacenihlzuzetaka implements Thread.UncaughtExceptionHandler { public void neuhvacenIzuzetak(Thread t, Throwable e) { System.out.printl n("uhvatio 1 1 + e) ;

} }
class HandlerThreadFactory implements ThreadFactory { public Thread newThread(Runnable r) { System.out.println(this + " pravi novu nit"); Thread t = new Thread(r); System.out.println("napravio " + t ) ;

Poglavlje 21: Paralelno izvravanje

917

t .setllncaughtExceptionHandler( new MojBlokZaObraduNeuhvacenihlzuzetakaO); System.out.println( "eh = 1 1 + t.getUncaughtExceptionHandler()); return t;

} }
public class HvatanjeNeuhvacenihlzuzetaka { public static void main(String[] args) { ExecutorService exec = Executors.newCachedThreadPool( new HandlerThreadFactory()); exec.execute(new ExceptionThread2());

}
} /* Ispis: (90% podudaranja) HandlerThreadFactory@de6ced pravi novu nit napravio Thread[Thread-0,5,main] eh = MojBlokZa0braduNeuhvacenihIzuzetaka@lfb8ee3 izvrava nit Thread[Thread-0,5,main] eh = MojBlokZa0braduNeuhvacenihIzuzetaka@lfb8ee3 uhvatio java.lang.RuntimeException

* ///:Ispisao sam i dodatne oznake kako biste mogli proveriti da se nitim a koje proizvodi Factory daje novi UncaughtExceptionHandler. Moete videti da neuhvaene izuzetke sada hvata m etoda neuhvacenlzuzetak( ). G ornji prim er om oguuje da blok za obradu (engl. handler) prilagoavate svakom pojedinom sluaju. Ukoliko nam eravate da isti blok za obradu izuzetaka koristite svuda, jo je jednostavnije da napravite p o d ra zu m eva n i blok za obradu neuhvaenih izuzetaka koji postavlja odredeno statiko polje u klasi Thread:
//: paralelno/PodraBlokZaObraduNeuhvacenihlzuzetaka.java import java.util.concurrent.*; public class PodraBlokZaObraduNeuhvacenihlzuzetaka { public static void main(String[] args) { Thread.setDefaultUncaughtExceptionHandler( new MojBlokZaObraduNeuhvacenihIzuzetaka()); ExecutorService exec = Executors.newCachedThreadPool(); exec.execute(new NitlzuzetkaO);

}
} /* Ispis: uhvatio java.1ang.RuntimeException

* ///:Ovaj program se poziva sam o u sluaju da ne postoji blok za obradu neuhvaenih izuzetaka prilagoden svakoj niti. Sistem proverava postoji li verzija za konkretnu nit i ako je ne pronae, proverava da li ta grupa niti im a sopstvenu specijalizovanu m etodu neuhvacenlzuzetak( ); ako nema, poziva se defau!tUncaughtExceptionHandler.

918

Misliti na Javi

Deljenje resursa
Program s jednom n id m oete da zamislite kao usam ljenu jedinku koja se kree kroz prostor vaeg problem a i reava jedan po jedan zadatak. Poto postoji samo jedna jedinka, uopte ne m orate da vodite rauna o situaciji kada dve jedinke pokuavaju da iskoriste isti resurs istovremeno, p o p u t dvoje koji pokuavaju da se parkiraju na istom mestu, ili da prou kroz vrata istovrem eno ili ak da sam o govore istovremeno. Ako za reavanje problem a upotrebite vie niti, ne javlja se problem usam ljenih jedinki, ali postoji m ogunost da dve ili vie niti pokuaju da upotrebe isti ogranien resurs u istom trenutku, tj. da se uzajam no om etu. Sukobljavanje zbog nekog resursa m ora se spreiti ili e dve niti istovrem eno pokuati da pristupe istom bankovnom nalogu, odtam paju neto na istom tam pau, okrenu isti ventil itd.

Nepravilno pristupanje resursima


Razm otrite sledei prim er u kojem jedan zadatak generie parne brojeve, a drugi zadaci ih troe. Jedini posao zadataka potroaa jeste da provere validnost parnih brojeva. Prvo em o definisati potroaki zadatak ProveraParnosti, poto emo ga koristiti i u narednim prim erim a. Da bism o zadatak ProveraParnosti odvojili od raznih tipova generatora s kojim a em o eksperim entisati, napraviem o apstraktnu klasu GeneratorCelobroj koja sadri m inim um neophodnih m etoda za koje ProveraParnosti m ora da zna: m etodu n e x t( ) i m etodu za otkazivanje pravljenja objekta. Ta klasa ne realizuje interfejs Generator, zato to m ora da proizvede ceo broj (int), a generike klase ne prim aju proste tipove kao param etre.
//: paralelno/GeneratorCel obroj.java public abstract class GeneratorCelobroj { private volatile boolean cancelled = false; public abstract int next(); // Dopustiu otkazivanje ovoga: public void cancel() { cancelled = true; ) public boolean isCancelled() { return cancelled; )

} lll--~

G eneratorCelobroj ima m etodu can cel( ) za prom enu stanja boolean indikatora cancelled i m etodu isC ancelled( ) koja utvruje da li je pravljenje objekta otkazano. Poto je indikator cancelled tipa boolean, on je a to m ski (nedeljiv), to znai a takvo polje ne
m oete proitati u nekom m eustanju usred jednostavnih operacija kao to su dodeljivanje i vraanje vrednosti, jer se one ne m ogu prekidati. Indikatoru cancelled dodat je i m odifikator volatile, da bi se obezbedila vidljivost. O atom skim indikatorim a i vidljivosti saznaete vie u nastavku poglavlja. Sledea klasa ProveraParnosti moe da testira svaki GeneratorCelobroj:
//: paralelno/ProveraParnosti.java import java.util.concurrent.*;

Poglavlje 2 1: Paralelno izvravanje

919

public class ProveraParnosti implements Runnable { private GeneratorCelobroj generator; private final int id; public ProveraParnosti(GeneratorCelobroj g, int ident) { generator = g; id = ident;

}
public void run{) { whi1e ( !generator.isCancel1e d ()) { int vre = generator.next(); if(vre % 2 != 0) { " System.out.println(vre + " nije paran!"); generator.cancel(); // Otkazuje sve ProvereParnosti

} } }
// Testiranje svih tipova GeneratorCelobroj: public static void test(GeneratorCelobroj gp, int broj) { System.out.println("Pritisnite Control-C ako elite da izaete iz programa"); ExecutorService exec = Executors.newCachedThreadPool(); for(int i = 0; i < broj; i++) exec.execute(new ProveraParnosti(gp, i)); exec.shutdown();

}
// Podrazumevana vrednost broja: public static void test(GeneratorCelobroj gp) { test(gp, 10);

} } ///:O bratite panju na to da, u ovom prim eru, klasa ije se pravljenje moe otkazati ne realizuje interfejs Runnable. Zato svi zadaci klase ProveraParnosti proveravaju da li je otkazano pravljenje objekta tipa GeneratorCelobroj od kojeg zavise, kao to vidite u metodi r u n ( ). Na taj nain, zadaci koji dele zajedniki resurs (GeneratorCelobroj) oslukuju signal tog resursa koji im kazuje kada da okonaju svoj rad. Time se izbegava tzv. uslov za trku (engl. race condition ), gde se dva ili vie zadataka trkaju da reaguju na ispunjenje nekog uslova i zato se sudare ili na drugi nain proizvedu nedoslene rezultate. Paljivo razmislite o svim m oguim nainim a na koje paralelni program moe da zakae; m orate napraviti zatitu od svakog. Na primer, zadatak ne sm e da zavisi od drugog zadatka, zato to Java ne jem i redosled gaenja zadataka. U prethodnom p rim eru zadatak je zavisio od olijekta koji nije zadatak; tim e je izbegnut potencijalni uslovza trku. M etoda te s t( ) priprem a i obavlja testiranje svih tipova GeneratorCelobroj tako to pokree vie ProveraParnosti za isti GeneratorCelobroj. Ukoliko GeneratorCelobroj nije dao eljeni rezultat, m etoda te s t( ) e to prijaviti i izai; u protivnom , m orate pritisnuti kom binaciju tastera C ontrol-C da biste je okonali.

920

Misliti na Javi

Zadaci ProvereParnosti stalno itaju i ispituju brojeve koje je dao njim a pridrueni GeneratorCelobroj. Ukoliko generator.isC ancelled( ) im a vrednost true, m etoda r u n ( ) vraa svoj rezultat i izlazi, to izvriocu u m etodi ProveraParnosti.test( ) kazuje da je zadatak obavljen. Svaki zadatak ProvereParnosti m oe da pozove m etodu cancel( ) za svoj pridrueni G eneratorCelobroj, im e prouzrokuje da sve druge ProvereParnosti tog objekta tipa GeneratorCelobroj n orm alno okonaju svoj rad. U nastavku poglavlja videete da Java im a i optije m ehanizm e za okonavanje rada niti. Prvi GeneratorCelobroj koji em o razm otriti im a m eto d u n e x t( ) koja proizvodi seriju parnih brojeva:
//: paralelno/GeneratorParnih.java // Kada se niti sudare. public class GeneratorParnih extends GeneratorCelobroj { private int tekuciParniBroj = 0; pub'lic int next() { ++tekuciParniBroj; // Ovo je opasno! ++tekuciParniBroj; return tekuciParniBroj;

}
public static void main(String[] args) { ProveraParnosti,test(new GeneratorParnih());

}
} /* Ispis: (primer) Pritisnite Control-C ako elite da izaete iz programa 89476993 nije paran! 89476993 nije paran!

* ///:Moe se desiti da jedan zadatak pozove n e x t ( ) nakon to je drugi zadatak obavio prvo poveanje broja tekuciP arniB roj za 1, ali ne i drugo (na o nom m estu u program u gde stoji kom entar Ovo je opasno!). Tim e je broj dospeo u nepravilno stanje. Da bih vam dokazao da se to m oe dogoditi, P ro v e ra P a rn o sti.te st() pravi grupu objekata tipa Prov era P a rn o sti koji stalno itaju izlaz G en e ra to raP arn ih i ispituju parnost svakog od tih brojeva. Kada se pronae neki koji nije paran, prijavljuje se greka i program se gasi. Taj program pre ili kasnije m ora da otkae, zato to zadaci P ro v ereP arn o sti m ogu da pristupe inform acijam a u G e n e ra to ru P a rn ih dok je on u nepravilnom stanju. M eutim , problem ne m ora biti otkriven dok G en e ra to rP a rn ih ne izvri m nogo ciklusa, iji se broj m enja u zavisnosti od operativnog sistema i pojedinosti realizacije. Ako elite da vidite otkazivanje to pre, izm eu prvog i drugog poveanja za 1 stavite poziv m etode y ie ld ( ). I to je deo problem a s vienitnim program im a - um eju da daju privid ispravnosti ak i kada greka postoji, ukoliko je verovatnoa otkazivanja veoma mala. Vano je uoiti da se operacija poveanja za 1 interno sastoji od vie koraka, i da vienitni m ehanizam moe zaustaviti (prekinuti) zadatak usred tih koraka - drugim reima, u Javi poveanje za 1 nije atom ska operacija. Dakle, ak se ni poveanje za 1 ne moe obaviti bezbedno ako zadatak nije zatien.

Poglavlje 21: Paralelno izvravanje

921

Razreavanje takmienja za deljene resurse


P rethodni prim er pokazuje osnovni problem s nitim a. N ikad se ne zna kada e koja nit proraditi. Zamislite da sedite za stolom s viljukom u ruci i da upravo nam eravate da nabodete poslednje pare hrane u tanjiru; u trenu tk u kad viljuka dodirne to pare, ono iznenada nestane (poto je vaa nit prekinuta, a pokrenuta druga n it koja je uletela i ukrala to pare). Sada razum ete o kakvom problem u se radi. Da bi paraleino izvravanje funkcionisalo, barem tokom bitnih razdoblja m orate spreiti da vie zadataka istovrem eno pristupi istom resursu. Takvi sudari jednostavno se spreavaju zakljuavanjem (engl. lock ) resursa d o k g a jedna nit koristi. Prva nit koja pristupi nekom resursu p rethod n o ga zakljua, im e se spreava da m u druge niti pristupaju sve dok ne b ude otkljuan; tada ga druga nit zakljuava, koristi ga itd. Ako prednje sedite u kolim a predstavlia ogranien resurs, dete koje povie: ,,Prvi! i sedne na njega, zakljuava ga. Da bi se reio problem sudaranja niti, gotovo sve eme paralelnog rada serijalizuju p ristup deljenim resursima. To znai da je, u svakom trenutku, p ristup deljenom resursu dozvoljen sam o jednom zadatku. To se obino postie stavljanjem koda u o dredbu koja, u svakom trenutku, dozvoljava sam o po jednom zaatku d ap ro lazi k ro ztaj deo koda. Poto ta odredba proizvodi uzajam no iskljuivanje (engl. m u tu a l exclusioti), takav m ehanizam se obino naziva uzajam no iskljuiva brava (engl. m u tex). Uzmimo kao prim er vae kupatilo; vie osoba (zadataka koji se izvravaju u nitim a) moe poeleti da sam ostalno koristi kupatilo (deljeni resurs). Kada poe u kupatilo, osoba kuca na vrata kako bi utvrdila da li je slobodno. Ako jeste, osoba ude i zakljua vrata. Nadalje je svima drugim a koji poele da koriste to kupatilo korienje ,,blokirano, pa m oraju da ekaju ispred vrata dok se kupatilo ne oslobodi. Analogija donekle poputa kada se kupatilo oslobodi i doe vreme da se prepusti d ru gom zadatku na korienje. Nema reda za ekanje i mi ne m oem o znati ko e sledei dobiti resurs na korienje, zato to m ehanizam za raspodelu procesorskog vrem ena nitim a nije u tom smislu determ inistiki. Za razliku od toga, kao da grupa blokiranih zadataka krui ispred kupatiia, i kada zadatak koji ga je zakljuao otkljua i izade, ulazi onaj koji se u tom tren utku zatekao najblie vratim a. Ve sam rekao da m ehanizm u za raspodelu procesorskog vrem ena nitim a pom ou m etoda y ie ld ( ) i se tP rio rity ( ) predlaete kom e da dodeli resurs, ali ti predlozi ne m oraju im ati m nogo uticaja, poto to zavisi od platform e i realizacije JVM-a. Da bi se spreili sukobi zbog resursa, Java im a ugradenu podrku u vidu rezervisane rei synchronized. Kada zadatak treba da izvri deo koda zatien rezervisanom reju synchronized, on najpre utvrdi da je resurs otkljuan, zakljua ga, izvri kod i onda otkljua. Deljeni resurs je najee deo m em orije u obliku objekta, ali m oe biti i datoteka, U/I prikljuak ili tampa. Da biste upravljali pristupom deljenom resursu, najpre ga stavite u neki objekat. Potom sinhronizujte sve m etode koje koriste taj objekat, tj. dodajte im m odifikator synchronized. Ukoliko se neki zadatak upravo izvrava pozivom jedne od sinhronizovanih m etoda, ulazak u sve sinhronizovane m etode tog objekta blokiran je za svc. ostale zadatke dokle god se prvi zadatak ne vrati iz svog poziva.

922

Misliti na Javi

Ve ste nauili da podaci klasa m oraju biti privatni (im ati m odifikator private) u kodu koji se isporuuje kupcima; toj m em oriji treba da pristupate sam o preko m etoda. Sukobe ete spreiti tako to ete deklarisati da su te m etode sinhronizovane, to se radi ovako:
synchronized void f() { / * . . . * / } synchronized void g() { / * . . . * / }

Svi objekti autom atski dobijaju sopstvenu bravu (koristi se i ime m onitor). Kada pozovete bilo koju sinhronizovanu m etodu, objekat se zakljuava i nijedna druga sinhronizovana m etoda tog objekta ne m oe biti pozvana dok se prva ne zavri i ne otkljua tu bravu. Za prethodne m etode vai sledee: ako jedan zadatak pozove f ( ) za neki objekat, drugi zadaci ne m ogu pozivati ni f ( ) ni g ( ) za taj objekat dokle god f ( ) ne zavri rad i ne otkljua bravu. Dakle, postoji samo jedna brava koju dele sve sinhronizovane m etode odreenog objekta, i ona spreava da vie zadataka istovremeno upisuje u m em oriju objekta. Vodite rauna o tom e da je za paralelno izvravanje izuzetno vano da polja b u d u privatna; ako nisu, rezervisana re synchronized ne m oe spreiti druge zadatke da polju pristupaju neposredno i tako izazivaju sukobe. Isti zadatak moe vie puta zakljuati objekat. To se deava kada jedna m etoda za isti objekat pozove drugu m etodu, a druga za isti objekat pozove treu itd. JVM prati broj zakljuavanja objekta. Broj zakljuavanja otkljuanog objekta je nula. Kada zadatak prvi put zakljua taj objekat, broj zakljuavanja se povea za jedan. Svaki p ut kada taj zadatak ponovo zakljua taj objekat, broj zakljuavanja se povea za jedan. Naravno, samo zadatak koji je prvi zakljuao taj objekat m oe da ga zakljua vie puta. Broj zakljuavanja se sm anjuje za jedan kad god taj zadatak izae iz neke od sinhronizovanih m etoda; kada broj zakljuavanja padne na nulu, objekat je slobodan i m ogu ga koristiti drugi zadaci. I svaka klasa autom atski dobija svoju bravu (koja je deo njenog objekta klase Class), tako da sinhronizovane statike m etode (oznaene kao synchronized i static) mogu jedna drugu spreiti da istovrem eno pristupe statikim podacim a u okviru pojedine klase. Kada sinhronizovati? Prim enite B rajanovopravilo sinhronizacijc:'0 Sinhronizaciju m orate upotrebiti ukoliko piete u prom enljivu koju bi zatim mogla da proita neka druga nit ili itate prom enljivu koju je prethodno m ogla upisati neka druga nit. Nadalje, i itanje i pisanje m ora biti sinhronizovano pom ou iste brave (m onitora). Ako u klasi im ate vie m etoda koje rade s bitnim podacima, m orate sinhronizovati sve relevantne metode. Ukoliko sinhronizujete sam o jednu od tih m etoda, ostale m ogu zanem ariti bravu na objektu i biti nesm etano pozvane. Ovo je vano: sve m etode koje pristupaju kritinom resursu m oraju biti sinhronizovane ili program nee raditi kako treba.

Sinhronizovanje GeneratoraParnih
Ako klasi G eneratorParnih.java dodam o rezervisanu re synchronized, spreiemo neeljeno pristupanje toj niti:

1 0 Po Brianu Goetzu, koautoru ]ava Concurrency in Practice ; ostali autori su Tini Peierls, Joshua Bloch, Joseph Bowbeer, David Holmes i Doug Lea (Addison-WesIey, 2006).

Poglavlje 2 1: Paralelno izvravanje

923

//: paralelno/SinhronizovanGeneratorParnih.java // Pojednostavljivanje uzajamno iskljuivih brava // pomou rezervisane rei synchronized. // {RunByHand} public class SinhronizovanGeneratorParnih extends GeneratorCelobroj { private int tekuciParniBroj = 0; public synchronized int next() { ++tekuciParniBroj; Thread.yield(); // Bre izaziva zakazivanje ++tekuci ParniBroj; return tekuciParniBroj;

}
public static void main(String[] args) { ProveraParnosti .test(new SinhronizovanGeneratorParnihO);

} } ///= Poziv m etode T hread.yield( ) um etnut je izmeu dva poveanja za 1 da bi se poveala verovatnoa prebacivanja konteksta dok je tekuciParniBroj neparan. Poto uzajam no iskljuiva brava spreava da vie zadataka u isto vreme bude u kritinom delu program a, do otkazivanja nee doi, ali poziv m etode y ie ld ( ) pom ae da se otkazivanje ranije desi ukoliko ve moe da nastane. M etodu n e x t( ) zakljuava prvi zadatalc koji ue u nju, i svi drugi zadaci koji pokuaju da je zakljuaju bivaju blokirani sve dok je prvi zadatak ne otkljua. Tada m ehanizam za raspodelu procesorskog vrem ena odabire jedan od ostalih zadataka koji ekaju da se brava otkljua. Na taj nain, u svakom trenutku samo po jedan zadatak moe proi kroz kod koji uva uzajam no iskljuiva brava (mutex). Veba 11: (3) N apravite klasu koja sari dva polja podataka i m etodu koja radi s tim poljim a u postupku koji traje vie koraka, tako da tokom izvravanja te m etode polja budu u nepravilnom stanju (u odnosu na definiciju koju ete sami dati). D odajte m etode koje itaju ta polja i napravite vie niti za pozivanje raznih m etoda i pokaite da su podaci vidljivi i kada su nepravilni. Reite problem pom ou rezervisane rei synchronized.

Upotreba eksplicitnih brava (Lock objekata)


Biblioteka Java SE5 java.util.concurrent sari i eksplicitan m ehanizam uzajam no iskljuive brave (mutexa) definisan u paketu java.util.concurrent.locks. O bjekat tipa Lock m orate eksplicitno praviti, zakljuavati i otkljuavati; zato je njegov kod m anje elegantan od ugradenih oblika. M eutim , prilagodljiviji je za reavanje odredenih vrsta problema. Evo kako izgleda SinhronizovanGeneratorParnih.java u verziji sa eksplicitnim bravam a (objektim a tipa Lock):
//: paralelno/MutexGeneratorParnih.java // Spreavanje sukoba niti pomou uzajamno iskljuivih brava (mutexa). // {RunByHand) import java.util.concurrent.locks.*;

924

Misliti na Javi

public class MutexGeneratorParnih extends GeneratorCelobroj { private int tekuciParniBroj = 0; private Lock brava = new ReentrantLock(); public int next() { brava.lock(); try { ++tekuciParniBroj; Thread.yield(); // Bre izaziva otkazivanje ++tekuciParniBroj; return tekuciParniBroj; } finally { brava.unlockO;

} }
public static void main(String[] args) { ProveraParnosti.test(new MutexGeneratorParnih());

} III-M utexGeneratorParnih dodaje m utex brava i m etodam a lo c k ( ) i u n lock( ) stvara kritini deo un utar m etode n e x t( ). Prilikom upotrebe Lock objekata m orate da internalizujete ovde prikazani idiom: neposredno iza poziva m etode lo ck ( ) m orate staviti blok try-finally, uz u n lo ck( ) u odredbi finally - to j e je d in i nain da brava uvek bude otkljuana. Naredba return mora biti u n u ta r odredbe try da otkljuavanje m etodoin u n lock( ) ne bi dolo prerano i izloilo podatke d ru gim zadacima. lako blok try-finally zahteva vie koda nego rezervisana re synchronized, on predstavlja i jednu od prednosti eksplicitnih Lock objekata. Kada program otkae uz rezervisanu re synchronized, baca se izuzetak, ali program er nem a priliku da poisti i tako sistem vrati u dobro stanje. Ukoliko upotrebljavam o eksplicitne Lock objekte, im am o odredbu finally unutar koje m oem o obaviti sve ienje potrebno da bism o sistem vratili u odgovarajue stanje. Uopte uzev, uz synchronized se pie m anje koda, pa je m anja prilika da program er pogrei. Stoga se eksplicitni Lock objekti obino koriste sam o za reavanje specijalnih problem a. Na primer, uz rezervisanu re synchronized svakako ete uspeti da zakljuate zadatak. Uz to, ne moete pokuavati da zakljuate zadatak pa odustati - m orate upotrebiti biblioteku concurrent:
//: paralelno/PokusajZakljucavanja.java // Brave u biblioteci concurrent dozvoljavaju // da odustanete od pokuaja zakljuavanja. import java.uti1 .concurrent.*; import java.util.concurrent.locks.*; public class PokusajZakljucavanja { private ReentrantLock brava = new ReentrantLock(); public void neodredjenoVreme() { boolean zakljucano = brava.tryLock();

Poglavfje 2 1: Paralelno izvravanje

925

try ( System.out.println("tryLock(): " + zakljucano); ) finally { if (zakl jucano) brava.unlock();

1
}
public void odredjenoVremeO { boolean zakljucano = false; try { zakljucano = brava.tryLock(2, TimeUnit.SECONDS); } catch(InterruptedException e) { throw new RuntimeException(e);

}
try { System.out.println("tryLock(2, TimeUnit.SECONDS): " + zakljucano); } finally { if(zakljucano) brav a .u n l o c k O ;

} }
public static void main(String[] args) { final PokusajZakljucavanja pz = new PokusajZakljucavanja(); pz.neodredjenoVremeO ; // True -- brava je otkljuana pz.odredjenoVreme(); // True brava je otkljuana // Sada napravi zaseban zadatak koji e pokuati da zakljua: new Thread() { { setDaemon(true); } public void run() { p z .brava.1ock(); System.out.pri ntln("zakljuano");

}
}.start(); Thread.yield(); // Daj priliku drugom zadatku pz.neodredjenoVremef); // False -- prethodni zadatak je ve zakljuao pz.odredjenoVremeO; // False prethodni zadatak je ve zakljuao

}
} /* Ispis: tryLock(): true tryLock(2, TimeUnit.SECONDS): true zakljuano tryLock(): false tryLock(2, TimeUnit.SECONDS): false

* ///:O bjekat tipa ReentrantLock om oguuje da pokuate zakljuati bravu i da u tom e ne uspete; ako je neki drugi zadatak bravu ve zakljuao, moete neto drugo da radite um csto da sam o ekate dok se brava ne oslobodi, kao u m etodi neodredjenoV rem e( ).

926

Misliti na Javi

Pokuaj zakljuavanja u m etodi odredjenoV rem e( ) traje najvie 2 sekunde dok se ne proglasi neuspenim (vremenska jedinica je zadata po m o u klase TimeUnit iz Jave SE5). U m etodi m a in ( ), zasebna nit se pravi kao an o n im n a kJasa koja zakljuava bravu, da bi m etode neodredjenoV rem e( ) i odredjenoV rem e( ) im ale oko ega da se takmie. Eksplicitni objekti tipa Lock om oguuju finiju kontrolu nad zakljuavanjem i otkljuavanjem od ugraenih brava synchronized. O ni slue za realizaju specijalizovanih struktu ra za sinhronizaciju, kao to je povezano zakljuavanje (engl. hand-over-hand locking ili lock coupling) koje se upotrebljava za prolaz kroz ulananu listu - kod za prolaz m ora zakljuati bravu sledeeg vora pre nego to otkljua bravu tekueg vora.

Atomske operacije i trenutna vidljivost


U raspravam a o vienitnom program iranju na Javi esto se ponavlja netana pria da atomske operacije ne m oraju biti sinhronizovane. A tom ska je svaka operacija koju m ehanizam za raspodelu procesorskog vrem ena nitim a ne m oe da prekine; ako takva operacija otpone, prebacivanje konteksta e postati m ogue tek kada se ona zavri. O sloniti se na neprekidnost operacije je kakljivo i opasno ne bi trebalo da se usudite da um esto sinhronizacije koristite neprekidnost ukoliko niste strunjak za paralelno program iranje ili ukoliko vam takav strunjak ne pom ae. Ako mislite da ste dovoljno pam etni da se igrate s vatrom , podvrgnite se ovom testu: Gecov test1': Ako umete da napiete JVM visokih perform ansi za savrem eni m ikroprocesor, onda ste kvalifikovani da razm islite o tom e m oete li da izbegnete sinhronizaciju.l: Korisno je zn a ti da neprekidne operacije postoje i da su one, uz druge napredne tehnike, bile upotrebljene za realizaciju dela pam etnijih kom ponenata biblioteke java.util.concurrent. Ali oduprite se nagonu da ih sam i koristite (ponovo proitajte Brajanovo pravilo za sinhronizaciju). Atomske su jednostavne operacije na prostim tipovim a (sem tipova long i double). itanje i pisanje prostih promenljivih (sem onih tipa long ili double) iz m em orije i u mem oriju zajemeno su neprekidne (atomske) operacije. M eutim , itanje i pisanje 64-bitnih vrednosti (promenljivih tipa long ili double) JVM sme da podeli na dve zasebne 32-bitne operacije; tim e se poveava verovatnoa da prebacivanje konteksta nastane usred itanja ili pisanja, pa bi razni zadaci videli pogrene rezultate (to se ponekad naziva cepanje rei, poto se moe desiti da vidite vrednost, tj. rezultat operacije, nakon to je izraunat samo jedan njen deo). Neprekidnost (jednostavnih dodeljivanja i vraanja rezultata) ostvaruje se ukoliko ispred definicije long ili double promenljivih stavite modifikator volatile (koji pre Jave SE5 nije radio ispravno, ali sada radi). Razne JVM sm eju da jeme i neto vie, ali ne bi trebalo da koristite sposobnosti koje se menjaju u zavisnosti od platforme. Dakle, m ehanizam za raspodelu procesorskog vrem ena nitim a ne moe da prekine atom ske operacije. Izuzetno struni program eri to koriste za pisanje kodn hez zakljuavanja koji se ne m ora sinhronizovati. Ali ak je i ta tvrdnja preterano pojednostavljena.
1' Po p re th o d n o p o m enutom B rajanu Gecu (Brian G oetz), stru n jak u za paralelno p ro g ram iran je koji m i je po m o g ao da napiem ovo poglavlje na osnovu njegovih sam o elim in o drskih kom entara. 2 Iz ovog testa n epo sredno sledi: ,Ako neko tvrdi da je paraleln o p ro g ram iran je lako i jednostavno, po starajte se da ta osoba ne donosi vane odluke o projektu na kojem radite. Ukoliko to nikako ne m oete da izbegnete, im ate problem .

Poglavlje 21: Paralelno izvravanje

927

Katkada izgleda da bi atom ska operacija trebalo da bude bezbedna, ali ona to nije. Veina italaca ove knjige sigurno nee m oi da poloi spom enuti Gecov test, te uopte nisu kvalifikovani n i da pokuaju da sinhronizaciju zam ene atom skim operacijama. Pokuaj uklanjanja sinhronizacije obino je znak prerane optim izacije i prouzrokuje grdne probleme, a dobitak je mali ili nikakav. Na vieprocesorskim sistemima (koji se sada prave u obliku procesora s vie jezgara - to je vie procesora u jednom integrisanom kolu), m nogo vei problem od neprekidnosti operacija je vidljivost. (Taj problem ne postoji u jednoprocesorskim sistemima.) Izmene koje napravi jedan zadatak, ak i kada su atom ske, odnosno neprekidne, ne m oraju biti vidljive drugim zadacima (jer sii privrem eno smeterte u lokalni ke procesora, na prim er), pa razni zadaci dobijaju razliite uvide u stanje aplikacije. S druge strane, m ehanizam sinhronizacije obezbeuje da izm ene koje na vieprocesorskom sistemu napravi jedan zadatak b u du vidljive celoj aplikaciji. Bez sinhronizacije se ne m oe znati kada e izm ene postati vidljive. Vidljivost u celoj aplikaciji obezbeduje i rezervisana re volatile. Ako deklariete da je neko polje volatile, to znai da e svi zadaci koji ga itaju videti izmenu im se upisivanje u njega zavri. To vai ak i za lokalnu ke m em oriju, zato to se polja oznaena s volatile odm ah prepisuju u glavnu m em oriju, a sva itanja se obavljaju iz glavne m em orije. Nadam se da shvatate kako su neprekidnost i trenutna vidljivost razliiti pojmovi. Atomska operacija polja koje nem a m odifikator volatile ne m ora da bude o dm ah prepisana u glavnu m em oriju, pa ni drugi zadaci koji itaju to polje ne m oraju odm ah da vide novu vrednost. Svako polje kojem pristupa vie zadataka treba da im a m odifikator volatile; u protivnom , polju treba pristupati sam o preko sinhronizacije. I sinhronizacija prouzrokuje prepisivanje u glavnu m em oriju, pa polje koje je potpuno zatieno sinhronizovanim m etodam a ili blokovim a ne m ora da ima m odifikator volatile. Rezultati svih upisivanja koja zadatak obavi odm ah su vidljivi tom zadatku, pa polju ne m orate da dodajete m odifikator volatile ukoliko ga ita sam o njegov zadatak. volatile ne funkcionie kada vrednost polja zavisi od njegove prethodne vrednosti (takvo je poveanje vrednosti brojaa za 1), niti funkcionie za vrednosti koje su ograniene vrednostim a drugih polja, kao to su lovver (donja) i upper (gornja) granica objekta tipa Range koje m oraju da zadovolje ogranienje lovver <= upper. U potreba m odifikatora volatile umesto rezervisane rei synchronized bezbedna je najee jedino u sluaju da klasa ima samo jedno prom enljivo polje. Ponavljam, najbolje je izabrati rezervisanu re synchronized - to je najbezbedniji pristup, a sve ostalo je rizino. Koje operacije su atomske? Dodeljivanje i vraanje vrednosti polja obino su atomske operacije. M eutim , u C++-U bi atom ske mogle biti ak i ove operacije:
i++; // Moda je atomska u C++-U

i += 2; // Moda je atomska u C++-U

Ali u C++-U to zavisi od prevodioca i procesora (zato pie m oda). Na C++-U se ne moe pisati prenosivi (m euplatform ski) kod koji koristi neprekidnost operacija, poto C++ nem a dosledan m odel m e m o r ije - ali Java im a (u Javi SE5).1 3
!' O vo e biti popravljeno u sledeem sta n d ar u C + + -a.

92 8

Misliti na Javi

U Javi gornje operacije sigurno nisu atomske, kao to vidite iz JVM instrukcija koje proizvode sledee metode:
//: paralelno/Neprekidnost.java // {Pokretanje: javap -c NeprekidnostJ public class Neprekidnost { int i ; void fl() { i++; } void f2() { i += 3; } } /* Ispis: (primer) void fl(); Code: 0: 1: 2: 5: 6: 7: 10: void f2(); Code: 0: 1: 2: 5: 6: 7: 10: aload_0 dup #2; //Polje i:I iconst_l iadd #2; //Polje i:I return

getfield

putfield

getfield

putfield

aload_0 dup #2; //Polje i : I iconst_3 iadd #2; //Polje i:I return

* ///:Svaka instrukcija proizvodi jedan get i jedan p ut izmeu kojih ima drugih instrukcija. Dakle, izm eu itanja (get) i upisivanja (put) neki drugi zadatak moe da izmeni vrednost polja; stoga te operacije nisu atomske. Ako slepo prim enjujete neprekidnost, mogli biste pom isliti da je takva i m etoda d a j V r e d n o s t ( ) u sledeem program u:
//: paralelno/TestNeprekidnosti.java import java.uti1.concurrent.*; public class TestNeprekidnosti implements Runnable { private int i = 0; public int dajVrednost() { return i; } private synchronized void povecanjeZaDva() { i++; i++; } public void run() { while(true) povecanjeZaDva();

Poglavlje 21: Paralelno izvravanje

929

public static void main(String[] args) { ExecutorService exec = Executors.newCachedThreadPool(); TestNeprekidnosti at = new TestNeprekidnosti(); exec.execute(at); while(true) { int vre = at.dajVrednost(); if(vre % 2 != 0) { System.out.println(vre); System.exit(0);

} } }
} /* Ispis: (primer) 191583767

* ///:M eutim , program je pronaao neparan broj i prekinuo rad. Prem da je retu rn i zaista atom ska operacija, zbog nepostojanja sinhronizacije i moe biti proitan dok je objekat u nestabilnom m eustanju (izm eu jednog i drugog i++). Sem toga, bie i problem a s vidljivou, poto ispred i nem a m odifikatora volatile. O be m etode, i dajV rednost( ) i povecanjeZaD va( ) m oraju biti sinhronizovane. Samo su strunjaci za paralelno program iranje kvalifikovani da u ovakvim situacijam a pokuaju optim izaciju; ponavljam , trebalo bi da prim enjujete Brajanovo pravilo za sinhronizaciju. Kao drugi prim er, razm otrite neto jo jednostavnije: klasu koja proizvodi serijske brojeve.1 4 M etoda sledeciSerijskiBroj( ) m ora pozivaocu da vrati jedinstven broj svaki put kada bude pozvana:
//: paralelno/GeneratorSerijskihBrojeva.java public class GeneratorSerijskihBrojeva { private static volatile int serijskiBroj = 0; public static int sledeciSerijskiBroj() { return serijskiBroj++; // Nije bezbedno za vienitno izvravanje

} III-GeneratorSerijskihBrojeva je otprilike najjednostavnija klasa koja se m oe zamisliti, i ako ste dosad koristili C++ ili neki drugi jezik u kojem se program ira na niskom nivou, m oda oekujete da je poveanje za 1 (engl. increm ent ) atom ska operacija, poto se operacija poveanja za 1 u C + + -u esto moe realizovati kao jedna m ikroprocesorska instrukcija (iako ni to ne na nain pouzdan na svim platform am a). M eutim , ve sam rekao da u Javi poveanje za 1 nije atomska operacija jer obuhvata jedno itanje i jedno upisivanje, pa ak i u tako jednostavnoj operaciji im a prostora za problem e s nitim a. Kao to ete videti, ovde problem nije trenutna vidljivost rezultata; pravi problem je to to sled e S erijskiB roj( ) pristupa deljenoj promenljivoj vrednosti bez sinhronizacije.

Idcja iz knjige Efikasno programiranje na ]avi Joshue Blocha (M ikro knjiga, 2004), s. 168.

930

Misliti na Javi

Polje serijskiBroj im a m odifikator volatile zato to svaka nit moe da ima lokalni stek i da u njem u odrava kopije nekih promenljivih. Ako ispred definicije promenljive stavite m odifikator volatile, tim e kazujete prevodiocu da ne sprovodi optimizacije kojima bi se uklonila itanja i upisivanja koja polje odravaju u sinhronizaciji s lokalnim podacim a u nitim a. Zato se itanja i upisivanja obavljaju neposredno u glavnoj m emoriji, a ne u keu. M odifikator volatile ograniava i prevodioevo prerasporeivanje pristupa tokom optim izacije. M eutim , volatile ne utie na injenicu da poveanje za 1 nije atomska operacija. U sutini, definiite da je polje volatile ukoliko m u vie zadataka moe istovremeno pristupiti, a barem jedno od tih pristupanja je upisivanje. Prim era radi, polje koje se upotrebljava kao indikator za zaustavljanje zadatka m ora biti deklarisano kao volatile; u protivnom bi indikator m ogao biti keiran u nekom registru procesora, i ta vrednost se ne bi izm enila kada vi iz nekog drugog zadatka izm enite vrednost indikatora, pa prvi zadatak ne bi znao kada treba da se zaustavi. Za ispitivanje GeneratoraSerijskihBrojeva treba nam skup kojem nee ponestati mem orije ni u sluaju da pronalaenje problem a potraje. Ovde prikazani KruzniSkup po novo upotrebljava m em oriju u kojoj su bili uskladiteni celi brojevi, uz pretpostavku da e verovatnoa sukoba sa zam enjenim brojevim a biti m inim alna kada se s kraja skupa vratim o na njegov poetak. M etode a d d ( ) i co n tain s( ) sinhronizovane su da bi se spreiIo sudaranje niti:
//: paralelno/ProveraSerijskihBrojeva.java // Operacije prestaju da budu bezbedne // kada izvravanje postane vienitno. // {Args: 4} import java.uti1 .concurrent.*; // Ponovo upotrebljava isto pare memorije da je ne bi ponestalo: class KruzniSkup { private int[] niz; private int duzina; private int indeks = 0; public KruzniSkup(int velicina) { niz = new int[velicina]; duzina = velicina; // Inicijalizacija brojem koji GeneratorSerijskihBrojeva // ne proizvodi: for(int i = 0; i < velicina; i++) n iz [i] = -1;

}
public synchronized void add(int i) { ni z [i ndeks] = i ; // Vrati indeks na poetak niza i poni da prepisuje // nove elemente preko starih: indeks = ++indeks % duzina;

Poglavlje 2 1: Paralelno izvravanje

931

public synchronized boolean contains(int vre) ( for(int i = 0; i < duzina; i++) if(niz[i] == vre) return true; return false;

} }
public class ProveraSerijskihBrojeva { private static final int VELICINA = 10; private static KruzniSkup serijskibr = new KruzniSkup(lOOO); private static ExecutorService exec = Executors.newCachedThreadPool(); static class ProveraSerijskih implements Runnable { public void run() { while(true) { int serijski = GeneratorSeri jskihBrojeva.sledeciSeri jskiBroj (); i f(serijskibr.contains(serijski)) { System.out.println("Duplikat: " + serijski); System.exit(0);

}
serijskibr.add(serijski);

} } }
public static void main(Strin g [] args) throws Exception { for(int i = 0 ; i < VELICINA; i++) exec.execute(new ProveraSerijskih()); // Ako argument postoji, prestani nakon n sekundi: if(args.1ength > 0) { Timellni t .SECONDS.sl eep(new Integer(args[0])); System.out.println("Nijedan duplikat nije pronaen"); System.exit(0);

} }
} /* Ispis: (primer) Duplikat: 8468656

* ///:-

ProveraSerijskihBrojeva sadri statiki KruzniSkup koji obuhvata sve proi/vedene serijske brojeve i ugneenu klasu ProveraSerijskih koja proverava da li je svaki od tih serijskih brojeva jedinstven. Kada napravite vie zadataka koji se takm ie za dobijanje serijskih brojeva, videete da e jedan od njih pre ili kasnije dobiti postojei serijski broj (duplikat), sam o ga dovoljno dugo pustite da radi. Problem ete reiti sinhronizovanjem m etode sledeciSerijskiBroj( ). (Dodajte rezervisanu re synchronized ispred nje.) Ako ita, onda bi itanje i dodeljivanje prostih tipova trebalo da b u d u atom ske operacije. M eutim , kao to vidite iz program a TestNeprekidnosti.java, i dalje je lako pozvati

932

Misliti na Javi

atom sku operaciju koja pristupa objektu dok je o n u nekom nestabilnom m eustanju. D onositi bilo kakve zakljuke o tom e kakljivo je i opasno. N ajrazboritije je prosto pridravati se Brajanovog pravila za sinhronizaciju.

Veba 12: (3) Popravite TestNeprekidnosti.java pom ou rezervisane rei synchronized.


U m ete li da pokaete kako je program sada ispravan?

Veba 13: (1) Popravite program ProveraSerijskihBrojeva.java p o m o u rezervisane rei synchronized. U mete li da pokaete kako je program sada ispravan?

Atomske klase
Java SE5 je donela i posebne atom ske klase prom enljivih kao to su Atomiclnteger, AtomicLong, AtomicReference itd., koje obezbeuju atom sko uslovno auriranje oblika:
boolean uporediIAuriraj(oekivanaVrednost, novaVrednost);

O ne su nam enjene za fino podeavanje p ri kom e se koriste atom ske operacije (nepieIdnosti) dostupne na nekim od savrem enih procesora, pa o njim a po pravilu ne bi trebalo da razbijate glavu. Ponekad m ogu da poslue i za obino program iranje, ali opet tam o gde se radi o podeavanju perform ansi. Na prim er, izm eniem o TestN eprekidnosti.java tako da upotrebljava klasu A tom iclnteger:
//: paralelno/AtomicIntegerTest.java import java.util.concurrent.*; import java.uti1 .concurrent.atomic.*; import java.util.*; public class AtomicIntegerTest implements Runnable { private Atomiclnteger i = new AtomicInteger(O); public int dajVrednost() { return i.get(); } private void povecanjeZaDvaO { i .addAndGet(2); } public void run() { whi1e(true) povecanjeZaDvaf);

}
public static void main(String[] args) { new Timer(),schedule(new TimerTask() { public void run() { System.err.pri ntln("Preki dam"); System.exit(0);

}
}, 5000); // Zavri rad nakon 5 sekundi ExecutorService exec = Executors.newCachedThreadPool(); AtomiclntegerTest ait = new AtomicIntegerTest(); exec.execute(ait); while(true) { int vre = ait.dajVrednost(); i f (vre % 2 != 0) {

Poglavlje 21: Paralelno izvravanje

933

System.out.println(vre); System.exit(0);

} ) } } ///= U potrebom klase Atom iclnteger izbegli sm o korienje rezervisane rei synchronized. Poto ovaj program ne otkazuje, dodat je Tim er koji autom atski prekida rad nakon 5 sekundi. Evo kako izgleda M utexG eneratorParnih.java izm enjen tako da upotrebljava klasu

Atomiclnteger:
//: paralelno/AtomskiGeneratorParnih.java // Atomske klase se povremeno koriste i u obinim programima. // {RunByHand} import java.util.concurrent.atomic.*; public class AtomskiGeneratorParnih extends GeneratorCelobroj { private Atomiclnteger tekuciParniBroj = new AtomicInteger(O); public int next() { return tekuciParniBroj.addAndGet (2);

}
public static void main(String[] args) { ProveraParnosti.test(new AtomskiGeneratorParnih());

} } ///:1 opet sm o korienjem klase Atoiniclnteger izbegli sve ostale oblike sinhronizacije. Treba naglasiti da klase iz paketa Atomic slue za izgradnju klasa u biblioteci java.util.concurrent pa ih u svojim program im a upotrebljavajte sam o u posebnim okolnostima, a ak i tada sam o ukoliko moete da izbegnete sve druge m ogue problem e. O bino je bezbednije raiti s bravam a i zakljuavanjem (pom ou rezervisane rei synchronized ili eksplicitnih objekata tipa Lock).

Veba 14: (4) Pokaite da java.util.Tim er daje velike brojeve tako to ete napisati program koji generie m noge Tim er objekte to obavljaju neke jednostavne zadatke kad istekne vreme.

Kritini odeljci
Ponekad treba izolovati sam o deo koda u n u ta r m etode r u n ( ), a ne celu m etodu. Deo koji treba izolovati naziva se kritian (engl. critical), a rezervisana re synchronized se u kritinom delu program a koristi drugaije. U sledeem prim eru re synchronized zakljuava bravu odreenog objekta da bi sinhronizovala obuhvaeni blok koda:
synchronized(brava) { // Ovom kodu moe da pristupi samo jedna nit u jednom trenutku

93 4

Misliti na Javi

O buhvaeni blok koda naziva se sinhrortizovani blok, pre ulaska u sinhronizovani blok, raorate da zakljuate objekat brava. Ako je neka druga nit ve zakljuala taj objekat, u taj blok nije mogue ui sve dok ne bude otkljuan. U sledeem prim eru uporeena su oba p ristupa sinhronizaji. Pokazano je da se vrem e tokom kojega je objekat dostupan d rugim zadacim a znatno produava kada se koristi sinhronizovani blok um esto sinhronizovanja cele m etode. Pored toga, pokazano je kako se nezatiena klasa m oe upotrebiti u vienitnoj situaciji ukoliko je kontrolie i titi druga klasa:
//: paralelno/KriticanDeo.java // Sinhronizovanje blokova umesto celih metoda. Pokazuje i zatitu klase // nebezbedne u vienitnom izvravanju pomou druge koja je bezbedna. package concurrency; import java.util.concurrent.*; import java.util.concurrent.atomic.*; import java.util class Par { // Nije bezbedna u vienitnom izvravanju private int x, y; public Par(int x, int y) { this.x = x; this,y = y;

}
public Par() { this(0, 0); } public int dajX() { return x; } public int dajV() { return y; } public void povecajXzaJedan() { x++; } public void povecajVzaJedan() { y++; } public String toStringO { return "x: 1 1 + x + , y: + y;

}
public class IzuzetakBrojeviUParuNisuJednaki extends RuntimeException { public IzuzetakBrojeviUParuNisuJednaki() { super("Brojevi u paru nisu jednaki: " + Par.this);

} }
// Proizvoljnih, ali nepromenljivih vrednosti -// obe promenljive moraju biti jednake: public void checkState() { if(x != y) throw new IzuzetakBrojeviUParuNisuJednaki();

} }
// Zatita Para unutar klase bezbedne u vienitnom izvravanju: abstract class MenadzerPara {

Poglavlje 21: Paralelno izvravanje

935

Atomiclnteger brojacProvera = new AtomicInteger(O); protecte Par p = new Par(); private List<Par> skladiste = Col1ections.synchronizedLi st(new ArrayList<Par>()); public synchronized Par dajPar() { // Napraviu kopiju da bi original bio bezbedan: return new Par(p.dajX(), p .dajY());

}
// Pretpostaviu da je ova operacija dugaka protected void sacuvaj(Par p) { skladiste.add(p); try { TimeUnit.MILLISECONDS.sleep(50); } catch(InterruptedException ignore) {}

}
public abstract void povecajZaJedan();

}
// Sinhronizacija cele metode: class MenadzerParal extends MenadzerPara { public synchronized void povecajZaJedan() { p.povecajXzaJedan(); p.povecajYzaJedan(); sacuvaj ( daj P a r O ) ;

// Zatitiu samo kritian deo: class MenadzerPara2 extends MenadzerPara { public void povecajZaJedan() { Par privr; synchronized(this) { p.povecajXzaJedan(); p.povecajYzaJedan(); privr = dajPar();

}
sacuvaj (pri vr);

class RukovalacPara implements Runnable { private MenadzerPara mp; public RukovalacPara(MenadzerPara mp) { this.mp = mp;

}
public void run() { while(true) mp.povecajZaJedanO;

936

Misliti na Javi

public String toString() { return "Par: " + mp.dajPar() + " brojacProvera = " + mp.brojacProvera.get();

} }
class ProveraPara implements Runnable { private MenadzerPara mp; public ProveraPara(MenadzerPara mp) { this.mp = mp;

}
public void run() { while(true) { m p .brojacProvera.incrementAndGet (); mp.dajParO .checkState();

} } }
public class KriticanDeo { // Poreenje dva razliita pristupa: static void pristupiTestiranju(MenadzerPara menpl, MenadzerPara menp2) { ExecutorService exec = Executors.newCachedThreadPool(); RukovalacPara mpl = new RukovalacPara(menpl), mp2 = new RukovalacPara(menp2); ProveraPara proveraparal = new ProveraPara(menpl), proverapara2 = new ProveraPara(menp2); exec.execute(mpl); exec.execute(mp2); exec.execute(proveraparal); exec.execute(proverapara2); try { Timellni t.MILLISECONDS.sl eep(500); } catch(InterruptedException e) { System.out.println("Spavanje prekinuto");

}
System.out.println("mpl: " + mpl + "\nmp2: " + mp2); System.exi t (0);

}
public static void main(String[] args) { MenadzerPara menpl = new MenadzerParal(), menp2 = new MenadzerPara2(); pristupiTestiranju(menpl, menp2);

Poglavlje 21: Paralelno izvravanje

937

} /* Ispis: (primer) mpl: Par: x: 15, y: 15 brojacProvera = 272565 mp2: Par: x: 16, y: 16 brojacProvera = 3956974

* ///:Kao to rekoh, Par nije bezbedan u vienitnom izvravanju zato to njegova invarijanta (priznajem , sasvim proizvoljno) zahteva da obe prom enljive zadre istu vrednost. Sem toga, ve ste u p retho dn om delu poglavlja videli da operacije poveanja za jedan nisu bezbedne u vienitnom izvravanju, a poto nijedna od m etoda nije sinhronizovana, ne m oete oekivati da e objekat tipa Par ostati nepokvaren u vienitnom program u. Recimo da vam je neko dao klasu Par koja nije bezbedna u vienitnom izvravanju, a vi m orate da je upotrebite u vienitnom okruenju. To ete postii pravljenjem kiase MenadzerPara koja sadri objekat tipa Par i kontrolie pristupanje njemu. Vodite rauna o tom e da su jedine javne m etode d ajP ar( ), koja je sinhronizovana, i apstraktna m etoda povecajZ ajed an ( ). M etoda povecajZaJedan( ) bie sinhronizovana na m estu svoje realizacije. S truktura klase MenadzerPara, u kojoj funkcije realizovane u osnovnoj klasi upotrebljavaju jednu ili vie apstraktnih m etoda definisanih u izvedenim klasama, naziva se projektni obrazac Template M ethod.'5 Projektni obrasci om oguuju kapsuliranje prom ena u kodu; deo koji se ovde menja je metoda povecajZaJedan( ). U klasi MenadzerParal sinhronizovana je cela metoda povecajZaJedan( ), dok je u klasi MenadzerPara2 pom ou sinhronizovanog bloka sinhronizovan samo deo metode povecajZaJedan( ). O bratite panju na to da rezervisana re synchronized ne spada u potpis m etode i stoga se m oe dodati prilikom preklapanja. M etoda sacuvaj( ) dodaje olijekat tipa Par sinhronizovanoj listi tipa ArrayList, pa je ta operacija bezbedna u vienitnom izvravanju. Stoga nju ne m oram da titim , pa sam je sm estio izvan sinhronizovanog bloka u klasi MenadzerPara2. RukovalacPara je napravljen da bi pozivanjem m etode povecajZaJedan( ) iz zadatka testirao dve razliite realizacije apstraktne klase MenadzerPara, dok se ProveraPara obavlja iz drugog zadatka. Svaki put kada je ProveraPara uspena, ona brojacProvera povea za 1 i tako prati uestalost prilika da obavi testiranje. U metodi m a in ( ), prave se dva objekta tipa RukovalacPara koji neko vreme rade pa se potom prikazuju rezultati svakog od njih. Iako e se izlazni rezultati verovatno m nogo razlikovati od jednog do drugog izvravanja, videete da m etoda M enadzerPara).povecajZaJedan( ) nc dozvoljava klasi ProveraPara pristup ni priblino onoliko esto kao m etoda M enadzerPara2.povecajZaJedan( ) koja ima sinhronizovan blok i zato due radi bez zakljuavanja. O bino je ba to razlog za upotrebu sinhronizovanog bloka umesto sinhronizacije cele metode: om oguiti drugim zadacim a ei i dui pristup (koliko je to bezbedno). Za zatitu kritinih delova program a moete upotrebiti i eksplicitne objekte tipa Lock:
//: paralelno/EksplicitanKriticanDeo.java // Zatita kritinih delova programa eksplicitnim objektima tipa Lock. package concurrency; import java.uti1.concurrent.locks.*;

V ideti krtjigu Dcsigit P atterns ; a u to r je G am m a i dr. (Adison-W esley, 1995).

938

Misliti na Javi

// Sinhronizacija cele metode: class EksplicitanMenadzerParal extends MenadzerPara { private Lock brava = new ReentrantLock(); public synchronized void povecaj2aJedan() { brava.lock(); try { p.povecajXzaJedan(); p.povecajYzaJedan(); sacuvaj(dajPar()); } finally { brava . unlockO;

i }

// Kritian deo: class EksplicitanMenadzerPara2 extends MenadzerPara { private Lock brava = new ReentrantLock(); public void povecajZaJedan () { Par privr; brava.lockO; try { p.povecajXzaJedan(); p.povecajYzaJedan(); privr = dajPar(); } finally { brava.unlockO ;

}
sacuvaj(pri vr);

public class EksplicitanKriticanDeo { public static void main(String[] args) throws Exception { MenadzerPara menpl = new EksplicitanMenadzerParal(), menp2 = new EksplicitanMenadzerPara2(); KriticanDeo.pristupiTestiranju(menpl, menp2);

}
} /* Ispis: (primer) mpl: Par: x: 15, y: 15 brojacProvera = 174035 mp2: Par: x: 16, y: 16 brojacProvera = 2608588

* ///:Ponovo sm o upotrebili najvei deo program a KriticanDeo.java i napravili nove tipove od M enadzerPara koji upotrebljavaju eksplicitne objekte tipa Lock. EksplicitanMenadzerPara2 prikazuje pravljenje kritinog dela pom ou objekta tipa Lock; poziv metode sacuvaj( ) nalazi se izvan kritinog dela.

Poglavlje 21; Paralelno izvravanje

939

Sinhronizacija s drugim objektima


S inhronizovanom bloku m ora biti dat objekat s kojim e se sinhronizovati, i obino je za to najpam etnije upotrebiti tekui objekat za koji se m etoda poziva: synchronized(this); upravo to je u radeno u klasi MenadzerPara2. Na taj nain, ostale sinhronizovane m etode i kritini delovi objekta ne m ogu biti pozvani nakon zakljuavanja sinhronizovanog bloka. Dakle, uticaj sinhronizovanog bloka na kritian deo objekta, kada se sinhronizuje na this, svodi se sam o na sm anjenje opsega sinhronizacije. Katkada se sinhronizacija m ora obaviti s nekim drugim objektom, ali se u tom sluaju svi relevantni zadaci m oraju sinhronizovati sa istim objektom . U narednom p rim eru pokazano je da dva zadatka m ogu ui u objekat ije su m etode sinhronizovane s razliitim bravam a:
//: paralelno/ObjekatSinhronizacije.java // Sinhronizacija s drugim objektom. import static net.mindview.util.Print.*; class DvojnaSinhronizacija ( private Object objekatSinhronizacije = new Object(); public synchronized void f() { for(int i = 0; i < 5; i++) { print("f()"); Thread.yield();

) }
public void g() { synchroni zed(objekatSinhronizacije) { for(int i = 0 ; i < 5 ; i++) { pri n t ("g()"); Thread.yield();

} } }

public class ObjekatSinhronizacije { public static void main(String[] args) { final DvojnaSinhronizacija ds = new DvojnaSinhronizacija(); new Thread() { public void run() { ds.f();

}
}.start(); ds.g();

}
} /* Ispis: (primer)

g()
f()

g()

940

Misliti na Javi

f()
9 ()

f()
g()

f()
g()

f() * ///:-

DvojnaSinhronizacija.f( ) sinhronizuje na objekat this (sinhronizacijom cele m etode), a g ( ) im a sinhronizovan blok koji sinhronizuje na objekatSinhronizacije. Dakle, te dve sinhronizacije su nezavisne. To je dokazano u m etodi m a in ( ) pravljenjem niti (objekta tipa Thread) koja poziva f ( ). Glavna nit - m etode m a in ( ) - upotrebljena je za poziv g ( ). Iz rezultata vidite da se obe m etode izvravaju istovrem eno, dakle sinhronizacija jedne ne blokira onu drugu. Veba 15: (1) Napravite klasu s tri m etode koje sadre kritine delove, a sve su
sinhronizovane sa istim objektom . N apravite vie zadataka kako biste pokazali da se u svakom trenutku moe izvravati sam o jedan od njih. Potom m odifikujte m etode tako da se sinhronizuju s razliitim objektim a i pokaite da se sve tri m etode m ogu izvravati istovremeno.

Veba 16: (1) Izmenite vebu 15 tako da se upotrebljavaju eksplicitni Lock objekti.

Lokalno skladite niti


D rugi nain da spreite sudare zadataka zbog deljenih resursa jeste da izbegnete deljenje prom enljivih. Lokalno skladite n iti je m ehanizam koji za svaku nit koja pristupa objektu autom atski pravi novo skladite za istu prom enljivu. Dakle, ako pet niti pristupa objektu koji im a prom enljivu x, lokalno skladite niti generie pet razliitih skladita za x. U sutini, ta skladita om oguuju da svakoj niti pridruite njeno stanje. Pravljenjem i upravljanjem lokalnim skladitem niti bavi se klasa java.lang.T hreadL ocal, kao to ete videti u narednom prim eru:
//: paralelno/ThreadLocalSpremnikPromenljivih .java // Svaka nit automatski dobija svoje skladite. import java.uti1 .concurrent.*; import java.util.*; class Pristup implements Runnable { private final int id; public Pristup(int idn) { id = idn; } public void run() { while(!Thread.currentThread().islnterrupted()) { ThreadLocalSpremni kPromenlj ivi h.povecajZaJedan(); System.out.println(thi s); Thread.yield();

Poglavlje 2 1: Paralelno izvravanje

941

public String toString() { return "#" + id + ": " + ThreadLocalSpremni kPromenljivih.get();

} }
public class ThreadLocalSpremnikPromenljivih { private static ThreadLocal<Integer> vrednost = new ThreadLocal<Integer>() { private Random slucajan = new Random(47); protected synchronized Integer initialValue() return slucajan.nextlnt(10000);

} };
public static void povecajZaJedan() { vrednost.set(vrednost.get() + 1);

}
public static int get() { return vrednost.get(); } public static void main(String[] args) throws Exception { ExecutorService exec = Executors.newCachedThreadPool (); for(int i = 0 ; i < 5 ; i++) exec.execute(new Pristup(i)); TimeUnit.SEC0NDS.sleep(3); // Radi neko vreme exec.shutdownNow(); // Zavrie se svi Pristupi

}
} /* Ispis: (primer) #0: 9259 #1: 556 #2: 6694 #3: 1862 #4: 962 #0: 9260 #1: 557 #2: 6695 #3: 1863 #4: 963

* ///:Objekti tipa ThreadLocal obino se skladite kao statika polja. Kada napravite objekat tipa ThreadLocal, sadraju tog objekta moete pristupiti sam o m etodam a g e t( ) i s e t( ). Metoda g e t( ) vraa kopiju objekta pridruenu toj niti, dok s e t( ) umee svoj argum ent u objekat uskladiten za tu nit i vraa stari objekat koji je bio u tom skladitu. To pokazuju m etode povecajZaJean( ) i g e t( ) program a ThreadLocalSpremnikPromenljivih. O bratite panju na to da m etode povecajZaJedan( ) i g e t( ) nisu sinhronizovane, zato to ThreadLocal jemi da nee doi do uslova za trku. Kada pokrenete ovaj program , videete dokaz da se svakoj niti dodeljuje njeno sopstveno skladite, poto svaka od njih odrava sopstveni broja iako postoji sam o jedan objekat ThreadLocalSpremnikPromenljivih.

942

Misliti na Javi

Gaenje zadataka
U nekim a od prethodnih prim era, m etode cancel( ) i isCanceIled( ) sm etene su u klasu vidljivu svim zadacima. Zadaci ispituju vrednost m etode isC ancelled( ) da bi znali kada da se ugase. To je razborit pristup problem u. M eutim , u nekim situacijam a zadatak m ora biti ugaen naglije. U ovom odeljku saznaete sve o takvom gaenju i problem im a zbog njega. Prvo, razm otrim o prim er koji ne sam o da pokazuje problem gaenja, nego je i dodatan prim er deljenja resursa.

Ukrasna bata
U ovoj simulaciji, batenska komisija bi htela da zna koliko Ijudi svakog dana ue u batu kroz neku od njenih kapija. Svaka kapija im a blokirajuu vrteku ili neku dru g u vrstu brojaa, i nakon poveanja brojaa blokirajue vrteke za 1, poveava se i zajedniki broja koji predstavlja ukupan broj posetilaca bate.
//: paralelno/UkrasnaBasta.java import java.util.concurrent.*; import java.util import static net.mindview.uti1 .Print.*; class Broj { private int broj = 0; private Random slucajan = new Random(47); // Ako uklonite rezervisanu re synchronized, // prebrojavanje e zakazati: public synchronized int povecajZaJedan() { int privr = broji; if(slucajan.nextBoolean()) // Pola vremena prepusti drugima Thread.yield(); return (broj = ++privr);

}
public synchronized int value() { return broj; }

}
class Ulaz implements Runnable { private static Broj broj = new Broj(); private static List<Ulaz> ulazi = new ArrayList<Ulaz>(); private int broj = 0; // Sinhronizacija nije potrebna za itanje: private final int id; private static volatile boolean cancelled = false; // Atomska operacija na volatile polju: public static void cancel() { cancelled = true; } public Ulaz(int id) { this.id = id;

Poglav[je 21: Paralelno izvravanje

943

// Dri ovaj zadatak u listi. Takoe spreava // da mrtvi zadaci budu pokupljeni kao smee: ulazi .add(this);

}
public void run() { while(!cancelled) { synchronized(this) { ++broj;

}
print(this + " Ukupno: " + broj.povecajZaJedan()); try { TimeUnit.MILLISECONDS.sleep(lOO); } catch(InterruptedException e) { print("spavanje prekinuto");

} }
print("Zaustavljam " + this);

}
public synchronized int dajVrednost() { return broj; } public String toStringO { return "Ulaz " + id + ": " + dajVrednost();

}
public static int dajUkupanBroj () { return broj.value();

}
public static int saberiU1aze() { int zbir = 0; for(Ulaz entrance : ulazi) zbir += entrance.dajVrednostO ; return zbir;

public class UkrasnaBasta { public static void main(String [] args) throws Exception { ExecutorService exec = Executors.newCachedThreadPool(); for(int i = 0; i < 5; i++) exec.execute(new Ulaz(i)); // Radi neko vreme, zatim stani i prikupi podatke: TimeUni t .SECONDS.sleep(3); U1az.cancel(); exec.shutdown(); if (!exec.awaitTermination(250, TimeUnit.MILLISECONDS)) print("Neki zadaci nisu ugaeni!"); print("Ukupno: " + U1az.dajUkupanBroj ()); pr in t fZb i r ulaza: " + Ulaz.saberiUlazeO);

}
} /* Ispis: (primer) Ulaz 0: 1 Ukupno: 1 Ulaz 2: 1 Ukupno: 3

94 4

Misliti na Javi

Ulaz Ulaz Ulaz Ulaz Ulaz Ulaz

1: 4: 3: 2: 4: 0:

1 1 1 2 2 2

Ukupno: Ukupno: Ukupno: Ukupno: Ukupno: Ukupno:

2 5 4 6 7 8

Ulaz 3: 29 Ukupno: 143 Ulaz 0: 29 Ukupno: 144 Ulaz 4: 29 Ukupno: 145 Ulaz 2: 30 Ukupno: 147 Ulaz 1: 30 Ukupno: 146 Ulaz 0: 30 Ukupno: 149 Ulaz 3: 30 Ukupno: 148 Ulaz 4: 30 Ukupno: 150 Zaustavljam Ulaz 2 : 30 Zaustavljam Ulaz 1: 30 Zaustavljam Ulaz 0 : 30 Zaustavljam Ulaz 3 : 30 Zaustavl jam Ulaz 4 : 30 Ukupno: 150 Zbir ulaza: 150

* ///:Jedan objekat tipa Broj pam ti ukupan broj posetilaca bate i uva se kao statiko polje klase Ulaz. M etode Broj.povecajZaJedan( ) i B roj.valuef ) sinhronizovane su da bi se kontrolisalo pristupanje polju broj. M etoda povecajZaJedan( ) upotrebljava objekat tipa Random da bi m etodom y ie ld ( ) drugim zadacim a prepustila priblino pola vremena, izm eu upisivanja broja u p rivr i poveanja za 1 i upisivanja promenljive privr nazad u broj. Ako rezervisanu re synchronized izbacite iz definicije m etode povecajZaJedan( ), program otkazuje zato to vie zadataka istovrem eno pristupa i m enja broj. (Poziv m etode y ie ld ( ) to sam o ubrzava.) Svaki (zadatak) Ulaz uva svoj lokalni broj; on pokazuje koliko je posetilaca ulo kroz taj ulaz. Time se moe kontrolisati da li objekat broj parnti taan broj posetilaca. U laz.ru n ( ) poveava broj i objekat broj i zatim spava 100 milisekundi. Poto je Ulaz.cancelled indikator tipa boolean s m odifikatorom volatile nad kojim se obavljaju sam o operacije itanja i dodeljivanja (i koji se nikada ne ita u kombinaciji s drugim poljim a), m oem o se izvui a da ga ne sinhronizujem o. Ukoliko ste iole nesigurni u takvoj situaciji, bolje upotrebite synchronized. Ovaj program se prilino m nogo trud i da sve pogasi na stabilan nain. Tako sam ga napisao da bih vam pokazao koliko paljivo m orate gasiti vienitni program i to koliko je vredna m etoda in te rru p t( ) o kojoj e uskoro biti vie rei. Nakon 3 sekunde, m a in ( ) alje statiku poruku cancel( ) na Ulaz, zatim poziva sh u td o w n ( ) za objekat exec (izvrilac), a onda poziva m etodu aw aitTerm ination( ) za taj exec. ExecutorService.aw aitTerm ination( ) eka da se svi zadaci zavre i vraa true ako se svi zavre pre isteka zadatog vrem ena ekanja; u protivnom vraa false, to pokazuje da svi zadaci nisu zavreni. Iako ovo prouzrokuje da svi zadaci izau iz svojih m etoda r u n ( )

Poglavlje 21: Paralelno izvravanje

945

i stoga zavre svoje izvravanje, objekti tipa Ulaz i dalje su validni zato to je u konstrukto ru svaki objekat Ulaz uskladiten u statikoj listi ulazi tipa List<Ulaz>. Zato saberiU laze( ) i dalje radi s validnim objektim a Ulaz. D ok ovaj program bude radio, prikazivae ukupan broj posetilaca i broj posetilaca na svakom ulazu koji p ro u kroz blokirajuu vrteku. Ako uklonite deklaraciju synchronized ispred m etode Broj.povecajZaJedan( ), videete da ukupan broj posetilaca nije jednak oekivanom . Broj posetilaca koji se prebroji na svim blokirajuim vrtekam a nee biti jednak iznosu prom enljive broj. Sve ispravno radi dok postoji uzajam no iskljuiva brava (m utex) koja sinhronizuje pristupanje objektu Broj. Imajte u vidu da m etoda Broj.povecajZaJedan( ) pom ou prom enljive p riv r i m etode y ield ( ) n am erno poveava m ogunost otkazivanja. U pravim vienitnim problem im a, m ogunost otkazivanja m oe biti statistiki mala, pa ete lake poverovati u zabludu da va program radi ispravno. Kao u gornjem prim eru, verovatno e postojati i skriveni problem i kojih niste svesni, pa budite izuzetno paljivi kada pregledate program e za paralelno izvravanje.

Veba 17: (2) N apravite Gajgerov broja koji m oe im ati proizvoljan broj daljinskih
senzora.

Gaenje blokiranih zadataka


U p reth odnom p rim eru, m etoda U la z .ru n () u svojoj petlji obuhvata i poziv m etode s le e p ( ). Z nam o da e se m etoda s le e p () kad-tad probu d iti i zadatak e doi na vrh petlje, gde nakon provere indikatora cancelled moe da izae iz nje. M eutim , spavanje prouzrokovano m etodom s le e p () sam o je jedna od situacija u kojim a je izvravanje zadatka blokirano, a ponekad se i zadatak koji je blokiran m ora ugasiti.

Stanja niti
Nit m oe biti u jed no m od etiri stanja: 1. Nova: Nit je u ovom stanju privrem eno, dok je prave. O na dodeljuje potrebne sistemske resurse i obavlja inicijalizaciju. O d tog trenutka postaje podobna za prim anje procesorskog vrem ena. Potom m ehanizam za raspodelu vrem ena menja stanje niti u sprem na za izvravanje ili blokirana. 2. Sprem na za izvravanje: Znai da se nit m oe pokrenuti kada m ehanizam podele procesorskog vrem ena bude im ao slobodnih procesorskih ciklusa za nju. M oda se ta nit izvrava, a m oda i ne, ali nita ne m oe spreiti njeno izvravanje ukoliko joj m ehanizam za raspodelu vrem ena dodeli procesor. D rugim reima, nit nije ni m rtva ni blokirana. 3. Blokirana: Nit bi mogla da se izvrava, ali je neto spreava. D ok je nit blokirana, m ehanizam za raspodelu vrem ena je preskae i ne dodeljuje joj procesor. Sve dok ta nit ponovo ne postane sprem na za izvravanje, nee biti pokretana.

4. M rtva: M rtva ili ugaena (engl. term inated) nit vie nije podobna za rasporeivanje
i procesorsko vrem e joj se ne dodeljuje. Njen zadatak je zavren i ona se vie ne moe pokrenuti. Zadatak norm alno um ire kada se vrati iz svoje m etode r u n ( ), ali i njegova nit m oe biti prekinuta, kao to ete uskoro videti.

946

Misliti na Javi

Prelazak u stanje blokiranosti


Z adatak m oe postati blokiran iz jednog od sledeih razloga: Uspavali ste taj zadatak pozivom m etode sleep(m ilisekundi) i on se nee izvravati tokom zadatog vremena. Zaustavili ste izvravanje niti m etodom w a it( ). N it nee postati sprem na za izvravanje sve dok ne dobije p o ruk u n o tify ( ) ili notifyA ll( ) (ili ekvivalentnu poru k u sig n al( ) odnosno signalA ll( ) za alatke iz Java SE5 biblioteke java.util.concurrent). O bjasniu to u jednom od kasnijih odeljaka. N it eka da se zavri neka ulazno/izlazna operacija. N it pokuava da pozove neku sinhronizovanu m etodu drugog objekta koji je zakljuan.

U starim program im a jo se m ogu videti pozivi m etoda su sp e n d ( ) i resum e( ) za blokiranje odnosno deblokiranje niti, ali su one zastarele u savremenoj Javi (poto su umele da izazovu uzajam nu blokadu), pa ih u ovoj knjizi neu opisivati. Zastarela je i m etoda s to p ( ) zato to ne otkljuava brave koje je n it zakljuala, pa ako su objekti u nedoslednom stanju (,,oteeni ), ostali zadaci ih u tom stanju m ogu videti i modifikovati. Iz toga se mogu izroditi suptilni problem i koji se teko otkrivaju. R azm otriem o sada ovaj problem : ponekad treba ugasiti zadatak koji je blokiran. Ako ne m oete saekati da on doe do mesta u kodu gde moe da proveri neko stanje i sam odlui da se ugasi, m orate ga p rim orati da izae iz stanja blokiranosti.

Prekidanje izvravanja
Kao to m oete zamisliti, m nogo je tee izai iz m etode R unnable.run( ) pre njenog kraja nego saekati da ona dospe do mesta gde ispituje vrednost indikatora ,,otkai ili do nekog drugog m esta gde je program er sprem an da napusti tu m etodu. Kada izaete iz blokiranog zadatka, esto treba da poistite resurse. Zato izlazak iz m etode r u n ( ) datog zadatka pre njenog kraja, najvie lii na bacanje izuzetka, pa se u Javinim nitim a za tu vrstu prekidanja koriste izuzeci.1 6 (Ovo je na samoj ivici nekorektne upotrebe izuzetaka, poto znai da ih esto upotrebljavate za kontrolu toka program a.) Da biste se prilikom ovakvog gaenja zadatka vratili na neko od poznatih dobrih stanja, m orate paljivo razm otriti putanje izvravanja koda i napisati odredbu catch tako da pravilno sve poisti. Klasa Thread sadri m etodu in te rru p t( ) koja slui za gaenje blokiranih zadataka. O na niti daje status prekinute. Nit kojoj je postavljen status prekinuta baca izuzetak InterruptedException ukoliko je ve blokirana ili pokuava da izvri operaciju koja prouzrokuje blokiranje. Status prekinuta bie resetovan kada se baci izuzetak ili kada zadatak pozove m etodu T h read .interru p ted ( ). Kao to ete videti, m etoda T hread.interrupted( ) prua drugi nain izlaska iz petlje r u n ( ), bez bacanja izuzetka.

16 M e u tim , izuzeci se nikada ne generiu asinhrono. Stoga n en ia o p asn osti da neto prekine instrukciju/poziv m eto d e. Ukoliko p rilikom u p o treb e m utexa (za razliku o d rezervisane rei sy n c h ro n ized ) b u d ete upotrebljavali idiom try-fin ally, te u zajam no iskljuive brave (m utex i) bie autom atski otk ljuane kada se baci izuzetak.

Poglavlje 21: Paralelno izvravanje

947

Da biste pozvali in te rru p t( ), m orate im ati nit, tj. objekat tipa Thread. M oda ste uoili da nova biblioteka concurrent (za paralelno izvravanje) kao da izbegava neposredan rad s nitim a; ona sve pokuava da ostvari izvriocima. Ako pozovete shutdow nN ow ( ) za nekog izvrioca, on e poslati poziv in te rru p t( ) svim nitim a koje je pokrenuo. To im a smisla, poto najee hoem o odjednom da ugasim o sve zadatke odredenog izvrioca, kada zavrimo deo projekta ili ceo program . M eutim , im a sluajeva kada bism o da ugasim o sam o jedan zadatak. Ako radite sa izvriocem, kontekst zadatka koji ste pokrenuli m oete da uhvatite tako to ete pozvati m etodu su b m it( ) um esto m etode execute( ). s u b m it( ) vraa generiki objekat Future<?> s nespeficiranim param etrom , zato to za taj objekat nikada n'eete zvati g e t( ) - svrha posedovanja ovakvog objekta tipa Future jeste da za njega m oete pozvati cancel( ) i tako ga upotrebiti za prekidanje odreenog zadatka. Ukoliko m etodi cancel( ) prosledite true, ona im a dozvolu da pozove in te rru p t( ) za tu nit da bi je prekinula; dakle, m etoda cancel( ) predstavlja jedan od naina prekidanja niti koju je pokrenuo izvrilac. U narednom prim eru videete osnove upotrebe m etode in te rru p t( ) u izvriocu:
//: paralelno/Prekidam.java // Prekidam blokirane niti. import java.util.concurrent.*; import java.io.*; import static net.mindview.util.Print.*; class BlokiranoSpavanjem implements Runnable { public void run() { try { TimeUni t.SECONDS.sleep(100); } catch(InterruptedException e) { pri nt ("InterruptedException");

}
print("Izlazim iz niti BlokiranoSpavanjem.runO");

} }
class BlokiranoUIOperacijom implements Runnable { private UlazniTok u l ; public B1okiranoUIOperacijom(UlazniTok ut) { ul = ut; } public void run() { try { print("ekamo na read():"); ul .read(); } catch(IOException e) { if(Thread.currentThread().isInterrupted()) { print("Prekinuto iz blokirane U/I operacije"); } else { throw new RuntimeException(e);

948

Misliti na Javi

print("Izlazim iz niti BlokiranoUIOperacijom.run()");

} }
class SinhronizovanoBlokiranje implements Runnable { public synchronized void f() { while(true) // Nikada ne otkljuava bravu Thread.yield();

}
public SinhronizovanoBlokiranje() { new T h r e a d O { public void run() { f(); // Ova nit zakljuava bravu

}
}.start();

}
public void run() { print("Pokuavam da pozovem f()"); f(); print("Izlazim iz niti SinhronizovanoBlokiranje.run()");

} }
public class Prekidam { private static ExecutorService exec = Executors.newCachedThreadPool(); static void test(Runnable r) throws InterruptedException{ Future<?> f = exec.submit(r); Timellnit .MILLISECONDS.sle ep(100); print("Prekidam + r.getClass() .get N a m e O ) ; f .cancel(true); // Prekida u sluaju izvravanja print("Prekid poslat objektu " + r.getClass() . g e t N a m e O ) ;

}
public static void main(String[] args) throws Exception { test(new BlokiranoSpavanjem()); test(new B1okiranoUIOperacijom(System.ul)); test(new SinhronizovanoBlokiranje()); TimeUnit.SECONDS.sleep(3); print("Prekidam uz System.exit(0)"); System.exit(0); // ... poto su poslednja 2 prekida zakazala

}
} /* Ispis: (95% podudaranja) Prekidam B1okiranoSpavanjem InterruptedException Izlazim iz niti BlokiranoSpavanjem.run() Prekid poslat objektu BlokiranoSpavanjem ekamo na read(): Prekidam BlokiranoUIOperacijom Prekid poslat objektu BlokiranoUIOperacijom

Poglavlje 21: Paralelno izvravanje

949

Pokuavam da pozovem f() Preki dam Si nhroni zovanoBloki ranje Prekid poslat objektu SinhronizovanoBlokiranje Prekidam uz System.exit(0)

* ///:Svaki zadatak predstavlja razliitu vrstu blokiranja. BlokiranoSpavanjem je prim er blokiranja koje se m oe prekinuti, dok BlokiranoUIOperacijom i SinhronizovanoBlokiranje nije m ogue prekinuti.1 7 Prethodni program dokazuje da se ulazno/izlazne operacije i ekanje na sinhronizovanu bravu ne m ogu prekinuti, ali ste to mogli predvideti i nakon pregledanja koda, poto za ulazno/izlazne operacije i ekanje na sinhronizovane m etode nije potreban blok za obradu izuzetka InterruptedException. Prve dve klase su jednostavne: m etoda r u n ( ) poziva sleep( ) u prvoj klasi i re a d ( ) u drugoj. M eutim , da bism o pokazali SinhronizovanoBlokiranje, najpre m oram o zakljuati bravu. To postiem o u konstruktoru, tako to se napravi instanca an o n im n e klase Thread koja zakljuava objekat pozivom m etode f ( ) (nit m ora biti neka druga a ne ona koja izvrava r u n ( ) za SinhronizovanoBlokiranje, zato to jedna nit m oe vie puta zakljuati objekat). Poto se f ( ) nikada ne vraa, ta brava se nikada ne otkljuava. SinhronizovanoBIokiranje.run( ) pokuava da pozove f ( ) i blokirano je dok eka da brava bude otkljuana. Iz rezultata vidite da poziv m etode sleep( ) moete prekinuti (kao i svaki drugi poziv koji zahteva hvatanje izuzetka InterruptedException). M edutim , ne m oete prekinuti zadatak koji pokuava da zakljua sinhronizovanu bravu, niti objekat koji pokuava da obavi ulazno/izlaznu operaciju. To je pom alo uznem irujue, naroito ako piete zadatak koji treba da obavlja ulazno/izlazne operacije, poto to znai da one m ogu da zakljuaju vienitni program . To je razlog za brigu, pogotovo ako se radi o program im a za Web. Dosta sloeno, ali ponekad efikasno reenje ovog problem a jeste zatvaranje pripadnog resursa na kojem je zadatak blokiran:
//: paralelno/ZatvoriResurs.java // Prekidam blokirani zadatak zatvaranjem pripadnog resursa. // {RunByHand} import java.net.*; import java.util.concurrent.*; import java.io.*; import static net.mindview.uti1.Print.*; public class ZatvoriResurs { public static void main(String[] args) throws Exception { ExecutorService exec = Executors.newCachedThreadPool (); ServerSocket server = new ServerSocket(8080); UlazniTok ulazlzllticnice = new Socket("localhost", 8080).getInputStream(); exec.execute(new BlokiranoUIOperacijom(ulazIzUticnice)); 1 Neka izdanja JDK podravaju i In te rru p tc d IO E x c e p tio n . M e u tim , taj izuzetak je te k d e lim i n o realizovan, i to sanio na nekim platform am a. Bacanje tog izuzetka p ro u zro k u je da U /I o bjekat postane neupotrebljiv. M alo je Verovatno da e bu d u a izdanja podravati taj izuzetak.

950

Misliti na Javi

exec.execute(new BlokiranoUIOperacijom(System.in)); TimeUni t.MILLISECONDS.sleep(lOO); print("Gasim sve niti"); exec.shutdownNow(); TimeUnit.SECONDS.sleep(l); printC'Zatvaram " + ul azIzUticnice.getClass() . g e t N a m e O ) ; ulazIzUticnice.close(); // Oslobaa blokiranu nit TimeUnit.SECONDS.sleep(l); print("Zatvaram " + System.in.getClass().getName()); System.in.close(); // Oslobaa blokiranu nit

}
} /* Ispis: (85% podudaranja) ekamo na read(): ekamo na read(): Gasim sve niti Zatvaram java.net.SocketlnputStream Prekinuto iz blokirane U/I operacije Izlazim iz niti BlokiranoUIOperacijom.run() Zatvaram java.io.BufferedlnputStream Izlazim iz niti B1okiranoUIOperacijom.run()

* ///:Nakon poziva m etode shutdow nN ow ( ), odlaganjim a pre poziva m etode close( ) za dva ulazna toka naglaava se da se zadaci deblokiraju posle zatvaranja odgovarajuih resursa. M oda ste prim etili da se in te rru p tf ) pojavljuje kada zatvaram o Socket, ali ne i kada zatvaram o System.in. Sreom, civilizovaniji prekid ulaz.no/izlaznih operacija om oguuju nio klase predstavljene u poglavlju o ulazno/izlaznim operacijam a. Blokirani nio kanali autom atski reaguju na prekide:
//: paralelno/NIOPrekid.java // Prekidam blokiran NIO kanal. import java.net.*; import java.nio.*; import java.nio.channels.*; import java.uti1 .concurrent.*; import java.io.*; import static net.mindview.util.Print.*; class NIOBlokiran implements Runnable { private final KanalUticnice ku; public NIOBlokiran(KanalUticnice ku) { this.ku = ku; | public void run() { try { print("ekamo na read() u 1 1 + this); ku.read(ByteBuffer.al 1 ocate(l)); } catch(C1osedByInterruptException e) { pri nt("ClosedByInterruptException"); } catch(AsynchronousCloseException e) { pri nt C'AsynchronousCl oseException");

Poglavlje 21. Paralelno izvravanje

951

} catch(IOException e) { throw new RuntimeException(e);

}
print(''Izlazim iz niti NIOBlokiran.run() " + this);

} }
public class NlOPrekid { public static void main(String[] args) throws Exception { ExecutorService exec = Executors.newCachedThreadPool(); ServerSocket server - new ServerSocket(8080); InetSocketAddress isa = new InetSocketAddress("localhost", 8080); KanalUticnice kul = KanalUticnice.open(isa); KanalUticnice ku2 = KanalUticnice.open(isa); Future<?> f = exec.submit(new NIOBlokiran(kul)); exec.execute(new NIOBlokiran(ku2)); exec.shutdown();
TimeUn it .S EC ON DS .s lee p( l); // Napravi prekid otkazivanjem operacije: f. c a nc el (t ru e); Time Un it .S EC ON DS .s lee p( l); // Deblokiraj zatvaranjem kanala: } ku2. cl os e( ); (primer) } /* Ispis:

ekamo na read() u NIOBlokiran@7a84e4 ekamo na read() u NIOBlokiran@15c7850 ClosedByInterruptException Izlazim iz niti N I 0 B 1 o k i r a n .r u n () NI0B1okiran@15c7850 AsynchronousCloseExcepti or Izlazim iz niti NI OB lo ki ra n. ru n() NIOBlokiran@7a84e4

* ///:Kao to vidite, deblokiranje moete izvriti i zatvaranjem pripadnog kanala, m ada bi to retko trebalo da bude neophodno. Vodite rauna o tom e da biste pokretanjem oba zadatka m etodom e x e c u te () i pozivom m etode e.sh u td o w n N o w () lako sve pogasili; hvatanje objekta tipa F uture u gornjem prim eru bilo je potrebno samo da bi se izuzetak poslao jednoj niti, a ne i onoj drugoj.1 8 Veba 18: (2) N apravite klasu bez zadataka, ali s m etodom koja poziva s le e p () za dugako razdoblje. Napravite zadatak koji poziva tu m etodu u klasi bez zadataka. U m etodi m a in ( ) pokrenite zadatak, a zatim pozovite in te r r u p t( ) da biste ga ugasili. Treba da postignete da se zadatak ugasi bezbedno. Veba 19: (4) Izmenite program U krasnaB asta.java tako da upotrebite m etodu in te r r u p t( ). Veba 20: (1) Izmenite program C achedT hreadPooI.java tako da svi zadaci prim e in te r r u p t( ) pre nego to budu zavreni.
Pri istraivanju grae za ovaj odeljak pom ogao m i je E rvin Varga.

952

Misliti na Javi

Blokiranost koju izaziva uzajamno iskljuiva brava (mutexj


Kao to ste videli u program u P rekidanje.java, ako pokuate da pozovete sinhronizovanu m etodu za zakljuani objekat, pozivajui zadatak e biti zaustavljen (blokiran) dok se objekat ne otkljua. U sledeem p rim eru videete kako jedan zadatak m oe vie puta zakljuati istu uzajam no iskljuivu bravu (m utex):
//: paralelno/VisePutaZakljucanaBrava.java // Nit moe vie puta da zakljua istu bravu. import static net.mindview.util.Print.*; public class VisePutaZakljucanaBrava { public synchronized void fl(int broj) { if(broj-- > 0) { print("f 1 () poziva f2() uz broj " + broj); f2(broj);

} }
public synchronized void f2(int broj) { if(broj-- > 0) { print("f2() poziva fl() uz broj " + broj); fl(broj);

} }
public static void mair,(String[] args) throws Exception { final VisePutaZakljucanaBrava visePutaZakljucanaBrava = new VisePutaZakljucanaBrava(); new Thread() { public void run() { visePutaZakljucanaBrava.fi(10);

}
}.start();

}
} /* Ispis: fl() pozi va f2() uz f2() poziva f i o uz fl() pozi va f2() uz f2 () pozi va fl() uz fl() pozi va f2 () uz f2() pozi va f i o uz fl() pozi va f2 () uz f2() pozi va f i o uz f i o poziva f2 () uz f2() poziva f i o uz *///: 9 8 7 6 5 4 3 2 1 broj 0 broj broj broj broj broj broj broj broj broj

U metodi m a in ( ) se pravi nit koja poziva f l ( ), a zatim se f l ( ) i f 2 ( ) uzajam no pozivaju dok broj ne postane jednak nuli. Poto je zadatak ve zakljuao bravu objekta visePutaZ akljucanaB rava unutar prvog poziva m etode f l ( ), isti zadatak je ponovo zakljuava u

Poglavlje 2 1: Paralelno izvravanje

953

pozivu m etode f 2 ( ) itd. To im a smisla zato to bi zadatak trebalo da moe da poziva sinhronizovane m etode u n u tar istog objekta; taj zadatak je ve zakljuao objekat (bravu). Kao to je ve bilo reeno za ulazno/izlazne operacije koje nije mogue prekinuti, kad god zadatak m oe biti tako blokiran da ga se ne m oe prekinuti, postoji m ogunost da e to zakljuati ceo program . U biblioteke za paralelno izvravanje Jave SE5 dodata je i m ogunost prekidanja zadataka blokiranih na bravam a (objektim a) tipa R eentrantL ock, za razliku od zadataka blokiranih na sinhronizovanim m etodam a ili kritinim delovima:
//: paralelno/Prekidam2.java // Prekidam zadatak blokiran pomou objekta tipa ReentrantLock. import java.uti1 .concurrent.*; import java.util.concurrent.locks.*; import static net.mindview.util.Print.*; class BlokiranMutex { private Lock brava = new ReentrantLock(); public BlokiranMutex() {
// Odmah emo ga zakljuati, da bismo pokazali prekidanje // zadatka blokiranog na bravi Re en tr an tL oc k: br av a.1o c k ( ) ;

}
public void f() try { // Drugim zadacima ovo nikada nee biti dostupno b r av a.1o c k l n t e r r u p t i b l y (); // Poseban poziv print("zakljuana brava u f ()"); } catch(InterruptedException e) { p r i n t ("Prekinuto u pokuaju zakljuavanja brave u f ()"); {

} } }
class Blokirana2 implements Runnable { B1 okiranMutex blokiran = new B1 ok ir an Mu te x(); public void run() blokiran.fO; p r i n t ("Izaao iz blokiranog poziva"); { print("ekamo na f() u klasi B l o k i r an Mu te x" );

} }
public class Prekidam2 { public static void m a i n ( S t r i n g [] args) throws Exception { Thread n = new Thread(new Bl ok ir an a2 () ); n.startO ; Timellnit.SECONDS.sleep(l);

System.out.println("Pozivam n .interrupt()"); n.interrupt();

954

Misliti na Javi

} /* Ispis: ekamo na f() u klasi BlokiranMutex Pozivam n.interrupt() Prekinuto u pokuaju zakljuavanja brave u f() Izaao iz blokiranog poziva

* ///Klasa BlokiranMutex im a k onstruktor koji zakljua bravu (objekat tipa Lock) samog objekta i nikada je ne otkljua. Zbog toga ete, ukoliko pokuate da pozovete f ( ) iz nekog drugog zadatka (razliitog od onoga koji je napravio objekat BlokiranMutex), uvek biti blokirani poto je ta uzajam no iskljuiva brava stalno zakljuana. U klasi Blokirana2, m etodu r u n ( ) zaustavie poziv m etode b lo k irana.f( ). Kada pokrenete program , videete da, za razliku od poziva ulazno/izlaznih operacija, in te rru p t( ) m oe da izae iz poziva koji je blokirala uzajamno iskljuiva brava (m u tex ).'9

Provera postoji li prekid


Kada za neku nit pozovete prekid m etodom in te r r u p t( ), prekid e nastati jedino u sluaju da zadatak ue u operaciju koja blokira ili je ve u n u tar nje (sem, kao to ste videli, u sluaju ulazno/izlaznih operacija koje nije m ogue prekinuti, i!i blokiranih sinhronizovanih m etoda - tada ste bespom oni). Ali ta ako kod takav blokirajui poziv pravi sam o u zavisnosti od uslova u kojim a se izvrava? Ukoliko m oete da izaete samo tako to ete baciti izuzetak na blokirajui poziv, neete uvek moi da izaete iz petlje m etode r u n ( ). Dakle, ako pozovete in te r r u p t( ) da zaustavi neki zadatak, on m ora imati jo neki nain da izae, za sluaj da petlja m etode r u n ( ) ne napravi nijedan blokirajui poziv. Tu m ogunost prua status prekinutosti (engl. interru pted sta tu s) koji se postavlja pozivom m etode in te r r u p t( ). Status prekinutosti ispitujete m etodom in te r r u p te d ( ). O na vam kazuje ne samo da li je m etoda in te r r u p t( ) bila pozvana, nego i brie status prekinutosti. Kada se izbrie status prekinutosti, obezbeuje se da osnovna stru k tu ra (engl. framevvork) ne obavetava dvaput o tom e da je neki zadatak prekinut. Biete obaveteni preko izuzetka InterruptedE xcep tio n ili poziva m e to d e T h re a d .in te rru p te d () koji vraa tru e. Ukoliko elite ponovo da proverite da li je nastao prekid, sauvajte rezultat poziva m etode T h re a d .in te rru p te d (). U narednom prim eru prikazan je tipian idiom. Ukoliko je status prekinutosti postavljen, koristite taj idiom za izlazak iz m etode r u n ( ) i u blokiranom i u neblokiranom stanju:
//: paralelno/IdiomPrekidanja.java // Opti idiom za prekidanje zadatka. // {Args: 1100} import java.util.concurrent.*; import static net.mindview.util.Print.*; class TrebaPocistiti { private final int id;

|g

lako je to m a!o verovatno, im ajte u vidu da bi se poziv m eto d e t . i n t e r r u p t ( ) zapravo m ogao desiti p re poziva m eto de b lo k ira n a .f( ).

Poglavlje 21: Paralelno izvravanje

955

public TrebaPocistiti(int ident) { id = ident; print("TrebaPocistiti " + id);

}
public void cleanup() { print("istim " + id);

class Blokirano3 implements Runnable { private volatile double d = 0.0; public void run() { try { while(!Thread.interrupted()} { // takal TrebaPocistiti nl = new TrebaPocistiti(1); // Blok try-firially ponite neposredno iza definicije nl, // da bi bilo zajemeno pravilno ienje nl: try { printC'Spavam"); Timellnit.SECONDS.sleep(l); // taka2 TrebaPocistiti n2 = new TrebaPocistiti(2); // Zajemeno pravilno ienje n2: try { print("Raunam"); // Dugotrajna operacija koja ne blokira: for(int i = 1; i < 2500000; i++) d = d + (Math.PI + Math.E) / d; print("Zavrena dugotrajna operacija "); } final ly { n2.cleanup();

}
} finally { nl.cleanupO;

print("Izlazim iz niti ispitivanjem uslova od while()"); } catch(InterruptedException e) { print("Izlazim iz niti pomou izuzetka InterruptedException");

} } }
public class IdiomPrekidanja { public static void main(String[] args) throws Exception { if(args.1ength != 1) { pri nt("pokretanje: java IdiomPrekidanja kanjenje_ujTis"); System.exit(l);

956

Misliti na Javi

Thread n = new Thread(new Blokirano3()); n.start(); TimeUnit.MILLISECONDS.sleep(new Integer(args)); n.interrupt();

}
} /* Ispis: (primer) TrebaPocistiti 1 Spavam TrebaPocistiti 2 Raunam Zavrena dugotrajna operacija istim 2 istim 1 TrebaPocistiti 1 Spavam istim 1 Izlazim iz niti pomou izuzetka InterruptedException

* ///:Klasa TrebaPocistiti naglaava potrebu za pravilnirn ienjem resursa ukoliko petlju napustite putem izuzetka. Vodite rauna o tom e da sve resurse klase TrebaPocistiti napravljene u m etodi B lokirano3.run( ) m oraju neposredno da slede odredbe try-finally koje jem e da e m etoda clean up ( ) uvek biti pozvana. Na kom andnoj liniji, program u m orate dati argum ent vrem e kanjenja u milisekundam a - pre nego to pozove in te rru p t( ). Korienjem razliitih kanjenja moete izai iz m etode B lokirano3.run( ) na razliitim takam a petlje: u pozivu m etode sleep( ) koja blokira ili u m atem atikom p roraunu koji ne blokira. Ukoliko interrupt( ) bude pozvana nakon kom entara ,,taka2 (tokom operacije koja ne blokira), videete da se prvo dovrava petlja, zatim se unitavaju svi lokalni objekti i najzad se izlazi iz petlje preko naredbe while na njenoni vrhu. M eutim , ako se in te rru p t( ) pozove izmeu takel" i ,,take2 (nakon naredbe while, ali pre ili u toku operacije sleep( ) koja blokira), zadatak izlazi pom ou izuzetka InterruptedException im se prvi put pokua operacija koja blokira. U tom sluaju, iste se samo objekti klase TrebaPocistiti napravljeni do trenutka bacanja izuzetka, a sve ostalo ienje moete obaviti u n u tar odredbe catch. Klasa koja odgovara na in te rru p t( ) m ora da ustanovi strategiju kojom obez.beuje svoj ostanak u konsistentnom (neoteenom ) stanju. Po pravilu, to znai da iza pravljenja svih objekata koji zahtevaju ienje m oraju slediti odredbe try-finally, da bi se ienje obavilo bez obzira na nain izlaska iz petlje r u n ( ). Takav kod moe dobro da radi, ali naalost - zato to u Javi nem a autom atskih poziva destruktora - zavisi od toga da li e program er klijent dobro napisati odredbe try-finally.

Meusobna saradnja zadataka


Kao to ste videli, kada pom ou niti istovrem eno izvravate vie zadataka, spreiete jedan zadatak da pokvari resurse drugog ako bravom (m utexom ) sinhronizujete njihovo ponaanje. D rugim reima, ako se dva zadatka sudaraju zbog deljenog resursa (najee m em orije), za pristupanje tom resursu upotrebite uzajam no iskljuivu bravu (m utex).

Poglavlje 2 1: Paralelno izvravanje

957

Kada je taj problem reen, sledei korak je nauiti kako da zadaci sarauju jedan s drugim, da bi vie zadataka m oglo zajedniki da rei problem . Sada se ne postavlja pitanje kako da se zadaci m edusobno ne om etaju, nego kako da rade usaglaeno, poto se takvi p roblem i m oraju reavati postepeno, utvrenim redosledom . Kao u graevinarstvu: najpre se m o ra iskopati rupa za temelje, ali se arm atura m oe poloiti paralelno sa pravljenjem betonskih blokova, i oba ta zadatka m oraju biti dovrena da bi se nalili temelji. Cevi m oraju biti poloene pre nalivanja betonske ploe, ploa m ora biti nalivena pre nego to pone podizanje zidova itd. Neki od ovih zadataka m ogu se izvravati paralelno, ali odreeni koraci ne m ogu se izvesti ukoliko svi zadaci nisu zavreni. U m eusobnoj saradnji zadataka glavno pitanje je prim opredaja signala. Za tu p rim opredaju upotrebljavam o zajedniki temelj: uzajam no iskljuivu bravu (m utex) koji u ovom sluaju jem i da e na odreeni signal odgovoriti sam o jedan zadatak. Tim e se izbegavaju svi eventualni uslovi za trku. Pored mutexa, dodaem o nain da se zadatak zaustavi do k se ne prom eni neko spoljno stanje (npr. Cevi su poloene), koje pokazuje da je vrem e da se taj zadatak pokrene. U ovom odeljku, razm otriem o prim opredaju signala izm eu zadataka, koju bezbedno realizuju m etode w a it( ) i notifyA ll( ) klase Object. Biblioteka za paralelno izvravanje Jave SE5 ima i klasu Condition, s m etodam a aw ait( ) i s ig n a l(). Videemo koji problem i m ogu nastati i kako se reavaju.

wait() i notifyAII()
M etoda w a it( ) se obino koristi dok ekate na prom enu uslova ije ispunjenje odreuju sile kojim a ne upravlja tekua m etoda. Cesto neki drugi zadatak m enja taj uslov. Ne elite da program stoji besposleno u niti, stalno ispitujui taj uslov; to se naziva zauzetost ekanjem (engl. busy vvaiting) i obino prestavlja lo nain korienja procesorskog vrem ena. Stoga w a it( ) om oguava da uspavate tu nit dok eka da se svet prom eni. O na se budi i ispituje prom ene tek kad naie na m etode n o tify( ) ili notifyA ll( ) koje nagovetavaju da se m oda desilo neto vano. Tako je dobijen jo jedan nain sinhronizacije niti. Vano je shvatiti da ni sleep( ) niti yield( ) ne otkljuavaju objekat kada ih pozovete. S druge strane, kada zadatak iz neke m etode pozove m etodu w a it( ), ona otkljuava objekat pre nego to zaustavi izvravanje niti. Poto w a it( ) otkljuava bravu im je pozovete, to znai da drugi zadaci mogu da pribave tu bravu, pa tokom izvravanja m etode w a it ( ) moete da pozivate druge sinhronizovane m etode tog (sada otkljuanog) objekta. To je neophodno, poto obino ba te druge metode prouzrokuju p rom enu koju zaustavljena nit eka da bi se probudila. Dakle, kada pozovete w a it( ), saoptavate: Zasad sam uradio sve to sam mogao, pa u na ovom m estu da priekam , ali hou da om oguim drugim sinhronizovanim operacijam a da se izvravaju ako mogu. Postoje dva oblika m etode wait( ). Prvi prim a argum ent u milisekundam a, ije je znaenje jednako onom u m etodi sle ep (), dakle zaustavlja rad u zadatom periodu vremena. Za razliku od prim ene m etode sle ep (), kada se koristi w ait(pauza), deava se sledee: 1. O tkljuava se objekat tokom ekanja izvravanja m etode w a it( ). 2. Iz m etode w a it( ) moete da izaete i zbog obavetenja koje alju m etode n o tify () ili notifyA U (), ili e se izvravanje samo nastaviti po isteku zadatog vrem ena ekanja.

958

Misliti na Javi

Drugi, ee upotrebljavani oblik m etode wait( ) ne prim a nikakve argum ente, to znai da e m etoda ekati sve dok ta n it ne prim i poruku n o tify ( ) ili notifyAIl( ); taj oblik m etode w a it( ) ne zavrava zadatak autom atski posle isteka nekog vremena. Jedna od posebnih osobina m etoda w a it( ), n o tify ( ) i notifyA ll( ) jeste to to su one delovi osnovne klase Object, a ne klase Thread. M ada to na prvi pogled izgleda udno - da neto to slui iskljuivo za niti b ude pripadnik univerzalne osnovne klase - ali je neizbeno, jer te m etode rade s bravam a koje su takoe deo svakog objekta. Zato w a it( ) m oete da smestite u svaku sinhronizovanu m etodu, bez obzira na to da li u toj konkretnoj klasi postoji ili ne postoji vie niti, tj. da li je izvedena iz klase Thread ili realizuje interfejs Runnable. U stvari, w a it( ), n o tify ( ) i notifyAll( ) moete da pozovete jedino iz sinhronizovanih m etoda ili blokova (sleep( ) moete pozivati i iz nesinhronizovanih me toda, poto ona ne radi s bravam a). Ako bilo koju od tih m etoda pozovete iz m etode koja nije sinhronizovana, program e se ispravno prevesti, ali ete tokom izvravanja dobiti izuzetak tipa IllegalM onitorStateException uz pom alo nerazum ljivu poruku current thread not ow ner (tekua nit tren u tn o nije vlasnik brave objekta s kojim hoe da radi). Poruka znai da zadatak koji poziva w a it( ), notify( ) ili notifyA ll( ) m ora zakljuati bravu objekta pre nego to pozove jed n u od tih metoda. M etode w a it( ), n o tify ( ) ili notifyA ll( ) moete da pozivate sam o za svoje brave. Nema petljanja s tuim bravama, ali od drugog objekta moete zatraiti da obavi operaciju koja radi s njegovom bravom. P rethodno m orate da pribavite bravu tog objekta. Prim era radi, ako objektu x elite da poaljete obavetenje notifyAll( ), to moete uraditi u n u ta r sinhronizovanog bloka koji pribavlja bravu od x, tj. zakljuava x:
synchronized(x) { x.notifyAll();

} Razm otriem o jednostavan prim er. VoskOMatik.java ima dva procesa: nanoenje voska na Kola i poliranje. Poliranja nem a dok se ne zavri nanoenje voska, a zadatak nanoenja m ora da saeka dok se zadatak poliranja zavri - tek onda se moe naneti sledei sloj voska. I Voskalma i VoskaNema rade sa istim objektom tipa Kola koji m etodam a w a it( ) i notifyA ll( ) zaustavlja i ponovo pokree zadatke dok oni ekaju da se prom eni odreeni uslov:
//: paralelno/voskomatik/VoskOMatik.java // Osnove meusobne saradnje zadataka. package paralelno.voskomatik; import java.util.concurrent.*; import static net.mindview.uti1 .Print.*; class Kola { private boolean voskalma = false; public synchronized void voskiranoO { voskalma = true; // Spremno za poliranje noti fyAl1 ();

Poglavlje 2 1: Paralelno izvravanje

959

public synchronized void polirano() { voskalma = false; // Spremno za nanoenje sledeeg sloja voska noti fyAl1();

}
public synchronized void cekajNaVoskiranje() throws InterruptedException { while(voskaIma == false) wait();

}
public synchronized void cekajNaPoliranje() throws InterruptedException { whi1e(voskaIma == true) wait();

class Voskalma implements Runnable { private Kola kola; public VoskaIma(Kola k) { kola = k; } public void run() { try { while(!Thread.interrupted()) { printnb("Voska ima! "); TimeUnit.MILLISECONDS.sleep(200); kola.voski rano(); kola.cekajNaPoliranje();

}
} catch(InterruptedException e) { print("Izlazim iz niti pomou prekida");

}
print("Zavravam zadatak Voska Ima");

class VoskaNema implements Runnable { private Kola kola; public VoskaNema(Kola k) { kola = k; } public void run() { try { whi 1e ( IThread.inter'rupted()) { kola.cekajNaVoski ranje(); printnb("Voska nema! "); TimeUni t.MILLISECONDS.sleep(200); kola.poli rano();

}
} catch(InterruptedException e) { print("Izlazim iz niti pomou prekida");

}
print("Zavravam zadatak Voska Nema");

960

Misliti na Javi

}
public class VoskOMatik { public static void main(String[] args) throws Exception { Kola kola = new Kola(); ExecutorService exec = Executors.newCachedThreadPool (); exec.execute(new VoskaNema(kola)); exec.execute(new Voskalma(kola)); TimeUnit.SEC0NDS.sleep(5); // Radi neko vreme... exec.shutdownNow(); // Prekini sve zadatke

}
} /* Ispis: (95% podudaranja) Voska ima! Voska nema! Voska ima! Voska nema! ima! Voska nema! Voska ima! Voska nema! Voska Voska nema! Voska ima! Voska nema! Voska ima! nema! Voska ima! Voska nema! Voska ima! Voska putem prekida Zavravam zadatak Voska Ima Izlazim iz niti pomou prekida Zavravam zadatak Voska Nema Voska ima! Voska nema! Voska ima! Voska nema! Voska ima! Voska nema! Voska ima! Voska nema! Voska ima! Izlazim iz niti

* ///:-

Kola im aju samo jednu prom enljivu tipa boolean pod im enom voskalm a koja pokazuje stanje procesa nanoenja voska i poliranja. U m etodi cekajNaVoskiranje( ) proverava se indikator voskalm a, i ako je njegova vrednost false, pozivajui zadatak se zaustavlja tako to se poziva m etoda w a it( ). Vano je da se to deava u sinhronizovanoj m etodi, gde je zadatak pribavio bravu. Kada pozovete w a it( ), nit se zaustavlja, a brava otkljuava. N eophodno je da brava bude otkljuana zato to drugi zadaci m oraju im ati m ogunost da je pribave (zakljuaju) da bi stanje tog objekta moglo biti bezbedno prom enjeno (na prim er, da bi vrednost indikatora voskaIm a prom enili u true, to m oram o da uradim o da bi zaustavljeni zadatak uopte dobio priliku da nastavi rad). U ovom prim eru, kada drugi zadatak pozove voskirano( ) da bi pokazao kako je vreme da se neto uradi, brava m ora biti pribavljena (zakljuana) da bi se vrednost indikatora voskalma prom enila u true. Zatini vosk iran o ( ) poziva metodu notifyA ll( ) koja budi zadatak zaustavljen pozivom m etode w a it( ). Da bi se zadatak probudio iz stanja ekanja koje prouzrokuje m etoda w a it( ), najpre m ora ponovo da zakljua bravu koju je otkljuao kada je uao u taj w a it( ). Zadatak se nee probuditi dok ta brava ne postane dostupna, tj. otkljuana.20
20 Na nckim platfo rm am a postoji i trei nain izlaska iz m etode w a it( ): to je takozvano shiajno budetije. U sutini, sluajno b u en je p ro u zro k u je nit koja p rera n o prestane da blokira (dok eka na prom enljivu ili sem afor uslova), a da je nisu pro b u d ile m etode notify( ) ili n otifyA ll( ) (ili njihovi ekvivalenti za objekte nove klase Condition). N it se jednostavno p ro b u d i, naizgled sam a od sebe. Sluajno b u en je postoji zato to realizacija PO SlX niti (ili njinia ekvivalentnih) na nekim platfo rm am a nije onoliko jednostavna koliko bi Sun hteo da bude. No, posao pisanja p th read biblioteke za te p latfo rm e m n o go je laki ako se proglasi da sluajna bu denja ,,ne sm etaju toliko.

Poglavlje 21: Paralelno izvravanje

961

VoskaIma.run( ) predstavlja prvi korak u postupku voskiranja kola, pa je njegova operacija poziv m etode sleep ( ) kojim sim ulira vreme potrebno za voskiranje. Zatim kazuje kolima da je voskiranje zavreno i poziva cekajNaPoliranje( ) koja zaustavlja taj zadatak m etodom w a it( ) dok zadatak VoskaNema ne pozove p o liran o( ) za kola, prom eni stanje i pozove n otifyA ll( ). S druge strane, VoskaNema.run( ) odm ah prelazi na cekajNaV oskiranje( ) i stoga biva zaustavljena sve dok Voskalma ne nanese vosak i ne pozove voskirano( ). Kada pokrenete ovaj program , videete kako se ovaj dvostepeni postupak ponavlja dok zadaci jedan drugom predaju kontrolu. Posle pet sekundi, obe niti prekida interrupt( ); kada m etodu shutdow nN ow ( ) pozovete za ExecutorService, on poziva interrupt( ) za sve zadatke koje kontrolie. U p retho dn om prim eru naglaeno je da poziv m etode w a it( ) m orate staviti u petlju w hile koja proverava odgovarajue uslove. To je vano zato to: M oete imati vie zadataka koji iz istog razloga ekaju na istu bravu, a prvi zadatak koji se probudi moe prom eniti tu situaciju (ak i ako to sami ne uradite, to moe svako ko je izveo svoju klasu iz vae). U tom sluaju, taj zadatak treba ponovo zaustaviti dok se njegov odgovarajui uslov ne prom eni. Kada se zadatak probudi iz svog ekanja m etode w a it() - m oda su drugi zadaci prom enili situaciju tako da ovaj zadatak ne moe da u tom tren u tk u obavi svoju operaciju ili nije zainteresovan za to. 1 opet, zadatak treba ponovo zaustaviti novim pozivom m etode w a it( ). M ogue je i da zaaci ekaju na bravu tog objekta iz razliitih razloga, a tada morate upotrebiti notifyAll( ). U tom sluaju, m orate proveriti da li je zadatak probuen iz pravog razloga, i ako nije, ponovo pozovite w a it( ). Dakle, n eophodno je da proveravate ispunjenost odgovarajueg uslova i da se vraate u w a it( ) ako taj uslov nije ispunjen. To se obino pie pom ou petlje while. Veba 21: (2) Napravite dve klase koje realizuju interfejs Runnable, jednu s m etodom r u n ( ) koja na svom poetku poziva w a it( ). D ruga klasa treba da uhvati referencu prvog Runnable objekta. Njena m etoda r u n ( ) treba da pozove n o tify A ll( ) za prvi zadatak nakon nekoliko sekundi, da bi prvi zadatak m ogao da prikae poruku. Ispitajte klase pom ou izvrioca (Executor). Veba 22: (4) Napravite prim er zauzetosti ekanjem. Jedan zadatak neko vrem e spava i zatim postavlja vrednost odreenog indikatora na true. Drugi zadatak posm atra taj indikator iz petlje while (to je ta zauzetost ekanjem ) i kada indikator poprim i vrednost true, zaaje m u vrednost false i prijavljuje prom enu konzoli. Izm erite vrem e koje program gubi na zauzetost ekanjem i napravite drugu verziju program a koja um esto zauzetosti ekanjem koristi w a it( ).

Proputeni signali
Kada je rad dve niti usklaen m etodam a notify( )/w a it( ) ili notifyAH( )/w a it( ), moe se propustiti signal. Pretpostavim o da je N1 nit koja obavetava nit N2 i da su obe realizovane na sledei (pogrean) nain:

962

Misliti na Javi

N1 : synchronized(deljeniMoni tor) {
<pripremo u slo v a za N2>

deljeniMonitor.notify();

}
N2: while(nekillslov) { // Taka 1 synchronized(deljeniMonitor) { deljeniMonitor.wait();

} } <priprema uslova za N2> treba da sprei N2 da zove w a it( ), ako to N2 ve nije uinila. Pretpostavim o da N2 izrauna nekiUsIov i dobije rezultat true. U Takil, m ehanizam za raspodelu procesorskog vrem ena moe prebaciti na nit N l. N1 izvrava svoju priprem u i zatim poziva n o tify ( ). Kada N2 nastavi da se izvrava, za nju je prekasno da shvati kako se uslov u m euvrem enu prom enio, i ona slepo ulazi u w a it( ). Poruka m etode n o tify ( ) bie proputena i N2 e ekati beskonano dugo na signal koji je ve bio poslat; dobili sm o uzajam nu blokadu (engl. deadlock). Reenje je da se sprei uslov za trku prim en om prom enljive nekiUslov. Ovo je ispravan nain realizacije N2:
synchronized(deljeniMonitor) { while(nekiUslov) deljeniMonitor.wait();

} U ovoj realizaciji vai sledee: ako nit N1 prva dobije priliku da se izvrava, kada se kontrola vrati niti N2 ona e shvatiti da se uslov prom enio i nee ui u w a it( ). U protivnom , ukoliko nit N2 prva dobije priliku da se izvrava, ui e u w a it( ) i kasnije e je probuditi N l. Dakle, signal ne moe biti proputen.

notify() u odnosu na notifyAII()


Strogo uzev, vie zadataka moe ekati - m etodom w a it() - na isti objekat tipa Kola, pa je bezbednije pozvati n otifyA ll( ) um esto n o tify ( ). M eutim , struktura gornjeg program a je takva da e samo jedan zadatak biti u stanju ekanja - w a it( ) - pa sm em o da upotrebim o n o tify ( ) um esto n o tify A ll( ). Korienje n o tify ( ) um esto notifyA H ( ) predstavlja optim izaciju. Od svih moguih zadataka koji ekaju na odreenu bravu, n o tify ( ) e probuditi sam o jedan, pa ako pokuate da upotrebite n o tify ( ), m orate biti sigurni da e se probuditi pravi zadatak. Pored toga, svi zadaci m oraju ekati na isti uslov cia biste mogli da upotrebite n o tif y ( ), jer ako razni zadaci ekaju na razliite uslove, ne m oete znati da e se probuditi onaj pravi. Ako upotrebite n o tify ( ), sam o jedan zadatak m ora im ati koristi od prom ene uslova. Najzad, ova ogranienja m oraju uvek vaiti za sve m ogue potklase. Ukoliko bilo koje od ovih pravila ne moe biti zadovoljeno, m orate koristiti n o tify A ll( ), a ne n o tify ( ).

Poglavlje 21: Paralelno izvravanje

963

U raspravama o vienitnom izvravanju u Javi esto se ponavlja zbunjujua tvrdnja da n otifyA ll( ) budi ,,sve zadatke koji ekaju. Da li to znai da svaki poziv m etode n otifyA ll( ) budi sve zadatke koji su pozvali w a it( ), na bilo kojem m estu u program u? U narednom prim eru, kod zadatka Zadatak2 pokazuje da ta tvrdnja nije istinita - zapravo, kada se pozove n otifyA ll( ) za odreenu bravu, bude se sam o zadaci koji ekaju na tu bravu:
//: paralelno/NotifyIliNotifyAll .java import j a va .u ti1 .concurrent.*; import ja v a . u t i l .*; class Blokator { synchronized void cekatnPoziv() { try { while(!Thread.interrupted()) wait(); System.out.print(Thread.currentThread() + " "); {

}
} catch(InterruptedException e) {

// Ovo je prihvatljiv nain izlaska

} }
synchronized void probudi() { notify(); } synchronized void probudiSve() { notifyAll(); }

}
class Zadatak implements Runnable { static Blokator blokator = new Blokator(); public void run() { blokator.cekamPoziv(); }

}
class Zadatak2 implements Runnable { // Zaseban objekat tipa Blokator: static Blokator blokator = new Blokator(); public void run() { blokator.cekamPoziv (); }

public class Notify11iNotifyAl1 { public static void main(String[] args) throws Exception { ExecutorService exec = Executors.newCachedThreadPool (); for(int i = 0; i < 5; i++) exec.execute(new Zadatak()); exec.execute(new Zadatak2()); Timer meracVremena = new Timer(); meracVremena.scheduleAtFixedRate(new TimerTask() { boolean probudi = true; public void run() { if(probudi) { System.out.print("\nnotify() "); Zadatak.blokator.probudi(); probudi = false;

964

Misliti na Javi

} else { System.out.print("\nnotifyAl 1() "); Zadatak.blokator.probudiSve(); probudi = true;

} }
}, 400, 400); // Pokreni na svake 0,4 sekunde Timellnit.SEC0NDS.sleep(5); // Neka radi neko vreme... meracVremena.cancel(); System.out.println("\nMera vremena otkazan"); TimeUni t .MILLISECONDS.sleep(500); System.out.print("Zadatak2.blokator.probudiSve() "); Zadatak2.blokator.probudiSve(); TimeUnit.MILLISEC0NDS.sleep(500); System.out.println("\nGasim"); exec.shutdownNow(); // Prekini sve zadatke

}
} /* Ispis: (primer) notify() Thread[pool-l-thread-l,5,main] notifyAl1() Thread[pool-l-thread-l,5,main] Thread[pool-l-thread-5,5,main] Thread[pool-l-thread-4,5,main] Thread[pool-l-thread-3,5,main] Thread[pool -l-thread-2,5,main] notify() Thread[pool-l-thread-l,5,main] notifyAl1() Thread[pool-1-thread-l,5,main] Thread[pool-l-thread-2,5,main] Thread[pool-l-thread-3,5,main] Thread[pool-l-thread-4,5,main] Thread[pool -l-thread-5,5,main] notify() Thread[pool-1-thread-l,5,main] notifyAl1 () Thread[pool-1-thread-l,5,main] Thread[pool-l-thread-5,5,main] Thread[pool-l-thread-4,5,main] Thread[pool-l-thread-3,5,main] Thread[pool -l-thread-2,5,main] notify() Thread[pool-l-thread-l,5,main] notifyAl1 () Thread[pool-l-thread-l,5,main] Thread[pool-l-thread-2,5,main] Thread[pool-l-thread-3,5,main] Thread[pool-l-thread-4,5,main] Thread[pool -l-thread-5,5,main] notify() Thread[pool-1-thread-l,5 ,main] notifyAll() Thread[pool-1-thread-l,5,main] Thread[pool-l-thread-5,5,main] Thread[pool-l-thread-4,5,main] Thread[pool-l-thread-3,5,main] Thread[pool -l-thread-2,5,main] notify() Thread[pool-1-thread-l,5,main] notifyAl1 () Thread[pool-1-thread-1,5,main] Thread[pool-l-thread-2,5,main] Thread[pool-l-thread-3,5,main] Thread[pool-l-thread-4,5,main] Thread[pool -l-thread-5,5,main] Mera vremena otkazan Zadatak2.blokator.probudiSve() Thread[pool-l-thread-6,5,main] Gasim *///:-

Zadatak i Zadatak2 imaju sopstvene Blokator objekte, pa svaki objekat tipa Zadatak.blokator blokira svaki objekat tipa Zadatak, i svaki objekat tipa Zadatak2.blokator blokira svaki objekat tipa Zadatak2 . U m etodi m a in ( ), objekat tipa java.util.Timer biva

Poglavlje 21: Paralelno izvravanje

965

podeen tako da svoju m etodu r u n ( ) izvrava svake 4/10 sekunde, i ta m etoda r u n ( ) n aizm enino poziva n o tify ( ) i n otify A ll( ) za Zadatak.blokator m etodam a ,,probudi. Iz rezultata uoavate sledee: iako objekat Zadatak2 postoji i blokirao ga je objekat Zadatak2.blokator, nijedan od poziva m etoda n o tify ( ) ili notifyA ll( ) za Zadatak.blokator ne prouzrokuje buenje objekta Zadatak2. Isto tako, na kraju m etode m a in ( ), poziva se ca n c e l( ) za meracVremena, i m ada je taj m era vrem ena otkazan, prvih pet zadataka se i dalje izvrava i jo su blokirani u svojim pozivima m etode Zadatak.blokator.cekam Poziv( ). Rezultat poziva m etode Zadatak2.blokator.probudiSve( ) netna veze ni sa jed n im od zadataka koji ekaju na bravu objekta Zadatak.blokator. To im a smisla i kada razm otrite m etode p ro b u d i( ) i probudiSve( ) u Blokatoru. Te m etode su sinhronizovane, to znai da same pribavljaju (zakljuavaju) svoju bravu, pa kada pozivaju n o tify ( ) ili n otify A ll( ), logino je da ih pozivaju sam o za tu bravu - i tim e b ude sam o zadatke koji ekaju na tu bravu. Blokator.cekam Poziv( ) toliko je jednostavan da sam mogao da napiem sam o for(;;) um esto w hile(!T hread.interrupted( )) i u ovom sluaju postignem isti rezultat; to je zato to u ovom prim eru nem a razlike izm eu naputanja petlje pom ou izuzetka i naputanja preko provere indikatora in terrup ted( ) - u oba sluaja izvrava se isti kod. M eutim , u ovom prim eru se form e radi proverava interrupted( ), poto postoje dva razliita naina za naputanje petlje. Ukoliko kasnije odluite da dodate jo koda u petlju, rizikujete da napravite greku ako ne uzm ete u obzir obe putanja izlaska iz petlje. Veba 23: (7) Pokaite da VoskO M atik.java dobro radi i kada upotrebite n o tif y ( ) um esto n o tify A II().

Proizvoai i potroai
Zam islite restoran s jednim kuvarom i jednim konobarom . Konobar m ora da saeka dok kuvar prip rem i jelo. Kuvar obavetava konobara kada je jeio zgotovljeno. Tada konobar uzim a jelo, posluuje ga pa se vraa i eka. To je prim er saradnje zadataka: kuvar predstavlja proizvoaa , a konobar potroaa. Zadaci m oraju da razm enjuju signale tokom proizvodnje i potronje jela, i sistem m ora da se uredno ugasi. Ovo je ta pria oblikovana kao kod:
//: paralelno/Restoran.java // Saradnja zadataka kao saradnja proizvoaa i potroaa. import java.util.concurrent.*; import static net.mindview.util .Print.*; class Jelo { private final int brojNarudzbe; public Jelo(int brojNarudzbe) { this.brojNarudzbe = brojNarudzbe; } public String toStringf) { return "Jelo " + brojNarudzbe; }

}
class Konobar implements Runnable { private Restoran restoran; public Konobar(Restoran r) { restoran = r; }

966

Misliti na Javi

public void run() { try { while(!Thread.interrupted()) { synchronized(this) { while(restoran.jelo == null) wait(); // ... dok kuvar ne pripremi jelo

}
printC'Konobar je uzeo " + restoran. jelo); synchronized(restoran.kuvar) { restoran.jelo = null; restoran.kuvar.notifyAl1(); // Spreman za sledee

} }
} catch(InterruptedException e) { print("Konobar prekinut");

} }

class Kuvar implements Runnable { private Restoran restoran; private int broj = 0; public Kuvar(Restoran r) { restoran = r; } public void run() { try { while(!Thread.interrupted()) { synchronized(this) { whi1e(restoran.jelo != null) wait(); // ... da jelo bude poslueno

}
if(++broj == 10) { print("Ponestalo hrane, zatvaramo"); restoran.exec.shutdownNow();

}
printnb("Evo narudzbe! "); synchronized(restoran.konobar) { restoran.jelo = new Jelo(broj); restoran.konobar.noti fyAl1 ();

}
TimeUni t .MILLI SE CO ND S.sl e ep(100) ;

}
} catch(InterruptedException e) { print("Kuvar prekinut");

public class Restoran { Jelo jelo;

Poglavlje 21: Paralelno izvravanje

967

ExecutorService exec = Executors.newCachedThreadPool (); Konobar konobar = new Konobar(this); Kuvar kuvar = new Kuvar(this); public RestoranO { exec.execute(kuvar); exec.execute(konobar);

)
public static void main(String[] args) { new Restoran();

}
} /* Ispis:
Evo narudzbe! Konobar je uzeo Evo narudzbe! Konobar je uzeo Evo narudzbe! Konobar je uzeo Evo narudzbe! Konobar je uzeo Evo narudzbe! Konobar je uzeo Evo narudzbe! Konobar je uzeo Evo narudzbe! Konobar je uzeo Evo narudzbe! Konobar je uzeo Evo narudzbe! Konobar je uzeo Ponestalo hrane, zatvaramo Konobar prekinut Evo narudzbe! Kuvar prekinut Jelo Jelo Jelo Jelo Jelo Jelo Jelo Jelo Jelo 1 2 3 4 5 6 7 8 9

* ///:Restoran je ia i za Konobara i za Kuvara. O ba m oraju znati za koji Restoran rade, poto m oraju staviti jelo, restoran.jelo, u restoranski izlog s jelima, odnosno, m oraju izvaditi jelo iz njega. U m etodi r u n ( ), Konobar prelazi u reim ekanja - w a it( ) - i zaustavlja taj zadatak dok ga ne probudi poruka n o tify A ll( ) od Kuvara. Poto je ovaj program veoma jednostavan, znam o da sam o jedan zadatak eka na Konobarovu bravu: to je sam zadatak Konobar. Zato sam um esto notifyA ll( ) mogao pozvati n o tify ( ). M eutim , u sloenijim situacijam a, na bravu odreenog objekta moe ekati vie zadataka, pa tada ne znam o koji od njih treba probuditi. Zato je bezbednije pozvati n otifyA ll( ); ona budi sve zadatke koji ekaju na odreenu bravu. Potom svaki od tih zadataka m ora odluiti da li je to obavetenje relevantno za njega. Nakon to Kuvar proizvede Jelo i o tom e obavesti (engl. notify ) Konobara, Kuvar eka dok Konobar ne uzm e jelo i o tom e obavesti Kuvara, a on zatim moe da proizvee sledee Jelo. O bratite panju na to da je w a it( ) om otana u naredbu w h ile( ) koja ispituje ono isto na ta se eka. To isprva izgleda malo udno - ako ekate (spavajui) na narudbu i budete probueni, to m ora znaiti da postoji nova narudba, zar ne? Ve sam rekao da je problem to to u paralelnoj aplikaciji neki drugi zadatak moe da uleti i ugrabi narudbu dok se Konobar budi. Jedini bezbedan nain jeste da za w a it( ) uvek upotrebljavate sledei idiom (uz pravilnu sinhronizaciju, naravno, i program iranje koje spreava proputanje signala):
whi1e(uslovNijelspunjen) wai t();

968

Misliti na Javi

Time se jem i da e uslov biti ispunjen pre nego to izaete iz petlje ekanja, i ako ste obaveteni o neem u to nem a veze sa uslovom - to se m oe desiti kada je poruka n o tifyA ll( ) - ili se uslov pro m eni pre nego to p o tp u n o izaete iz petlje ekanja, svakako ete se vratiti u ekanje. Vodite rauna o tom e da poziv n o tify A ll( ) m ora najpre zakljuati bravu konobara. Poziv m etode w a it( ) iz m etode K onobar.run( ) autom atski otkJjuava bravu, pa je to mogue. Poto brava m ora biti zakljuana da bi n o tify A ll( ) m ogla biti pozvana, dva zadatka koji pokuavaju da pozovu n o tify A ll( ) za isti objekat zajemeno nee zasmetati jedan drugom . O be m etode r u n ( ) uredno se gase tako to je cela m etoda r u n ( ) u m etnuta u blok try. O dredba catch zavrava se neposredno pre zavrne vitiaste zagrade m etode r u n ( ), pa ako zadatak prim i InterruptedException, zavrie se o dm ah im uhvati izuzetak. U klasi Kuvar, obratite panju na to da bism o nakon poziva m etode sh utdow n N ow ( ) obinom naredbom return mogli da se jednostavno vratim o iz m etode r u n ( ), i to najee i treba da uradite. M eutim , ovako je m alo zanimljivije. Prisetite se da sh utdow n N ow ( ) alje in terru p t( ) svim zad a m a koje je ExecutorService pokrenuo. Ali u sluaju klase Kuvar, zadatak se ne gasi neposredno po prijem u poruke in terru p tf ), poto prekid m oe da generie izuzetak InterruptedException jedino kada zadatak pokua da ue u (neprekidnu) operaciju koja blokira. Zato se najpre prikazuje Evo narudzbe! i zatim baca izuzetak InterruptedException kada Kuvar pokua da pozove sle e p ( ). Ako uklonite poziv m etode sle e p ( ), zadatak e doi na vrh petlje r u n ( ) i izai zbog ispitivanja vrednosti m etode Thread.interrupted( ), a da ne baci izuzetak. U p retho dn om prim eru, zadatak na sam o jedno m esto m oe da smesti objekat koji e drugi zadatak kasnije upotrebiti. M eutim , u tipinoj realizaciji proizvodaa i potroaa, za skladitenje objekata koji se proizvode i troe prim enjuje se princip FIFO (fir$t-inyfirstout prvi izlazi onaj koji je prvi uao). Vie rei o takvim redovim a za ekanje bie u nastavku poglavlja. Veba 24: (1) M etodam a w a it() i n o tify A ll() reite problem jednog proizvoaa i jednog potroaa. Proizvoa ne sme da prepuni prim aoev bafer, to se moe desiti ukoliko je proizvoa bri od potroaa. Ako je potroa bri od proizvoaa, onda isti podatak treba da proita samo jednom . Ne sm ete nita pretpostaviti o relativnim brzinam a proizvoaa i potroaa. Veba 25: (1) U klasi Kuvar program a Restoran.java, vratite se naredbom return iz metode r u n ( ) nakon poziva m etode sh u td ow n N o w ( ) i posm atrajte razliku u ponaanju. Veba 26: (8) Program u Restoran.java dodajte klasu Pom ocnik. Kada poslui jelo, Konobar treba da obavesti P om ocnika da poisti.

Korienje eksplicitnih objekata tipa Lock i Condition


U biblioteci Java SE5 java.util.concurrent ima i drugih, eksplicitnih alatki pom ou kojih m oem o preraditi program VoskOMatik.java. O snovna klasa koja koristi uzajam no iskljuivu bravu (mutex) i om oguuje zaustavljanje zadataka nazvana ie Condition.

Poglavlje 2 1: Paralelno izvravanje

969

Zadatak zaustavljate pozivom m etode a w ait( ) za objekat tipa Condition. Kada nastanu spoljne prom ene stanja koje m oda znae da neki zadatak treba da nastavi izvravanje, taj zadatak obavetavate poru ko m sig n a l( ), odnosno sign alA ll( ) da biste probudili sve zadatke koji su se zaustavili do ispunjenja uslova tj. tog objekta C ondition (kao i notifyA Il( ), sign alA ll( ) je bezbednije koristiti). Evo kako izgleda program VoskOMatik.java preraden tako da sadri C ondition pom ou kojeg se odreeni zadatak zaustavlja u n u ta r m etoda cekajNaVoskiranje( ) ili cekajN aPoliranje():
//: paralelno/voskomatik2/VoskOMatik2.java // Upotreba objekata Lock i Condition. package paralelno.voskomatik2; import java.util.concurrent.*; import java.util.concurrent.locks.*; import static net.mindview.util.Print.*; class Kola { private Lock brava = new ReentrantLock(); private Condition uslov = brava.newCondition(); private boolean voskalma = false; public void voskirano() { brava.lockO ; try { voskalma = true; // Spremna za poliranje usl o v .signalAll (); } finally { b r ava . u n l o c k O ;

} }
public void polirano() { brava.1ock(); try { voskalma = false; // Spremna za nanoenje sledeeg sloja voska uslov.signalA11 (); } fi nal 1y { brava.unlock();

public void cekajNaVoskiranje() throws InterruptedException { brava.lockO; try { while(voskaIma == false) uslov.await (); } finally { brava.unlockO;

970

Misliti na Javi

public void cekajNaPoliranje() throws InterruptedException{ brava.lock(); try { while(voskaIma == true) uslov.await(); } finally { b rav a .unlockO;

} }

class Voskalma implements Runnable { private Kola kola; public VoskaIma(Kola k) { kola = k; } public void run() { try { while(!Thread.interrupted()) { printnb("Voska ima! "); TimeUnit.MILLISECONDS.sl eep(200); kola.voskiranoO ; kola.cekajNaPoli ranje();

}
} catch(InterruptedException e) { print("Izlazim iz niti pomou prekida");

}
print("Zavravam zadatak Voska Ima");

} }
class VoskaNema implements Runnable { private Kola kola; public VoskaNema(Kola k) { kola = k; } public void run() { try { . whi1e ( !Thread.interrupted()) { kola.cekajNaVoskiranje(); printnb("Voska nema! "); Timelinit.MILLISECONDS. sl eep(200); kola.pol i rano();

}
} catch(InterruptedException e) { print("Izlazim iz niti pomou prekida");

}
print("Zavravam zadatak Voska Nema");

public class VoskOMatik2 { public static void main(String[] args) throws Exception { Kola kola = new Kola();

Poglavlje 21: Paralelno izvravanje

971

ExecutorService exec = Ex ec ut or s.newCachedThreadPool(); exec.execute(new Vosk aN em a( ko la )); exec.execute(new Voskal ma (k ol a) ); TimeUnit.S EC 0N DS .s lee p( 5); exec.s hu td ow nN ow ();

}
} /* Ispis: (90% podudaranja) Voska ima! Voska nema! Voska ima! Voska nema! Voska ima! Voska nema! Voska ima! Voska nema! Voska ima! Voska nema! Voska ima! Voska nema! Voska ima! Voska nema! Voska ima! Voska nema! Voska ima! Voska nema! Voska ima! Voska nema! Voska ima! Voska nema! Voska ima! Voska nema! Voska ima! niti pomou prekida Za vr a va m zadatak Voska Nema Izlazim iz niti pomou prekida Za vravam zadatak Voska Ima Izlazim iz

* ///:U ko nstruktoru Kola, jedna brava (objekat tipa Lock) proizvodi uslov, tj. objekat tipa C ondition pom ou kojeg se upravlja kom unikacijom izm eu zadataka. M eutim , objekat C ondition ne sadri inform acije o stanju procesa, pa su potrebne dodatne inform acije kojim a pokazujete stanje procesa, a to je prom enljiva voskalm a tipa boolean. Iza svakog poziva m etode lo c k ( ) m ora neposredno slediti odredba try-finally koja jem i da e se u svim sluajevima ostvariti otkljuavanje. Kao u ugraenim verzijama, zaatak m ora zakljuati bravu da bi mogao da pozove a w a it( ), sig n a l( ) ili sig n a lA ll( ). Im ajte u vidu da je ovo reenje sloenije nego prethodno, a ta sloenost u ovom sluaju nije donela nita dobro. Objekti tipa Lock i Condition potrebni su sam o za tee problem e vienitnog izvravanja. Veba 27: (2) Izmenite Restoran.java tako da korisli eksplicitne objekte tipa Lock i Condition.

Proizvoai, potroai i redovi za ekanje


M etode w a it( ) i n otifyA ll( ) reavaju problem saradnje zadataka na prilino niskom nivou, razm enjujui signale u svakoj interakciji. U m nogo sluajeva, m oete se popeti za jedan nivo apstrakcije i problem e saradnje zadataka reiti pom ou sinhronizovanog reda za ekanjc koji u svakom trenutku dozvoljava samo po jednom zadatku da u red um etne ili iz njega ukloni neki element. Taj red sadri interfejs java.util.concurrent.BlockingQueue koji ima vie standardnih realizacija. Najee ete koristiti LinkedBlockingQueue, to je neogranien red za ekanje; ArrayBlockingQueue ima neprom enljivu veliinu, pa u njega moete staviti sam o nevelik broj elem enata pre nego to se zablokira. Tikav red za ekanje zaustavlja potroaki zadatak koji pokua da uzm e objekat iz praznog reda i nastavlja njegovo izvravanje kada vie elem enata postane dostupno. Blokiranje redova za ekanje reava veliki broj problem a na m nogo jenostavniji i pouzaniji nain od m etoda w ait( ) i notifyA lI().

972

Misliti na Javi

Sledi jenostavan test koji serijalizuje izvravanje Lansiranje objekata. Potroa je IzvravaLansiranje, koji vadi svaki objekat Lansiranje iz blokirajueg reda za ekanje (BlockingQueue) i izvrava ga neposredno. (D rugim reima, eksplicitnim pozivom metode r u n ( ) koristi sopstvenu nit, um esto da za svaki zadatak pokree novu.)
//: paralelno/TestBlokirajucihRedovaZaCekanje.java // {RunByHand} import java.util.concurrent.*; import java.io.*; import static net.mindview.util.Print.*; class IzvrsavaLansiranje implements Runnable { private BiockingQueue<Lansiranje> rakete; public IzvrsavaLansiranje(BlockingQueue<Lansiranje> redZaCekanje) { rakete = redZaCekanje;

}
public void add(Lansiranje lo) { try { rakete.put(lo); } catch(InterruptedException e) { print("Prekinuto tokom operacije put()1 1 );

} }
public void run() { try { while(!Thread.interrupted()) { Lansiranje raketa = rakete.take(); raketa.run(); // Koristi ovu nit

}
} catch(InterruptedException e) { print("Buenje iz metode take()");

}
print("Izlazim iz niti IzvrsavaLansiranje");

} }
public class TestBlokirajucihRedovaZaCekanje { static void getkey() { try { // Zato to taster Enter proizvodi rezultat koji je // razliite duine u Windowsu i u Linuxu: new BufferedReader( new InputStreamReader(System.in)).readLine(); } catch(java.io.IOException e) { throw new RuntimeException(e);

} }
static void getkey(String poruka) { print(poruka); getkey ();

Poglavlje 21: Paralelno izvravanje

973

static void test(String prk, BlockingQueue<Lansiranje> redZaCekanje) { print(prk); IzvrsavaLansiranje izvrsilac = new IzvrsavaLansiranje(redZaCekanje); Thread n = new Thread(izvrsilac); n.start(); for(int i = 0 ; i < 5 ; i++) izvrsilac.add(new Lansiranje(5)); getkey("Pritisnite 'Enter' (" + prk + ")"); n.i nterrupt(); print("Zavren " + prk + " test");

}
public static void main(String[] args) { test("LinkedBlockingQueue", // Neograniena veliina new LinkedBlockingQueue<Lansiranje>()); test("ArrayBlockingQueue", // Nepromenljiva veliina new ArrayBlocking0ueue<Lansiranje>(3)); test("SynchronousQueue", // Veliine 1 new SynchronousQueue<Lansiranje>());

} } ///:M etoda m a in ( ) smeta zadatke u BlockingQueue, iz kojeg ih vadi m etoda IzvrsavaLansiranje. O bratite panju na to da IzvrsavaLansiranje moe da zaboravi na sinhronizaciju zato to to reava BlockingQueue. Veba 28: (3) Izmenite TestBlokirajucihRedovaZaCekanje.java dodavanjem novog zadatka koji stavlja Lansiranje u BlockingQueue, um esto da se to radi u m etodi m a in ( ).

Blokirajui redovi za ekanje tosta


Kao prim er koricenja blokirajuih redova za ekanje (BlockingQueue), uzm im o m ainu koja ima tri zadatka: pravljenje tosta, mazanje maslaca na tost i m azanje pckm eza na tost nam azan maslacem. Izmedu tih postupaka tost m oe da se nalazi u blokirajuim redovim a za ekanje:
//: paralelno/TostOMatik.java // Toster koji upotrebljava redove za ekanje. import java.util.concurrent.*; import java.uti1 .*; import static net.mindview.uti1 .Print.*; class Tost { public enum Status { SUV, SMASLACEM, SPEKMEZOM } private Status status = Status.SUV; private final int id; public Tost(int idn) { id = idn; } public void namaziMaslacem() { status = Status.SMASLACEM; } public void namaziPekmezom() { status = Status.SPEKMEZOM; } public Status dajStatus() { return status; }

974

Misliti na Javi

public int dajId() { return id; } public String toString() { return "Tost 1 1 + id + " + status;

} }
class RedZaCekanjeTosta extends LinkedBlockingQueue<Tost> {} class Toster implements Runnable { private RedZaCekanjeTosta redZaCekanjeTosta; private int broj = 0; private Random slucajan = new Random(47); public Toster(RedZaCekanjeTosta rzct) { redZaCekanjeTosta = rzct; } public void run() { try { whi1e(!Thread.interrupted()) { TimeUnit.MILLISECONDS.sleep( 100 + slucajan.nextlnt(500)); // Napravi tost Tost n = new Tost(broj++); print(n); // Umetni u redZaCekanje redZaCekanjeTosta.put(n);

}
} catch(InterruptedException e) { print("Toster prekinut");

}
print("Toster iskljuen");

// Namai tost maslacem: class MazeMaslacem implements Runnable { private RedZaCekanjeTosta redSuvih, redSMaslacem; public MazeMaslacem(RedZaCekanjeTosta suv, RedZaCekanjeTosta smaslacem) { redSuvih = suv; redSMaslacem = smaslacem;

}
public void run() { try { whi 1e(.'Thread.interrupted()) { // Blokira dok ne dobije sledee pare tosta: Tost n = redSuvih.takeO; n.namaziMaslacem(); print(n); redSMaslacem.put(n);

}
} catch(InterruptedException e) { print("MazeMaslacem prekinut);

Poglavfje 21: Paralelno izvravanje

975

print("MazeMaslacem iskljuen");

} }
// Namai pekmezom tost s maslacem: class MazePekmezom implements Runnable { private RedZaCekanjeTosta redSMaslacem, redGotovih; public MazePekmezom(RedZaCekanjeTosta smaslacem, RedZaCekanjeTosta gotovi)

{
redSMaslacem = smaslacem; redGotovih = gotovi;

}
public void run() { try { while(!Thread.interrupted()) { // Blokira dok ne dobije sledee pare tosta: Tost n = redSMaslacem.take(); n.namaziPekmezomO; print(n); redGotovih.put(n);

}
} catch(InterruptedException e) { print("MazePekmezom prekinut");

}
print("MazePekmezom iskljuen");

} }
// Potroi tost: class Gost implements Runnable { private RedZaCekanjeTosta redGotovih; private int brojac = 0; public Gost(RedZaCekanjeTosta gotovi) { redGotovih = gotovi;

}
public void run() { try { while(!Thread.interrupted()) { // Blokira se dok ne dobije sledee pare tosta: Tost n = redGotovih.take(); // Proveri da li tost uredno dolazi, // i da li sva parad bivaju namazana pekmezom: if(n.dajld() != brojac++ | | n.dajStatus() != Tost.Status.SPEKMEZOM) { p r i n t ( " Greka: " + n); System.exit(l); } else print("Njam! " + n);

}
} catch(InterruptedException e) {

976

Misliti na Javi

print("Gost prekinut");

}
print("Gost iskljuen");

} }
public class TostOMatik { public static void main(String[] args) throws Exception { RedZaCekanjeTosta redSuvih = new RedZaCekanjeTosta(), redSMaslacem = new RedZaCekanjeTosta(), redGotovih = new RedZaCekanjeTosta(); ExecutorService exec = Executors.newCachedThreadPoo1(); exec.execute(new Toster(redSuvih)); exec.execute(new MazeMaslacem(redSuvih, redSMaslacem)); exec.execute(new MazePekmezotn(redSMaslacem, redGotovih)); exec.execute(new Gost(redGotovih)); TimeUnit.SEC0NDS.sleep(5); exec.shutdownNow();

}
} /* (Pokrenite da biste videli rezultat) *///:-

Tost je odlian prim er vrednosti nabrojanih tipova. Vodite rauna o tom e da nema eksplicitne sinhronizacije (ne upotrebljavaju se objekti tipa Lock niti rezervisana re synchronized), poto sinhronizaciju im plicitno (interno) obavljaju redovi za ekanje i projekat sistema - svako pare Tosta u svakom trenutku predm et je obrade sam o jednog zadatka. Poto redovi za ekanje blokiraju izvravanje, procesi se zaustavljaju i nastavljaju autom atski. N adam se da vidite kako pojednostavljenje koje su doneli blokirajui redovi za ekanje um e da bude veom a veliko. Izbegnute su veze izm eu klasa koje bi postojale uz eksplicitne naredbe w a it( ) i n o tifyA ll( ), jer svaka klasa kom unicira samo sa svojim blokirajuim redom za ekanje. Veba 29; (8) Izmenite TostOMatik.java tako da na dve zasebne linije pravi sendvie od tosta nam azanog maslacem i m arm eladom (jedna za tost nam azan maslacem, druga za m arm eladu, zatim spojite linije).

Cevi za ulazno/izlazne operacije izmeu zadataka


esto je korisno da zadaci m eusobno kom uniciraju pom ou ulazno/izlaznih operacija. Biblioteke za vienitno izvravanje podravaju ulazno/izlazne operacije izm edu zadataka u obliku cevi (engl. pipes). U Javinoj biblioteci za ulazno/izlazne operacije njih predstavljaju klase PipedWriter (pom ou koje zaatak pie u cev) i PipedReader (pom ou koje drugi zadatak ita iz iste cevi). To m oete sm atrati varijacijom problem a proizvoaa i potroaa, uz cevovod kao gotovo reenje. U sutini, cev je blokirajui red za ekanje koji je postojao u verzijama Jave pre uvoenja klase BlockingQueue. Sledi jednostavan prim er u kojem dva zadatka kom uniciraju pom ou cevi:
//: paralelno/UICevovod.java // Ulazno/izlazna komunikacija zadataka kroz cevi import java.util.concurrent.*;

Poglavlje 21: Paralelno izvravanje

977

import java.io.*; import java.util.*; import static net.mindview.util.Print.*; class Predajnik implements Runnable { private Random slucajan = new Random(47); private PipedWriter izlaz = new PipedWriter(); public PipedWriter getPipedWriter() { return izlaz; } public void run() { try { while(true) for(char znak = 'A'; znak <= 'z'; znak++) { izlaz.write(znak); TimeUnit.MILLISEC0NDS.sleep(slucajan.nextInt(500));

}
} catch(IOException e) { print(e + Izuzetak tokom pisanja Predajnika"); } catch(InterruptedException e) { print(e + " Prekinuto spavanje Predajnika");

class Prijemnik implements Runnable { private PipedReader ulaz; public Prijemnik(Predajnik predajnik) throws IOException { ulaz = new PipedReader(predajnik.getPipedWriter());

}
public void run() { try { while(true) { // Blokira proces dok ne dobije znakove: printnb("itam: " + (char)ulaz.read() + ", ");

}
} catch(IOException e) { print(e + " Izuzetak tokom itanja Prijemnika");

}
public class UlCevovod { public static void main(String[] args) throws Exception { Predajnik predajnik = new Predajnik(); Prijemnik prijemnik = new Prijemnik(predajnik); ExecutorService exec = Executors.newCachedThreadPool(); exec.execute(predajni k) ; exec.execute(prijemnik); TimeUnit.SEC0NDS.sleep(4); exec.shutdownNow();

978

Misliti na Javi

} /* Ispis: (65% podudaranja) itam: A, itam: B, itam: C, itam: D, itam: E, itam: F, itam: G, itam: H, itam: I, itam: J, itam: K, itam: L, itam: M, java.lang.InterruptedException: sleep interrupted Prekinuto spavanje Predajnika java.io.InterruptedIOException Izuzetak tokom itanja Prijemnika

* ///:Predajnik i Prijemnik predstavljaju zadatke koji treba m eusobno da kom uniciraju. Predajnikpravi objekat tipa PipedW riter to jeste sam ostalan objekat, ali u n u tar Prijemnika pravljenje PipedReadera m ora biti p ridrueno odreenom PipedWriteru u konstruktoru. Predajnik smeta podatke u Writer i odlazi na nasum ino dugo spavanje. M eutim , Prijemnik nem a ni m etode sle e p ( ) niti w a it( ). Ali kada pokrene m etodu re a d ( ), cev se autom atski blokira im ponestane podataka za itanje. O bratite panju na to da se predajnik i prijem nik pokreu u m etodi m a in ( ), nakon to su objekti potp u n o konstruisani. Ako p o tp u n o konstruisane objekte ne pokrenete, cev se razliito ponaa na raznim platform am a. (Vodite rauna o tom e da su objekti tipa BlockingQueue (blokirajui redovi za ekanje) robusniji i lake se koriste.) Vana razlika izm eu objekta tipa PipedReader i norm alnih ulazno/izlaznih operacija vidi se u pozivu m etode sh utdow n N ow () - PipedReaderse m oe prekinuti; s dm ge strane, ako biste poziv ulaz.read( ) prom enili u System .ulaz.rea( ), m etoda in terru p t( ) ne bi uspela da izae iz poziva rea d ( ). Veba 30: (1) Izmenite UlCevovod.java tako da se um esto cevi koristi BlockingQueue.

Uzajamna blokada
Sada znate da objekat moe im ati sinhronizovane m etode ili druge oblike zakljuavanja koji spreavaju zadatke da pristupaju tom objektu dok se ne otkijua uzajam no iskljuiva brava (mutex). Saznali ste i da zadaci m ogu postati blokirani. Dakle, m ogue je da se jedan zadatak zaglavi ekajui na drugi, koji eka na trei it., dok lanac ne oe do zadatka koji eka na onaj prvi zadatak - neprekidan lanac zadataka koji ekaju jedan na drugog i nijedan od njih ne moe da se pom eri. To se naziva uzajamna blokada (engl. deadlock).1' Ako pokrenete program i on odm ah ue u uzajam nu blokadu, greku ete odm ah moi da pronaete. Pravi problem je kada program naizgled radi dobro, ali sadri skrivenu m ogunost uzajam ne blokade. U tom sluaju, uopte ne m orate dobiti nikakvu naznaku da m ogunost uzajam ne blokade postoji, pa e greka biti latentna u program u dok se neoekivano ne desi kupcu (na nain koji e gotovo sigurno biti teko ponoviti). Zato je paljivo projektovanje program a kako bi se spreila uzajam na blokada, kljuan deo razvoja paralelnih sistema. Klasian prim er uzajam ne blokade jeste problem veercfilozofa , koji je smislio Edsger Dijkstra. U njegovoj verziji, filozofa im a pet, ali u prim eru koji u ja ovde opisati, broj fiIozofa je proizvoljan. Ti filozofi deo vrem ena provode u razm iljanju, a deo u jelu. Dok razmiljaju, ne koriste deljene resurse, ali za jelo imaju na raspolaganju ogranienu
' M o e n a s ta ti i uzajam na blokada uprkos izvravanju, k a d a z a d a c i m e n ja ju sv o ja s ta n ia (n e b lo k ira ju se), ali n e u s p e v a ju d a n a p ra v e ita k o risn o .

Poglav[je 21: Paralelno izvravanje

979

koliinu pribora. U originalnom opisu problem a, p rib o r za jelo su viljuke, i za vaenje pageta iz inije na sredini stola potrebne su dve viljuke, ali m eni se ini da je prikladnije da za p rib or za jelo proglasim o tapie (kakvim a Istonjaci jedu pirina). Jasno, svakom filozofu trebaju dva tapia za jelo. Zaplet problem a je sledei: budui da su filozofi, veom a su sirom ani i im aju sam o pet tapia (odnosno tapia im a onoliko koliko im a i filozofa). Stapii su ravnom erno rasporeeni po celom stolu. Kada filozof eli da jede, m o ra da uzm e jedan tapi sa svoje leve strane i jedan s desne. Ako je neki od filozofa s njegove leve ili desne strane ve uzeo jedan od eljenih tapia, na filozof m o ra da saeka dok potrebni tapi ne postane dostupan.
//: paralelno/Stapic.java // tapii za veeru filozofa. public class Stapic ( private boolean zauzet = false; public synchronized void take() throv/s InterruptedException { while(zauzet) wait(); zauzet = true;

}
public synchronized void pusti() { zauzet = false; noti fyAl1 ();

} III-Dva Filozofa ne mogu istovremeno uzeti (m etodom take( )) isti Stapic. Sem toga, ako je jedan Filozof ve uzeo odreeni Stapic, drugi moe m etodom w a it( ) da saeka dok taj Stapic ne postane dostupan, a to e se destiti kada trenutni korisnik pozove m etodu p u sti( ). Kada zadatak Filozof pozove m etodu ta k e ( ), on eka dok indikator zauzet ne poprim i vrednost false (dok Filozof koji ga trenu tn o dri ne pusti taj Stapic). Zatim zadatak daje indikatoru zauzet vrednost true da bi pokazao da Stapic sada dri novi Filozof. Kada taj Filozof zavri jelo pom ou Stapica, on poziva m etodu p u s ti( ) da bi prom enio vrednost tog indikatora i m etodu n otifyA ll( ) kojom obavetava sve druge Filozofe koji m oda ekaju na taj Stapic.
//: paralelno/Fi1ozof.java // Filozof veera import java.util.concurrent.*; import java.uti1 .*; import static net.mindview.uti 1 .Print.*; public class Filozof implements Runnable { private Stapic levi; private Stapic desni; private final int id;

98 0

Misliti na Javi

private final int tezinskiFaktor; private Random slucajan = new Random(47); private void pause() throws InterruptedException { if(tezinskiFaktor == 0) return; TimeUnit.MILLISECONDS.sleep( slucajan.nextInt(tezinskiFaktor * 250));

}
public Filozof(Stapic levi, Stapic desni, int ident, int ponder) { this.levi = levi; this.desni = desni; id = ident; tezinskiFaktor = ponder;

}
public void run() { try { while(!Thread.interrupted()) { print(this + " " + "razmilja"); pause(); // Filozof je ogladneo print(this + " " + "uzima desni"); desni.take(); print(this + " " + "uzima levi"); levi ,take(); print(this + " " + "jede"); pause(); desni.pusti(); levi .pusti ();

}
} catch(InterruptedException e) { print(this + " " + "izlazi pomou prekida");

} }
public String toStringO { return "Filozof " + id; }

} ///:U m etodi F ilozof.ru n ( ), svaki F ilozof ili misli ili jede. M etoda p a u se ( ) prouzrokuje spavanje zadatka tokom nasum ino odabranog vrem enskog razdoblja ukoliko je tezinskiFaktor razliit od nule. Dakle, F ilozof razmilja tokom nasum ino odabranog vrem enskog razdoblja, zatim pokuava da uzm e desni i levi Stapic, jede tokom nasum ino odabranog vrem enskog razdoblja i zatim ponavlja ceo postupak od poetka. Sada em o napraviti verziju program a koja e se zavriti uzajam nom blokadom :
//: paralelno/UzajamnoBlokiraniFilozofiKojiVeceraju.java // Pokazuje kako u programu moe biti prikrivena uzajamna blokada. // {Args: 0 5 odmor} import java.util .concurrent.*;

Poglavfje 2 1: Paralelno izvravanje

981

public class UzajamnoBlokiraniFilozofiKojiVeceraju { public static void main(String[] args) throws Exception { int ponder = 5; if(args.length > 0) ponder = Integer.parselnt(args); int broj = 5; if(args.length > 1) broj = Integer.parselnt(args); ExecutorService exec = Executors.newCachedThreadPool(); Stapic[] stapici = new Stapic[broj]; for(int i = 0 ; i < broj; i++) stapici [i] = new Stapic (); for(int i = 0; i < broj; i++) exec.execute(new Filozof( stapici[i], stapici [ (i+1) % b r o j ] , i, ponder)); if(args.length == 3 && args.equals("odmor")) TimeUni t.SECONDS.sleep(5); el se { System.out.print1n("Pritisnite 'Enter' ako elite da prekinete izvravanje programa"); System.in.read();

}
exec.shutdownNow();

}
} /* (Pokrenite da biste videli rezultat) *///:-

Prim etili ste sledee: ukoliko Filozofi provode malo vrem ena razmiljajui, kada pokuaju da jedu svi e jedan drugom konkurisati za Stapice i uzajam na blokada e nastupiti m nogo bre. Prvi argum ent na kom andnoj liniji zadaje teinski faktor ponder koji utie na koliinu vrem ena koje svaki Filozof provodi u razmiljanju. Ako Filozofa im a m nogo ili oni vazdan razmiljaju, m oda uopte neete videti uzajam nu blokadu, iako je ona i dalje m ogua. N ula kao argum ent na kom andnoj liniji ini da program prilino brzo ue u uzajam nu blokadu. O bratite panju na to da objektim a tipa Stapic nisu potrebni interni identifikatori; njih identifikuje njihov poloaj u nizu stapici. K onstruktor svakog objekta tipa Filozof dobija referencu na levi i desni Stapic. Svaki Filozof (sem poslednjeg) u inicijalizaciji biva smeten izm edu sledeeg para Stapica. Poslednji Filozof dobija nulti Stapic kao svoj desni i tako se obiao krug oko stola - naime, poslednji Filozof sedi odm ah do prvog i obojica dele nulti Stapic. Ukoliko sada svi Filozofi istovrem eno pokuaju da jedu, svaki od njih e m orati da eka dok susedni Filozof ne spusti svoj Stapic. Program e zato ui u uzajam nu blokadu. Ukoliko Filozofi vie vrem ena provode u razmiljanju nego u jelu, onda je i verovatnoa da e im zatrebati deljeni resursi (Stapici) m nogo m anja, i vi ete biti uvereni da program ne m oe ui u uzajam nu blokadu (ako za ponder zadate vrednost razliitu od nule ili veliki broj Filozofa), iako to nije istina. Ovaj prim er je zanimljiv upravo zato to pokazuje da program moe naizgled da radi ispravno, a da u stvari krije uzajam nu blokadu.

982

Misliti na Javi

Da biste reili problem , m orate shvatiti da uzajam na blokada nastaje ako su istovrem eno ispunjena etiri uslova: 1. Uzajam na iskljuivost. Barem jedan resurs koji zadaci upotrebljavaju ne sme biti deljiv. U ovom sluaju, svaki Stapic u svakom trenutku moe koristiti sam o jedan Filozof. 2. Barem jedan zadatak m o ra zauzim ati resurs i ekati na resurs koji tren u tn o koristi drugi zadatak. D rugim reima, da bi se ostvarila uzajam na blokada, Filozof m ora drati jedan Stapic i ekati na drugi. 3. Resurs ne m oe biti predu p red n o oduzet zadatku. Zadaci oslobadaju resurse iskljuivo kao norm alan dogaaj. Nai Filozofi su pristojni i ne otim aju Stapice jedan od drugog. 4. Moe se javiti kruno ekanje, p ri kojem prvi zadatak eka na resurs koji koristi drugi zadatak, drugi eka na resurs koji koristi trei zadatak itd., dok jedan od zadataka eka na resurs koji koristi prvi i tim e zatvara uzajam nu blokadu. U program u UzajamnoBlokiraniFilozofiKojiVeceraju.java, kruno ekanje nastaje zato to svaki Filozof najpre pokuava da uzm e Stapic s desne strane, a zatim onaj sleva. Poto svi ovi uslovi m oraju biti ispunjeni da bi se ostvariia uzajam na blokada, dovoljno je da spreite sam o jedan od njih i izbegli ste uzajam nu blokadu. U ovom program u, najlaki nain spreavanja uzajam ne blokade jeste ruenje etvrtog uslova. Taj uslov je ispunjen zato to svaki F ilozof pokuava da uzm e svoje Stapice po odreenom redosledu: prvo desni, onda levi. Zato je m ogua situacija u kojoj svaki od njih dri svoj desni Stapic i eka da uzm e levi, a to je uslov za kruno ekanje. M eutim , kada bi poslednji Filozof bio inicijalizovan tako da prvo pokua da uzm e levi tapi, a tek onda desni, tai Filozof ne bi mogao da sprei Filozofa sa svoje desne strane da uzm e tapi izmedu njih. Time bi kruno ekanje bilo spreeno. To je sam o jedno reenje problem a, a reili biste ga i spreavanjem ispunjenja bilo kojeg od preostalih uslova. (Vie o tom e proitajte u nekoj od knjiga za naprednije vienitno program iranje):
//: paralelno/PopravljeniFi1ozofiKojiVeceraju.java // Filozofi koji veeraju bez uzajamne blokade. // {Args: 5 5 odmor} import java.util.concurrent public class PopravljeniFilozofiKojiVeceraju { public static void main(String[] args) throws Exception { int ponder = 5; if(args.length > 0) ponder = lnteger.parselnt(args[0]); int broj = 5; if(args.length > 1) broj = Integer.parselnt(args[1]); ExecutorService exec = Executors.newCachedThreadPool(); Stapic[] stapici = new Stapicfbroj]; for(int i = 0; i < broj; i++) stapici [i] = new Stapic();

Poglavlje 2 1: Paralelno izvravanje

983

for(int i = 0; i < broj; i++) if(i < (broj-1)) exec.execute(new Fi1ozof( stapici [i], stapici [i+1], i, ponder)); el se exec.execute(new Filozof( stapici[l], stapici[i], i, ponder)); if(args.length == 3 && args.equals("odmor")) TimeLlni t.SEC0NDS.sleep(5); el se { System.out.println("Pritisnite 'Enter' ako elite da prekinete izvravanje programa"); System.in.read();

}
exec.shutdownNow();

}
} /* (Pokrenite da biste videli rezultat) *///:

U zajam nu blokadu sm o uklonili tako to poslednji Filozof sada uzima i puta levi Stapic pre desnog, i program e raditi dobro. Java nem a podrku za spreavanje uzajam ne blokade; m orate je sami izbei tako to ete paljivije projektovati program . To nisu utene rei za onoga ko pokuava da otkrije i otkloni greku iz program a koji upada u uzajam nu blokadu. Veba 31: (8) Izmenite program UzajamnoBiokiraniFilozofiKojiVeceraju.java na sledei nain: kada filozof upotrebi tapie, neka ih ne sputa na sto nego u zdelu. Kada filozof hoe da jede, uzim a prva dva dostupna tapia iz te zdele. Da li je tim e izbegnuta m ogunost uzajam ne blokade? Da li ete prostim sm anjenjem broja dostupnih tapia ponovo uvesti m ogunost uzajam ne blokade?

Nove komponente biblioteke


U biblioteci java.util.concurrent u Javi SE5 postoji znatan broj novih klasa za reavanje problem a paralelnog izvravanja. Ako nauite da ih koristite, to e vam pom oi da piete jednostavnije i robusnije program e za paralelno izvravanje. U ovom odeljku prikazau reprezentativan uzorak prim era raznih kom ponenata, ali nekoliko njih - one koje se ree sreu i koriste ovde nee biti razm otrene. Poto ove kom ponente slue za reavanje raznih problem a, ne postoji jasan nain da ih organizujem o, pa u krenuti od jednostavnijih prim era ka sve sloenijim.

CountDovvnLatch - brava sa odbrojavanjem


Koristi se za sinhronizaciju jednog ili vie zadataka tako to ih prim orava da ekaju na dovrenje skupa operacija koje obavljaju drugi zadaci. Najpre CountDovvnLatch objektu date poetnu vrednost. Svaki zadatak koji za taj objekat pozove m etodu a w a it( ), blokira se dok se ta vrednost ne izjednai s nulom . Ostali zadaci m ogu pozivati m etodu co u n tD o w n ( ) za taj objekat i tako smanjivati tu vrednost;

984

Misliti na Javi

to odbrojavanje se obino radi kada zadatak obavi svoj posao. CountDownLatch se koristi jednokratno; vrednost se ne m oe resetovati. Kada vam zatreba verzija koja resetuje vrednost, upotrebite CyclicBarrier. Zadaci koji pozovu co u n tD o w n ( ) ne blokiraju se zbog tog poziva. Blokiran je sam o poziv m etode aw ait( ) dok se vrednost ne izjednai s nulom . Ova klasa se obino koristi tako to se problem podeli na n nezavisno reivih zadataka i napravi objekat tipa CountDownLatch kojem se dodeli poetna vrednost n. Svaki zavren zadatak poziva co u n tD o w n ( ) za taj objekat. Zadaci koji ekaju da se problem rei pozivaju a w ait( ) za taj objekat, da bi saekali dok problem bude reen. Tu tehniku prikazuje sledei prim er:
//: paralel no/CountDownl_atchPrimer.java import java.uti1 .concurrent.*; import java.uti1.*; import static net.mindview.util.Print.*; // Obavlja deo nekog zadatka: class DeoZadatka implements Runnable { private static int brojac = 0; private final int id = brojac++; private static Random slucajan = new Random(47); private final CountDownLatch brava; DeoZadatka(CountDownLatch brava) { this.brava = brava;

)
public void run() { try { radiStaTrebaO ; brava.countDown(); } catch(InterruptedException izz) { // Prihvatljiv nain ekanja

} }
public void radiStaTreba() throws InterruptedException { TimeUnit.MILLISEC0NDS.sleep(slucajan.nextInt(2000)); print(this + "zavren");

}
public String toString() { return String.format("%l$-3d ", id);

} }
// eka na CountDownLatch: class ZadatakKojiCeka implements Runnable { private static int brojac = 0; private final int id = brojac++; private final CountDownLatch brava; ZadatakKojiCeka(CountDownLatch brava) { this.brava = brava;

Poglavjje 21: Paralelno izvravanje

985

}
public void run() { try { brava.await(); print(this + " proao barijeru brave"); } catch(InterruptedException izz) { print(this + " prekinut");

} }
public String toStringO { return String.format("ZadatakKojiCeka %l$-3d ", id);

} }
public class CountDownLatchPrimer { static final int VELICINA = 100; public static void main(String[] args) throws Exception { ExecutorService exec = Executors.newCachedThreadPool(); // Svi moraju deliti isti objekat tipa CountDownLatch: CountDownLatch brava = new CountDownLatch(VELICINA); for(int i = 0; i < 10; i++) exec.execute(new ZadatakKojiCeka(brava)); for(int i = 0; i < VELICINA; i++) exec.execute(new DeoZadatka(brava)); print("Svi zadaci pokrenuti"); exec.shutdown(); // Ugasi kada se svi zadaci zavre

}
} /* (Pokrenite da biste videli rezultat) *///:-

DeoZadatka spava tokom nasum ino odabranog vrem enskog razdoblja i tako sim ulira izvravanje dela zadatka, a ZadatakKojiCeka predstavlja deo sistema koji m ora da saeka dok se prvi deo problem a ne rei. Svi zadaci rade sa istim objektom tipa CountDownLatch definisanim u m etodi m a in ( ). Veba 32: (7) Upotrebite CountDownLatch za korelaciju rezultata raznih Ulaza u prim eru UkrasnaBasta.java. Iz nove verzije prim era uklonite nepotreban kod.

Bezbednost niti iz biblioteke


O bratite panju na to da DeoZadatka sadri statian objekat tipa Random, to znai da bi vie zaataka moglo istovrem eno da pozove R andom .nextInt( ). Da li je to bezbedno? Ako problem postoji, u ovom sluaju ga moete reiti tako to ete objektu DeoZadatka dati sopstveni Random objekat, tj. ukloniti m odifikator static. Ali ostaje opte pitanje o svim standardnim m etodam a iz Javine biblioteke: koje su bezbedne u vienitnom radu, a koje nisu? Naalost, u dokum entaciji JDK ne postoji odgovor. Ispostavlja se da Random .nextInt( ) jeste bezbedna u vienitnom izvravanju, ali to se tie drugih m etoda, moraete to sami da otkrivate od sluaja do sluaja, bilo pretraivanjem Weba bilo pregledanjem koda Java biblioteke. To i nije ba pohvalno za programski jezik koji je, barem u teoriji, projektovan upravo zato da podri paralelno izvravanje.

986

Misliti na Javi

CyclicBarrier
Klasu CyclicBarrier koristite u situacijam a kada hoete da napravite gru p u zadataka koji se izvravaju paralelno, a zatim da saekate dok se svi oni ne zavre pre nego to preete na sledei korak (neto kao j o in ( ), recim o). Prilikom prolaska kroz barijeru svi paralelni zadaci su zavreni, pa napred m oete da krenete sa rezultatim a svih njih. To je veom a slino klasi CountDownLatch, sem to je objekat tipa CountDownLatch za jed n o k ratn u upotrebu, dok se objekat tipa CyclicBarrier moe koristiti vie puta. Simulacije m e fasciniraju od prvih susreta s raunarom , a paralelno izvravanje je kljuni faktor koji om oguuje simulacije. Prvi program koji sam napisao22 bila je sim ulacija: igra s konjskim trakam a napisana u BASIC-u, nazvana (zbog ograniene duine im ena datoteka) HOSRAC.BAS. Sledi objektno orijentisana, vienitna verzija tog programa, u kojoj je upotrebljena klasa CyclicBarrier:
//: paralelno/TrkaKonja.java // Upotreba klase CyclicBarrier. import java.util.concurrent.*; import java.util.*; import static net.mindview.util.Print.*; class Konj implements Runnable { private static int brojac = 0; private final int id = brojac++; private int koraka = 0; private static Random slucajan = new Random(47); private static CyclicBarrier barijera; public Konj(CyclicBarrier b) { barijera = b; } public synchronized int dajKorake() { return koraka; } public void run() { try { whi 1 e(IThread.interr u p t e d O ) { synchronized(this) { koraka += slucajan.nextlnt(3); // Daje 0, 1 ili 2

}
bari jera.await();

}
} catch(InterruptedException e) { // Legitiman nain izlaska } catch(BrokenBarrierException e) { // elimo da dobijemo obavetenje o ovome throw new RuntimeException(e);

} }
public String toStringO { return "Konj " + id + " "; } public String staza() { StringBui1der s = new StringBui1der();

22 U p rv o j g o d in i s re d n je k o le; u u io n ic i je b io te le p rin te r A SR -33 s m o d e m o m b rz in e 110 b o d a za


a k u s ti n u v e zu s ra u n a r o m H P -1 0 0 0 .

Poglavlje 2 1: Paralelno izvravanje

987

for(int i = 0; i < dajKorake(); i++) s.append("*"); s.append(id); return s.toString();

public class TrkaKonja { static final int DUZINA_STAZE = 75; private List<Konj> konji = new ArrayList<Konj>(); private ExecutorService exec = Executors.newCachedThreadPool(); private CyclicBarrier barijera; public TrkaKonja(int nKonja, final int sacekaj) { barijera = new CyclicBarrier(nKonja, new Runnable() { public void run() { StringBuilder s = new StringBuilder(); for(int i = 0; i < DUZINA_STAZE; i++) s.append("="); // Ograda trkalita print(s); for(Konj konj : konji) print(konj.staza()); for(Konj konj : konji) if(konj,dajKorake() >= DUZINA_STAZE) { print(konj + "pobedio!"); exec.shutdownNow(); return;

}
try { TimeUnit.MILLISECONDS.sleep(sacekaj); } catch(InterruptedException e) { print("prekinuto spavanje koje je akcija barijere");

} } });
for(int i = 0; i < nKonja; i++) { Konj konj = new Konj(barijera); konji.add(konj); exec.execute(konj);

} }
public static void main(String[] args) { int nKonja = 7; int sacekaj = 200; if(args.length > 0 ) { // Opcioni argument int n = new lnteger(args[0]); nKonja = n > 0 ? n : nKonja;

}
if (args.1ength > ! ) { / / Opcioni argument

988

Misliti na Javi

int p = new Integer(args[l]); sacekaj = p > -1 ? p : sacekaj;

}
new TrkaKonja(nKonja, sacekaj);

}
} /* (Pokrenite da biste videli rezultat) *///:

O bjektu tipa CyclicBarrier m oe biti pri od ata akcija barijere", a to je klasa koja realizuje interfejs Runnable i izvrava se autom atski kada se (na poetku zadat) broj izjednai s nulom - to je druga razlika izm eu klasa CyclicBarrier i CountdownLatch. Ovde je akcija barijere anonim na klasa koja se daje k o nstru k to ru objekta tipa CyclicBarrier. Probao sam da postignem da svaki konj odtam pa kada je stigao na cilj, ali se u tom sluaju redosled prikazivanja m enja u zavisnosti od m ehanizm a za raspodelu procesorskog vrem ena nitim a (zadacim a). Klasa CydicBarrier om oguuje svakom konju da radi sve to treba kako bi doao do cilja, a zatim m ora da eka na barijeri dok ne dou svi ostali konji. Kada svi konji stignu, objekat tipa CyclicBarrier autom atski poziva svoju akciju barijere, tj. zadatak koji realizuje interfejs Runnable i prikazuje konje redom kojim su stigli, kao i ogradu trkalita. Kada svi zadaci pro u barijeru, ona je autom atski sprem na za sledeu rundu. Ako elite da postignete efekat veom a jednostavne animacije, sm anjite prozor konzole tako da se vide sam o konji.

De!ayQueue
Ovo je neogranien BlockingQ ueue (blokirajui red za ekanje) objekata koji realizuju interfejs Delayed, tj. svaki je odloen na neko vreme (engl. delayed). O bjekat moe biti uzet iz reda za ekanje tek kada njegovo vrem e odlaganja istekne. Red je tako ureen da je eonom objektu preostalo vrem e odlaganja koje se najvie skratilo od poetka izvravanja. Ako se nijedno vrem e odlaganja nije skratilo, onda nem a eonog elem enta i metoda p o ll( ) vraa null (zato u taj red za ekanje ne m oete stavljati null elemente). U sledeem prim eru D elayed objekti su i sami zadaci, a P otrosacO dlozenihZ adataka iz rada za ekanje uzim a najhitniji zadatak (onaj kojem se vreme odlaganja najvie skratilo od poetka izvravanja) i izvrava ga. Dakle, D elayQ ueue je varijacija prioritetnog reda za ekanje.
//: paralelno/DelayQueuePrimer.java import java.util.concurrent.*; import java.uti1 .*; import static java.util.concurrent.TimeUnit.*; import static net.mindview.util.Print.*; class OdlozeniZadatak implements Runnable, Delayed ( private static int brojac = 0; private final int id = brojac++; private final int delta; private final long okidac; protected static List<OdlozeniZadatak> sekvenca =

Poglav[ie 21: Paralelno izvravanje

989

new ArrayList<OdlozeniZadatak>(); public OdlozeniZadatak(int odlaganjeUMilisekundama) { delta = odlaganjeUMilisekundama; okidac = System.nanoTime() + NANOSECONDS.convert(delta, MILLISECONDS); sekvenca.add(this);

}
public long getDelay(TimeUnit jedinica) { return jedinica.convert( okidac - System.nanoTime(), NANOSECONDS);

}
public int compareTo(Delayed arg) { OdlozeniZadatak onaj = (OdlozeniZadatak)arg; if(okidac < onaj.okidac) return -1; if(okiac > onaj.okidac) return 1; return 0;

}
public void run() { printnb(this + " "); } { public String toString() " Zadatak " + id;

return St ri ng .f or ma t("[%l$-4d]", delta) +

}
public String sazetak() {

return "(" + id +

+ delta + ")";

}
public static class StrazarNaCilju extends OdlozeniZadatak { private ExecutorService exec; public StrazarNaCilju(int odlaganje, ExecutorService e) { su pe r( od la ga nj e); exec = e;

}
public void run() { { + " "); for(OdlozeniZadatak iz : sekvenca) p r i n t n b ( i z .s a z e t a k O

}
pri nt (); print(this + " poziva shutdo wn No w( )"); ex e c . s h u td ow nN ow ();

class Po trosacOdlozenihZadataka implements Runnable { pr ivate DelayQueue<OdlozeniZadatak> rzc; public PotrosacOdlozenihZadataka(DelayQueue<OdlozeniZadatak> rzc) this.rzc = rzc; {

}
public void run() try { w h i 1e ( !T h r e a d .i nterru pt ed ()) {

990

Misliti na Javi

rzc.take().run(); // Pokreni zadatak s tekuom niti } catch(InterruptedException e) { // Prihvatljiv nain izlaska

}
print("Zavren PotrosacOdlozenihZadataka");

} }
public class DelayQueuePrimer { public static void main(String[] args) { Random slucajan = new Random(47); ExecutorService exec = Executors.newCachedThreadPool(); DelayQueue<OdlozeniZadatak> redzacekanje = new DelayQueue<OdlozeniZadatak>(); // Popuni zadacima s nasuminim odlaganjima: for(int i = 0; i < 20; i++) redzacekanje.put(new 0dlozeniZadatak(slucajan.nextInt(5000))); // Zadaj taku zaustavljanja redzacekanje.add(new OdlozeniZadatak.StrazarNaCilju(5000, exec)); exec.execute(new PotrosacOdlozenihZadataka(redzacekanje));

}
) /* Ispis: [128 ] Zadatak 11 [200 ] Zadatak 7 [429 ] Zadatak 5 [520 ] Zadatak 18 [555 ] Zadatak 1 [961 ] Zadatak 4 [998 ] Zadatak 16 [1207] Zadatak 9 [1693] Zadatak 2 [1809] Zadatak 14 [1861] Zadatak 3 [2278] Zadatak 15 [3288] Zadatak 10 [3551] Zadatak 12 [4258] Zadatak 0 [4258] Zadatak 19 [4522] Zadatak 8 [4589] Zadatak 13 [4861] Zadatak 17 [4868] Zadatak 6 (0:4258) (1:555) (2:1693) (3:1861) (4:961) (5:429) (6:4868) (7:200) (8:4522) (9:1207) (10:3288) (11:128) (12:3551) (13:4589) (14:1809) (15:2278) (16:998) (17:4861) (18:520) (19:4258) (20:5000) [5000] Zadatak 20 poziva shutdownNow() Zavren PotrosacOdlozenihZadataka

* ///:OdlozeniZadatak sadri listu List<OdlozeniZadatak> nazvanu sekvenca koja odrava redosled kojim su zadaci pravljeni, pa m oem o videti da se red za ekanje zaista ureduje. Interfejs Delayed im a m etodu g etD ela y ( ) koja kazuje koliko vrem ena je preostalo do isteka odlaganja ili koliko je proteklo od isteka odiaganja. Ta m etoda nas prisiljava da upotrebim o klasu TimeUnit, zato to je njen argum ent tog tipa. Ispostavlja se da je ta klasa veoma podesna jer om oguuje laku konverziju vrem enskih jedinica bez ikakvih prorauna. Prim era radi, iznos delta se pam ti u m ilisekundam a, a m etoda Jave SE5 System .nanoTim e( ) daje vrem e u nanosekundam a. Iznos delta konvertujem o tako to saoptim o u kojim jedinicam a jeste i u kojim elimo da bude, i to na ovaj nain:
NAN0SEC0NDS.convert(delta, MILLISECONDS);

U metodi getD elay( ), eljena jedinica se prosleduje kao argum ent jedinica i pom ou nje vreme proteklo od okidaa konvertujem o u jedinice koje zahteva pozivalac, a da ak i ne znam o koje su. (Ovo je jednostavan prim er projektnog obrasca Strategy, u kojem se deo algoritm a prosleuje kao argum ent.)

Poglavlje 21: Paralelno izvravanje

991

Za ureivanje, interfejs Delayed nasleuje i interfejs Comparable, pa m oram o realizovati m etodu com pareT o( ) tako da proizvodi razum na poreenja. M etode to S trin g ( ) i sazetak( ) form atiraju izlaz, a ugneena klasa StrazarNaCilju slui za gaenje svega zato to je stavljena u red za ekanje kao njegov poslednji element. Im ajte u vidu da je PotrosacOdlozenihZadataka i sam zadatak, pa im a sopstvenu nit (objekat tipa Thread) u kojoj m oe da izvrava svaki zadatak koji izae iz reda za ekanje. Poto se zadaci izvravaju redom prioriteta reda za ekanje, u ovom prim eru nem a potrebe da pravim o zasebne niti za izvravanje odloenih zadataka (objekata tipa OdlozeniZadatak). Iz rezultata vidite da redosled pravljenja zadataka nem a uticaja na redosled izvravanja, nego se zadaci izvravaju redosledom kojim im istie odlaganje, kao to smo oekivali.

PriorityBlockingQueue
U sutini, ovo je p rio ritetn i red za ekanje ije se operacije vaenja iz reda blokiraju. U narednom prim eru, objekti u prioritetn o m redu za ekanje su zadaci koji se iz reda za ekanje pojavljuju po redosledu njihovih prioriteta. ZadatakSPrioritetom dobija broj prioriteta iz kojeg sledi taj redosled:
//: paralelno/PriorityBlockingQueuePrimer.java import java.uti1.concurrent.*; import java.uti 1 .*;
import static net. mi nd vi ew .u ti1.P r i n t .*; class Za da ta kS Pr io ri te to m implements Runnable, Co mp ar ab le <ZadatakSPrioritetom> private Random slucajan = new Random(47); private static int brojac = 0; private final private final int id = brojac++; int prioritet; (

protected static List<ZadatakSPrioritetom> sekvenca = new A r r a y L i s t < Za da ta kS Pri or it et om >( ); public ZadatakSPrioritetom(int prioritet) thi s .priori tet = prioritet; sekvenca.addfthi s ) ; {

}
public int co mp ar eT o(ZadatakSPrioritetom arg) return prioritet < arg.prioritet ? 1 : (prioritet > a r g .prioritet ? -1 : 0); {

}
public void run() try { TimeUnit.MILLISECONDS.sleep(sl uc a j a n . n e x t l n t (250)); ) catch(InterruptedException e) { // Prihvatljiv nain izlaska {

}
pri nt(thi s ) ;

992

Misliti na Javi

public String toString() { return String.format("[%l$-3d]", prioritet) + " Zadatak " + id;

}
public String sazetak() { return "(" + id + + prioritet + ")";

}
public static class StrazarNaCilju extends ZadatakSPrioritetom { private ExecutorService exec; public StrazarNaCilju(ExecutorService e) { super(-l); // Najnii prioritet u ovom programu exec = e;

}
public void run() { int broj = 0; for(ZadatakSPrioritetom iz : sekvenca) { printnb(iz.sazetak()); if(++broj % 5 == 0) print();

}
print(); print (this + 1 1 Poziva snutdownNow()"); exec.shutdownNow();

} } }
class ProizvodjacZadatakaSPrioritetom implements Runnable { private Random slucajan = new Random(47); private Queue<Runnable> redzacekanje; private ExecutorService exec; public ProizvodjacZadatakaSPriori tetom( Queue<Runnable> rzc, ExecutorService e) { redzacekanje = rzc; exec = e; // Upotrebljava se za StrazarNaCilju

}
public void run() { // Neogranien red za ekanje; nikada se ne blokira. // Popuniu brzo nasuminim prioritetima: for(int i = 0; i < 20; i++) { redzacekanje.add(new ZadatakSPrioritetom(slucajan.nextlnt(10))); Thread.yield();

}
// Ubacujem postepeno poslove najvieg prioriteta: try { for(int i = 0; i < 10; i++) { TimeUni t.MILLISECONDS.sleep(250); redzacekanje.add(new ZadatakSPrioritetom(lO));

}
// Dodajem poslove, najpre one s najniim prioritetom:

Poglavlje 2 1: Paralelno izvravanje

993

for(int i = 0; i < 10; i++) redzacekanje.add(new ZadatakSPrioritetom(i)); // Oznaka kraja koja zaustavlja sve zadatke: redzacekanje.add(new ZadatakSPrioritetom.StrazarNaCilju(exec)); } catch(InterruptedException e) ( // Prihvatljiv nain izlaska

}
print("Zavren ProizvodjacZadatakaSPrioritetom");

} }
class PotrosacZadatakaSPrioritetom implements Runnable { private PriorityBlockingQueue<Runnable> rzc; publi c PotrosacZadatakaSPri ori tetom( PriorityBlockingQueue<Runnable> rzc) { this.rzc = rzc;

}
public void run() { try { whi 1e (!Thread.interrupted ()) // Za izvravanje zadatka upotrebi tekuu nit: rzc.take(),run(); } catch(InterruptedException e) { // Prihvatljiv nain izlaska

}
pri nt("Zavren PotrosacZadatakaSPri ori tetom");

public class PriorityBlockingQueuePrimer { public static void main(String[] args) throws Exception { Random slucajan = new Random(47); ExecutorService exec = Executors.newCachedThreadPool(); PriorityBlockingQueue<Runnable> redzacekanje = new Priori tyBlocki ngQueue<Runnable>(); exec.execute(new ProizvodjacZadatakaSPrioritetom(redzacekanje, exec)); exec.execute(new PotrosacZadatakaSPrioritetom(redzacekanje));

}
} /* (Pokrenite da biste videli rezultat) * / / / : -

Kao u prethodnom prim eru, redosled pravljenja objekata tipa ZadatakSPrioritetom pam ti se u listi sekvenca, radi poreenja sa stvarnim redosledom izvravanja. M etoda run( ) spava kratko, nasum ino odabrano vreme i tam pa inform acije o objektu, a StrazarN aC ilju ima funkciju kao pre i jem i da je poslednji objekat u redu. ProizvodjacZadatakaSPrioritetom i PotrosacZadatakaSPrioritetom m eusobno su povezani preko objekta tipa PriorityBlockingQueue. Poto blokirajui red za ekanje obavlja svu potrebnu sinhronizaciju, eksplicitna sinhronizacija nije n eo phodna - ne m orate da mislite ima li red za ekanje ijedan elem ent kada pokuate da ga itate, poto e on blokirati itaoca kada m u ponestane elemenata.

99 4

Misliti na Javi

Kontroler staklenika napravljen pomou ScheduledExecutora


U poglavlju Unutranje klase predstavljen je prim er hipotetikog staklenika i njegovog upravljakog sistema koji ukljuuje, iskljuuje i podeava oprem u. To se m oe posm atrati kao jedna vrsta problem a paralelnog izvravanja, pri kom e je svaki eljeni ogaaj u stakleniku zadatak koji se izvrava u unapred definisano vreme. Klasa ScheduIedThreadPoolExecutor im a sve to treba za reavanje ovog problem a. M etodam a sch ed u le( ) (koja zadatak izvrava jednom ) ili schedu!eAtFixedRate( ) (koja izvravanje zadatka ponavlja u pravilnim intervalim a) priprem ate objekte koji realizuju interfejs Runnable za izvravanje u odreeno budue vreme. U poredite naredni prim er i prim er iz poglavlja Unutranje klase da biste videli koliko je sve jednostavnije kada je na raspolaganju gotova alatka kao to je ScheduIedThreadPooIExecutor:
//: paralelno/RasporedjivacStaklenika.java // Prerada programa unutrasnjeklase/KontrolerStaklenika.java // u kojoj je upotrebljen ScheduledThreadPoolExecutor. // (Args: 5000} import java.util.concurrent.*; import java.uti1.*; public class RasporedjivacStaklenika { private volatile boolean svetlo = false; private volatile boolean voda = false; private String termostat = "Dan"; public synchronized String dajTermostat() { return termostat;

}
public synchronized void podesiTermostat(String iznos) { termostat = iznos;

}
ScheduledThreadPoolExecutor rasporedjivac = new ScheduledThreadPoolExecutor(10); public void raspored(Runnable dogadjaj, long odlaganje) { rasporedjivac.raspored(dogadjaj ,odlaganje,Timellni t.MILLISECONDS);

}
public void ponavljaj(Runnable dogadjaj, long pocetnoOdlaganje, long period) { rasporedji vac.scheduleAtFixedRate( dogadjaj, pocetnoOdlaganje, period, TimeUnit.MILLISECONDS);

}
class UkljuciSvetlo implements Runnable { public void run() { // Ovde napiite kod za upravljanje // hardverom koji fiziki pali svetlo. System.out.println("Ukljuujem svetlo"); svetlo = true;

Poglavlje 2 1: Paralelno izvravanje

995

class IskljuciSvetlo implements Runnable { public void run() { // Ovde napiite kod za upravljanje hardverom koji fiziki gasi svetlo. System.out.println("Gasim svetlo"); svetlo = false;

} }
class UkljuciVodu implements Runnable { public void run() { // Ovde napiite kod za upravljanje hardverom. System.out.println("Ukljuujem vodu u stakleniku"); voda = true;

}
class IskljuciVodu implements Runnable { } public void run() { // Ovde napiite kod za upravljanje hardverom. System.out.println("Iskljuujem vodu u stakleniku"); voda = false;

} }
class TermostatNoc implements Runnable { public void run() { // Ovde napiite kod za upravljanje hardverom. System.out.println("Termostat podeen za no"); podesiTermostat("No");

} }
class TermostatDan implements Runnable { public void run() { // Ovde napiite kod za upravljanje hardverom. System.out.println("Termostat podeen za dan"); podesiTermostat("Dan");

} }
class Zvono implements Runnable { public void run() { System.out.println("Zvrc!"); }

}
class Gasi implements Runnable { public void run() { System.out.pri ntln("Gasim"); rasporedjivac.shutdownNow(); // Moram da pokrenem zaseban zadatak da ovo obavi, // poto je rasporeiva ugaen: new Thread() { public void run() { for(RadnaTacka d : podaci) System.out.println(d);

}
} .s ta rt ();

} }

996

Misliti na Javi

// Novina: prikupljanje podataka static class RadnaTacka { final Calendar vreme; final float temperatura; final float vlaznost; public RadnaTacka(Calendar d, float privr, float vlag) { vreme = d; temperatura = privr; vlaznost = vlag;

}
public String toStringO { return vreme.getTime() + String.format( " temperatura: %l$.lf vlaznost: %2$.2f", temperatura, vlaznost);

} }
private Calendar poslednjiPut = Calendar.getlnstance(); { // Podesi vreme na pola punog sata poslednjiPut.set(Calendar.MINUTE, 30); poslednjiPut.set(Calendar.SECOND, 00);

}
private private private private private float poslednjaTemp = 65.0f; int smerTemp = +1; float poslednjaVlaznost = 50.Of; int smerVlaznosti = +1; Random slucajan = new Random(47);

List<RadnaTacka> podaci = Col1ections.synchronizedList( new ArrayList<RadnaTacka>()); class PrikupiPodatke implements Runnable { public void run() { System.out.println("Prikupljanje podataka"); synchronized(RasporedjivacStaklenika.this) { // Praviu se da je interval dui nego to zapravo jeste: poslednjiPut.set(Calendar.MINUTE, poslednjiPut.get(Calendar.MINUTE) + 30); // Verovatnoa promene smera 1/5: if(slucajan.nextlnt(5) == 4) smerTemp = -smerTemp; // Sauvaj prethodni iznos: poslednjaTemp = poslednjaTemp + smerTemp * (l.Of + slucajan.nextFloat()); if(slucajan.nextlnt(5) == 4) smerVlaznosti = -smerVlaznosti; poslednjaVlaznost = poslednjaVlaznost + smerVl aznosti * sl ucajan.nextFloat(); // Kalendar mora biti kloniran, inae bi sve // RadneTake imale reference na isti poslednjiPut. // Za jednostavan objekat kao to je Calendar, // metoda clone() je dovoljno dobra.

Poglavlje 21: Paralelno izvravanje

997

podaci,add(new RadnaTacka((Calendar)pos1ednjiPut.clone(), poslednjaTemp, poslednjaVlaznost));

} } }
public static void main(String[] args) { RasporedjivacStaklenika st = new RasporedjivacStaklenika(); st.raspored(st.new Gasi(), 5000); // Ranije klase "Restart" nisu potrebne: st.ponavljaj(st.new Zvono(), 0, 1000); st.ponavljaj(st.new TermostatNoc(), 0, 2000); st.ponavljaj(st.new UkljuciSvetlo(), 0, 200); st.ponavljaj(st.new IskljuciSvetlo(), 0, 400); st.ponavljaj(st.new UkljuciVodu(), 0, 600); st.ponavljaj(st.new IskljuciVodu(), 0, 800); st.ponavljaj(st.new TermostatDan(), 0, 1400); st.ponavljaj(st.new PrikupiPodatke(), 500, 500);

}
} /* (Pokrenite da biste videli rezultat) *///:-

Ova verzija reorganizuje kod i dodaje jednu novinu: prikupljanje m erenja tem perature i vlanosti u stakieniku. RadnaTacka sari i prikazuje jedan skup merenja, dok je PrikupiPodatke planiran zadatak koji generie sim ulirane podatke i dodaje ih listi List<RadnaTacka> u Stakleniku nakon svakog svog izvravanja. O bratite panju na to da se m odifikatori volatile i synchronized upotrebljavaju na odgovarajuim m estim a kako bi se spreilo da zadaci sm etaju jedan drugom . Prilikoin pravljenja liste koja sadri RadneTake, sve m etode bivaju sinhronizovane uslunom metodom synchronizeL ist( ) iz biblioteke java.util.Collections. Veba 33: (7) Izm enite RasporedjivacStaklenika.java tako da koristi DeIayQueue um esto ScheduledExecutora.

Semafor
O bina brava (eksplicitna iz concurrent.locks ili ugraena synchronized) u svakom trenutku dozvoljava sam o jed no m zadatku da pristupi svakom resursu. Setnafor broja om oguuje da n zadataka istovrem eno pristupa jednom resursu. Moete sm atrati da semafor izdaje dozvole za korienje odredenog resursa, m ada se zapravo ne upotrebljavaju nikakvi objekti dozvola. Kao prim er, razm otriem o pojam grupe objekata koja upravlja ogranienim brojem objekata tako to dozvoljava da b ud u pojedinano izdati na u p otrebu i p otom vraeni kada korisnik zavri svoj posao s njima. Te funkcije em o kapsulirati u generikoj klasi:
//: paralelno/Grupa.java // Semafor u grupi objekata koji ograniava broj zadataka // koji istovremeno mogu koristiti resurs. import java.uti1 .concurrent.*; import java.uti1.*;

998

Misliti na Javi

public class Grupa<T> { private int velicina; private List<T> stavke = new ArrayList<T>{); private volatile boolean[] izdat; private Semaphore dostupan; public Grupa(Class<T> classObjekat, int velicina) { this.velicina = velicina; izdat = new boolean[velicina]; dostupan = new Semaphore(velicina, true); // Popuni grupu objektima koji mogu biti izdati: for(int i = 0; i < velicina; ++i) try { // Pretpostavlja da se koristi podrazumevani konstruktor: stavke.add(classObjekat.newInstance()); } catch(Exception e) { throw new RuntimeException(e);

} }
public T izdajObjekatNaKoriscenje() throws InterruptedException { dostupan.acquire(); return uzmiStavku();

}
public void primiObjekatNazadUGrupu(T x) { if(pustiStavku(x)) dostupan.release();

}
private synchronized T uzmiStavku() { for(int i = 0; i < velicina; ++i) if (! izdat [i]) { izdat[i] = true; return stavke.get(i);

}
return null; // Semafor ne dozvoljava pristup ovoj taki

}
private synchronized boolean pustiStavku(T stavka) { int indeks = stavke.index0f(stavka); if (indeks == -1) return false; // Nije u listi if (izdat[indeks]) { izdat [indeks] = false; return true;

}
return false; // Nije bio izdat

} } ///U ovom pojednostavljenom obliku, konstruktor m etodom n ew lnstance( ) popunjava grupu objektima. Kada vam zatreba nov objekat, pozovete m etodu izdajObjekatNaKoriscenje( ), a kada vam objekat vie nije potreban, prosledite ga m etodi primiObjekatNazadUGrupu().

Poglavlje 21: Paralelno izvravanje

999

Niz izdat tipa boolean prati objekte koji su izdati na korienje, a njim e upravljaju metode uzm iStavku( ) i pustiStavku( ). N jih pak uva sem afor (objekat tipa Semaphore) dostupan, tako da u m etodi izdajObjekatNaKoriscenje(), dostupan blokira poziv ukoliko vie nem a dostup nih dozvola za korienje objekata iz grupe (to znai da u grupi vie nem a objekata). M etoda prim iObjekatNazadUGrupu( ) vraa dozvolu sem aforu ukoliko je objekat koji se vraa validan. Kao p rim er upotrebiem o Debeli, tip objekta koji je skupo praviti zato to njegovom konstruktoru treba m nogo vrem ena da obavi svoj posao:
//: paralelno/Debel i.java // Objekti koje je skupo praviti. public class Debeli ( private volatile double d; // Sprei optimizaciju private static int brojac = 0; private final int id = brojac++; public Debeli() { // Skupa operacija koja se moe prekinuti: for(int i = 1; i < 10000; i++) { d += (Math.PI + Math.E) / (double)i;

} }
public void operacija() { System.out.println(this); } public String toStringO { return "Debeli id: " + id; }

} ///= O kupiem o ove objekte u grupu da bism o ograniili uticaj tog konstruktora. Klasu Grupa em o testirati pravljenjem zadatka koji objekte tipa Debeli uzima na korienje, dri ih neko vrem e i zatim vraa nazad:
//: paralelno/PrimerSemafora.java // Testiranje klase Grupa import java.uti1.concurrent.*; import java.util.*; import static net.mindview.util.Print.*; // Zadatak koji uzima resurs iz grupe: class ZadatakKojiUzimaResurs<T> implements Runnable { private static int brojac = 0; private final int id = brojac++; private Grupa<T> grupa; public ZadatakKojiUzimaResurs(Grupa<T> grupa) { this.grupa = grupa;

}
public void run() { try { T stavka = grupa.izdajObjekatNaKoriscenje(); print(this + "izdat " + stavka); TimeUnit.SECONDS.sleep(l);

1000

Misliti na Javi

print(this +"vraen " + stavka); grupa.primiObjekatNazadUGrupu(stavka); } catch(InterruptedException e) { // Prihvatljiv nain izlaska

} }
public String toStringO { return "ZadatakKojiUzimaResurs " + id + " ";

} }
public class PrimerSemafora { final static int VELICINA = 25; public static void main(String[] args) throws Exception { final Grupa<Debeli> grupa = new Grupa<Debeli>(Debeli.class, VELICINA); ExecutorService exec = Executors.newCachedThreadPool(); for(int i = 0; i < VELICINA; i++) exec.execute(new ZadatakKojiUzimaResurs<Debeli>(grupa)); print("Napravljeni svi ZadaciKojiUzimajuResurs "); List<Debeli> lista = new ArrayList<Debeli>(); for(int i = 0; i < VELICINA; i++) { Debeli f = grupa.izdajObjekatNaKoriscenje(); printnb(i + nit main() izdata na korienje "); f.operacija(); lista.add(f);

}
Future<?> blokiran = exec.submit(new Runnable() { public void run() { try { // Semafor spreava daljnje izdavanje, // pa se poziv blokira: grupa.izdajObjekatNaKoriscenje(); } catch(InterruptedException e) { print("izdajObjekatNaKoriscenjef) prekinuta");

} } });
TimeUnit.SEC0NDS.sleep(2); blokiran.cancel(true); // Izai iz blokiranog poziva print("Vraanje objekata u " + lista); for(Debeli f : lista) grupa.primiObjekatNazadUGrupu(f); for(Debeli f : 1 ista) grupa.primiObjekatNazadUGrupu(f); // Drugi primiObjekatNazadUGrupu // ignoriem exec.shutdown();

}
} /* (Pokrenite da biste videli rezultat) * / / / : -

Poglavlje 2 1: Paralelno Izvravanje

1001

U m etodi m a in ( ), pravi se Grupa objekata tipa D ebeli i skup ZadatakaKojiUzimajuResurs koji poinje da je upotrebljava. Zatim nit m a in ( ) poinje da uzim a objekte tipa D ebeli i da ih ne vraa natrag. Kada uzme sve objekte iz grupe, semafor nee dozvoliti dalje izdavanje objekata na korienje. Zato se blokira m etoda r u n ( ) objekta blokiran, i nakon dve sekunde poziva se m etoda ca n cel( ) da izae iz tog objekta tipa Future. O bratite panju na to da Grupa ignorie redu n d an tn a vraanja objekata. U ovom prim eru raunao sam na to da e se klijent Grupe uvek setiti da dobrovoljno vrati objekte u grupu, to je najjednostavnije reenje kada funkcionie. Ukoliko ne moete da se oslonite na to, knjiga Thinking in Patterns (na adresi www.M indView.net) sadri dalja istraivanja naina upravljanja objektim a izdatim na korienje iz grupe.

Exchanger
Exchanger (razmenjiva) jeste barijera koja om oguava da se m eusobno zam ene objekti dvaju zadataka. Kada zadaci uu u barijeru, im aju svaki po jedan objekat, a kada izau, im aju objekat koji je prethodno im ao onaj drugi zadatak. Ovakvi razmenjivai se obino koriste kada jedan zaatak pravi objekte ija je proizvodnja skupa, a drugi zadatak ih troi; na taj nain, moe se napraviti vie objekata koji se istovrem eno troe. Da bism o upotrebili klasu Exchanger, napraviem o zadatke proizvoaa i potroaa koji preko generikih tipova i Generatora rade sa svim vrstam a objekata, i zatim em o ih prim eniti na klasu Debeli. Klase RazmenjivacProizvodjac i RazmenjivacPotrosac koriste objekat tipa List<T> za razm enu; svaka od njih sadri Exchanger za ovu listu List<T>. Kada pozovete m etodu Exchanger.exchangc( ), ona blokira zadatak dok partnerski zadatak ne pozove svoju m etodu exch an ge( ). Kada obe m etode exchan ge( ) zavre svoj posao, lista tipa List<T> bie zamenjena:
//: paralelno/ExchangerPrimer.java import java.uti1 .concurrent.*; import java.uti1.*; import net.mindview.util.*; class RazmenjivacProizvodjac<T> implements Runnable { private Generator<T> generator; private Exchanger<List<T razmenjivac; private List<T> spremnik; Razmenji vacProizvodjac(Exchanger<Li s t < T rapro, Generator<T> gen, List<T> spremnik) { razmenjivac = rapro; generator = gen; this.spremnik = spremnik;

}
public void run() { try { while(!Thread.interrupted()) { for(int i = 0; i < ExchangerPrimer.velicina; i++) spremni k.add(generator.next());

1002

Misliti na Javi

// Zameni pun za prazan: spremnik = razmenjivac.exchange(spremnik);

}
} catch(InterruptedException e) { // Prihvatljiv nain izlaska.

} } }
class RazmenjivacPotrosac<T> implements Runnable { private Exchanger<List<T razmenjivac; private List<T> spremnik; private volatile T vrednost; RazmenjivacPotrosac(Exchanger<List<T rapot, List<T> spremnik){ razmenjivac = rapot; this.spremnik = spremnik;

}
public void run() { try { while(!Thread.interrupted()) { spremnik = razmenjivac.exchange(spremnik); for(T x : spremnik) { vrednost = x; // Daj vrednost spoljnom svetu spremnik.remove(x); // OK za CopyOnWriteArrayList

} }
} catch(InterruptedException e) { // Prihvatljiv nain izlaska.

}
System.out.println("Konana vrednost: " + vrednost);

} }
public class ExchangerPrimer { static int velicina = 10; static int odlaganje = 5; // Sekundi public static void main(String[] args) throws Exception { if(args.length > 0) velicina = new lnteger(args[0]); iffargs.length > 1) odlaganje = new Integer(args[l]); ExecutorService exec = Executors.newCachedThreadPool(); Exchanger<Li st<Debel i xc = new Exchanger<List<Debel i ( ) ; List<Debeli> 1 istaProizvodjaca = CopyO"WriteArrjyL'>st-'f-hi' , 1istaPotrosaca = new CopyOnWriteArrayList<Debeli>(); exec.execute(new Razmenji vacProi zvodjac<Debeli >(xc, BasicGenerator.create(Debel i.class), 1istaProi zvodjaca)); exec.execute( new RazmenjivacPotrosac<Debeli>(xc, 1istaPotrosaca));

Poglavlje 2 1: Paralelno izvravanje

1003

T imeUni t .SECONDS.sl eep(odl aganje); exec.shutdownNow();

}
} /* Ispis: (primer) Konana vrednost: Debeli id: 29999

* ///:U m etodi m a in ( ), pravi se jedan Exchanger koji koriste oba zadatka, i dve liste tipa CopyOnWriteArrayList za razm enu. Ova varijanta objekta tipa List tolerie pozivanje m etode rem ove( ) tokom prolaska kroz listu (ne baca ConcurrentModificationException). RazmenjivacProizvodjac popunjava jednu listu, zatim zamenjuje p u n u listu praznom koju m u prosleduje RazmenjivacPotrosac. Klasa Exchanger ini da popunjavanje jedne liste i troenje druge m ogu da se odvijaju istovremeno. Veba 34: (1) Izm enite ExchangerPrimer.java tako da umesto klase Debeli upotrebite neku svoju.

Simulacija
Jedna od najzanimljivijih i najuzbudljivijih prim ena paralelnog izvravanja jeste pravljenje simulacija. Zbog paralelnosti, svaka kom ponenta simulacije moe biti zaseban zadatak, a sim ulaciju je zato m nogo lake program irati. Mnoge video igrice i CGI anim acije u film ovim a su simulacije, a i ranije prikazani program i TrkaKonja.java i RasporedjivacStaklenika.java mogu se sm atrati sim ulacijama.

Simulacija alterskog slubenika


Ova klasina simulacija predstavlja svaku situaciju u kojoj se objekti pojavljuju nasum ino i za iju je obradu - koju obavlja ogranien broj posluilaca, potrebna nasum ina koliina vrem ena. M oem o napraviti sim ulaciju za izraunavanje idealnog broja posluilaca. U ovom prim eru, potrebno je da se svakom klijentu banke posveti odreeno vreme, a to je broj vrem enskih jedinica koje slubenik m ora da potroi na klijenta da bi ga usluio. Koliina vrem ena za pruanje usluge razlikuje se za svakog klijenta i utvrdiem o je nasumino. Sem toga, ne znam o koliko klijenata dolazi u svakom vrem enskom intervalu, pa em o i to utvrditi nasum ino.
//: paralelno/SimulacijaSalterskogSluzbeni ka.java // Pomou redova za ekanje i vienitnog izvravanja. // {A r gs: 5} import java.uti1 .concurrent.*; imDOrt java.uti1.*; // Za objekte namenjene samo za itanje nije potrebna sinhronizacija: class Klijent { private final int vremeUsluzivanja; public KIijent(int tm) { vremeUsluzivanja = tm; }

1004

Misliti na Javi

public int dajVremeUsluzivanja() { return vremeUsluzivanja; } public String toStringO { return "[ + vremeUsluzivanja + "]";

} }
// Naui red klijenata da se prikae: class RedKlijenata extends ArrayBlockingQueue<Klijent> { public RedKlijenata(int najveciRed) { super(najveciRed);

}
public String toStringO { if(this.velicina() == 0) return "[Prazan]"; StringBuilder rezultat = new StringBuilder(); for(Klijent klijent : this) rezultat.append(kl ijent); return rezultat.toString();

} }
// Nasumino dodavanje klijenata u red: class GeneratorKlijenata implements Runnable { private RedKlijenata klijenti; private static Random slucajan = new Random(47); public GeneratorKlijenata(RedKl ijenata rp) { klijenti = rp;

}
public void run() { try { whi 1e ( IThread.interruptedO) { TimeUnit.MILLISECONDS.sleep(slucajan.nextInt(300)); klijenti,put(new K1ijent(slucajan.nextlnt(1000)));

}
} catch(InterruptedException e) { System.out.println ("GeneratorKlijenata preki nut");

}
System.out.println("GeneratorKlijenata zavrava");

class Sluzbenik implements Runnable, Comparable<Sluzbenik> { private static int brojac = 0; private final int id = brojac++; // Broj klijenata usluenih u ovoj smeni: private int usluzenihKlijenata = 0; private RedKlijenata klijenti; private boolean redKlijenataSeUsluzuje = true; public S1uzbenik(RedKlijenata rp) { klijenti = rp; } public void run() {

Poglavlje 21: Paralelno izvravanje

1005

try { while(!Thread.interrupted{)) { Klijent klijent = klijenti.take(); TimeUnit.MILLISECONDS.sleep( kli jent.dajVremeUsluzivanjaO); synchronized(this) { usluzenihKlijenata++; while(!redKlijenataSeUsluzuje) wait();

} }
} catch(InterruptedException e) { System.out.println(this + "prekinut");

}
System.out.println(this + "zavrava");

}
public synchronized void radiNestoDrugo() { usluzenihKlijenata = 0; redKlijenataSeUsluzuje = false;

}
public synchronized void usluzujRedKlijenata() { assert !redKlijenataSeUsluzuje:"ve usluujem: " + this; redKlijenataSeUsluzuje = true; notifyAl1 ();

}
public String toString() { return "Slubenik " + id + " "; } public String shortStringO { return "T" + id; } // Upotrebljava prioritetni red za ekanje: public synchronized int compareTo(Sluzbenik drugi) { return usluzenihKlijenata < drugi.usluzenihKlijenata ? -1 : (usluzenihKlijenata == drugi.usluzenihKlijenata ? 0 : 1);

} }
class Rukovodi1acSluzbenika implements Runnable { private ExecutorService exec; private RedKlijenata klijenti; private PriorityQueue<Sluzbenik> radiSluzbenika = new PriorityQueue<Sluzbenik>(); private Queue<Sluzbenik> sluzbenikaKojiRadeNestoDrugo = new LinkedList<Sluzbenik>(); private int periodPrilagodjavanja; private static Random slucajan = new Random(47); public Rukovodi1acSluzbenika(ExecutorService e, RedKlijenata klijenti, int periodPrilagodjavanja) { exec = e; this.klijenti = klijenti; this.periodPrilagodjavanja = periodPrilagodjavanja; // Poni s jednim slubenikom: Sluzbenik sluzbenik = new Sluzbenik(klijenti);

1006

Misliti na Javi

exec.execute(sluzbeni k); radiSluzbenika.add(sluzbenik);

}
public void prilagodiBrojSluzbenika() { // Ovo je zapravo upravljaki sistem. Menjanjem brojeva otkriete // nestabilne radne take upravljakog mehanizma. // Ako je red predugaak, dodaj jo jednog slubenika: if(klijenti.size() / radiSluzbenika.size() > 2 ) { // Ako su slubenici na pauzi ili rade neto drugo, // vrati jednog nazad: if(sluzbenikaKojiRadeNestoDrugo.size() > 0) { Sluzbenik sluzbenik = sluzbenikaKojiRadeNestoDrugo.remove(); sluzbenik.usluzujRedKl ijenata(); radiSluzbenika.offer(sluzbenik); return;

}
// U protivnom napravi (zaposli) novog slubenika Sluzbenik sluzbenik = new Sluzbenik(klijenti); exec.execute(sluzbenik); radiSluzbenika.add(sluzbenik); return;

}
// Ako je red dovoljno kratak, ukloni jednog slubenika: if(radiSluzbenika.size() > 1 && klijenti.size() / radiSluzbenika.size() < 2) premestiJednogSluzbeni ka(); // Ako reda uopte nema, treba nam samo jedan slubenik: if (kl ijenti .size() == 0) whi1e(radiS1uzbenika.size() > 1) premestiJednogSluzbenika();

}
// Daj slubeniku drugi posao ili ga poalji na pauzu: private void premestiJednogSluzbenikaf) { Sluzbenik sluzbenik = radiSluzbenika.pol1(); sluzbeni k .radi NestoDrugo(); sluzbeni kaKojiRadeNestoDrugo.offer(sluzbeni k);

}
public void run() { try { while(!Thread.interrupted()) { TimeUni t.MILLISECONDS.sleep(periodPri1agodjavanja); prilagodiBrojSluzbenika(); System.out.print(klijenti + " { "); for(Sluzbenik sluzbenik : radiSluzbenika) System.out.print(sluzbenik.shortString() + " "); System.out.pri ntln("}");

}
} catch(InterruptedException e) { System.out.println(this + "prekinut");

Poglavlje 21: Paralelno izvravanje

1007

System.out.println(this + "zavrava")-

}
public String toStringO { return "RukovodilacSluzbenika }

}
public class SimulacijaSalterskogSluzbenika { static final int NAJVECI_DOZVOLJENI_RED = 50; static final int PERIOD_PRILAGODJAVANJA = 1000; public static void main(String[] args) throws Exception { ExecutorService exec = Executors.newCachedThreadPool(); // Ako je red predugaak, klijenti e otii: RedKlijenata klijenti = new RedKl ijenata(NAJVECI_D0ZV0LJENI_RED); exec.execute(new GeneratorKlijenata(klijenti)); // Rukovodilac dodaje i uklanja slubenike prema potrebi: exec.execute(new Rukovodi1acSluzbenika( exec, klijenti, PERI0D_PRILAG0DJAVANJA)); if(args.1ength > 0) // Opcioni argument TimeLlni t.SECONDS.sl eep(new lnteger(args[0])); el se { System.out.println("Pritisnite 'Enter' ako elite da prekinete izvravanje programa"); System.i n .read ();

}
exec.shutdownNow();

}
} /* Ispis: (primer) [429][200][207] { TO T1 } [861][258] [140] [322] { TO T1 } [575] [342] [804] [826] [896] [984] { TO T1 T2 } [984] [810] [141] [12] [689] [992] [976] [368] [395] [354] { TO T1 T2 T3 } Slubenik 2 prekinut Slubenik 2 zavrava Slubenik 1 prekinut Slubenik 1 zavrava RukovodilacSluzbenika prekinut Rukovodi1acSluzbenika zavrava Slubenik 3 prekinut Slubenik 3 zavrava Slubenik 0 prekinut Slubenik 0 zavrava GeneratorKlijenata prekinut GeneratorKlijenata zavrava

O bjekti tipa Klijent veoma su jednostavni, poto sare sam o jedno final int polje. Poto se ti objekti nikada ne menjaju, oni su samo za itanje i ne zahtevaju sinhronizaciju niti m odifikator volatile. Sein toga, svaki zadatak Slubenik u svakom tren u tk u uklanja

1008

Misliti na Javi

sam o po jedan objekat tipa Klijent iz ulaznog reda i usluuje ga dok ne zavri, pa svakom objektu tipa Klijent u svakom tren u tk u ionako p ristu p a sam o po jedan zadatak. RedKlijenata, kako m u im e kae, ine klijenti koji ekaju da ih uslui neki objekat tipa Slubenik. To je sam o objekat tipa ArrayBlockingQueue s m etodom to S trin g ( ) koja ispisuje rezultate u eljenom obliku. O bjektu tipa RedKlijenata p ridru en je jedan GeneratorKlijenata koji u nasum inim vrem enskim intervalim a dodaje klijente u red. Slubenik uzim a klijente iz objekta tipa RedKIijenata i obrauje ih jednog po jednog, pam tei broj klijenata koje je usluio tok om odreene sm ene. Moe m u se poruiti da radiN estoD rugo( ) kada nem a dovoljno klijenata i usluzujRedKlijenata( ) kada doe m nogo klijenata. Slubenika koji e biti vraen na opsluivanje ulaznog reda bira m etoda com pareTo( ) koja poredi broj usluenih klijenata da bi p rio ritetn i red za ekanje (PriorityQueue) m ogao autom atski da stavi najm anje optereenog slubenika napred. RukovodilacSluzbenika upravlja svim aktivnostima. On prati sve slubcnike i nadgleda ta rade klijenti. Jedna od zanimljivosti ove simulacije jeste pokuaj otkrivanja optim alnog broja slubenika za dati dotok klijenata. To moete videti u metodi prilagodiBrojSluzben ik a( ) koja je upravljaki sistem za dodavanje i uklanjanje slubenika na stabilan nain. Svi upravljald sistemi m oraju da paze na stabilnost; ako na prom enu reaguju prebrzo, izgubie stabilnost, a ako reaguju presporo, sistem e prei u jedan od ekstrema (red klijenata maksimalne duine, a ne rade svi dostupni slubenici ili su zaposleni svi slubenici iako klijenata nem a). Veba 35: (8) Izm enite program SimuIacijaSalterskogSluzbenika.java tako da predstavlja Web klijente koji alju zahteve o dreenom broju servera. Treba utvrditi optereenje koje ta grupa servera moe da savlada.

Simulacija restorana
Jednostavan prim er Restoran.java prikazan u prethodnom delu poglavlja obogatiu u ovoj simulaciji dodavanjem vie kom ponenata kao to su objekti tipa Narudzba i DeoObroka; pored toga, ponovo u upotrebiti klase jelovnik iz poglavlja Nnbrojani tipovi. Predstaviu i Java SE5 klasu SynchronousQueue to je blokirajui red za ekanje nultog internog kapaciteta, pa svaki poziv m etode p u t( ) m ora da eka na svoj poziv m etode take( ) i obrnuto. Kao da nekome treba da predate neki predm et, a nema stola na koji biste ga mogli staviti - uspeete jedino u sluaju da ta osoba prui ka vama ruku da uzme predmet. U ovom prim eru, SynchronousQueue predstavlja prostor neposredno ispred restoranskog gosta, kako bi se naglasilo da u svakom trenutku moe biti poslueno sam o jedno jelo. Ostale klase i funkcije u ovom prim eru slede iz strukture program a R estoran.java ili predstavljaju prilino neposredno preslikavanje operacija pravog restorana:
//: paral el no/'restoran2/RestoranSRedovima.java // {Args: 5} package concurrency.restoran2; import enumerated.menu.*; import java.util.concurrent.*; import java.uti1.*; import static net.mindview.uti1 .Print.*;

Poglavlje 21: Paralelno izvravanje

1009

// Ovo se daje konobaru, koji to predaje kuvaru: class Narudzba { // (Objekat za prenos podataka) private static int brojac = 0; private final int id = brojac++; private final Gost gost; private final Konobar konobar; private final Hrana hrana; public Narudzba(Gost gst, Konobar knbr, Hrana h) { gost = gst; konobar = knbr; hrana = h;

}
public Hrana item() { return hrana; } public Gost dajGosta() { return gost; } public Konobar dajKonobara() { return konobar; } public String toStringO { return "Narudzba: " + id + " stavka: " + hrana + " z a : + gost + " servirao: " + konobar;

} }
// Ovo se vraa od kuvara: class DeoObroka { private final Narudzba narudzba; private final Hrana hrana; public DeoObroka(Narudzba nar, Hrana h) { narudzba = nar; hrana = h;

}
public Narudzba dajNarudzbu() { return narudzba; } public Hrana dajHranu() { return hrana; } public String toString() { return hrana.toStringO; }

}
class Gost implements Runnable { private static int brojac = 0; private final int id = brojac++; private final Konobar konobar; // U svakom trenutku moe primiti samo jedan deo obroka: private SynchronousQueue<DeoObroka> mestoZaStolom = new SynchronousQueue<DeoObroka>(); public Gost(Konobar k) { konobar = k; } public void posluzi(DeoObroka deoO) throws InterruptedException { // Blokira samo ako gost jo uvek // jede prethodni deo obroka: mestoZaStolom.put(deoO);

1010

Misliti na Javi

public void run() { for(Jelo jelo : Jelo.valuesO) { Hrana hrana = jelo.randomSelection(); try { konobar.primiNarudzbu(this, hrana); // Blokira dok se jelo ne poslui: print(this + "jede " + mestoZaStolom.takeO); } catch(InterruptedException e) { print(this + "eka na " + jelo + " prekinut"); break;

} }
print(this + "pojeo, odlazi");

}
public String toStringO { return "Gost + id + ";

class Konobar implements Runnable { private static int brojac = 0; private final int id = brojac++; private final Restoran restoran; BlockingQueue<DeoObroka> primljeneNarudzbe = new LinkedBlockingQueue<DeoObroka>(); public Konobar(Restoran rest) { restoran = rest; } public void primiNarudzbu(Gost gst, Hrana hrana) { try { // Ne bi trebalo da blokira zato to se radi o objektu tipa // LinkedBlockingQueue koji nema ogranienu veliinu: restoran.narudzbe.put(new Narudzba(gst, this, hrana)); } catch(InterruptedException e) { print(this + " primiNarudzbu prekinuta");

} }
public void run() { try { while(!Thread.interrupted()) { // Blokira dok se jelo ne pripremi DeoObroka deoObroka = primljeneNarudzbe.take(); print(this + "primio " + deoObroka + " to posluuje gostu " + deoObroka.dajNarudzbu() . d a j G o s t a O ) ; deoObroka.dajNarudzbu().dajGosta().posluzi(deoObroka);

}
} catch(InterruptedException e) { print(this + " prekinut");

Poglavlje 21: Paralelno izvravanje

1011

print(this + " nije u smeni");

}
public String toStringO { return "Konobar " + id + " ";

} }
class Kuvar implements Runnable { private static int brojac = 0; private final int id = brojac++; private final Restoran restoran; private static Random slucajan = new Random(47); public Kuvar(Restoran rest) { restoran = rest; } public void run() { try { while(!Thread.interrupted()) { // Blokira dok ne dobije narudbu: Narudzba narudzba = restoran.narudzbe.take(); Hrana narucenaStavka = narudzba.item(); // Vreme za pripremu narudzbe: TimeUnit.MILLISECONDS.sleep(slucajan.nextInt(500)); DeoObroka deoObroka = new DeoObrokafnarudzba, narucenaStavka); narudzba.dajKonobara().primljeneNarudzbe.put(deoObroka);

}
} catch(InterruptedException e) { print(this + " prekinut");

}
print(this + " nije u smeni");

}
public String toStringO { return "Kuvar " + id + " "; }

class Restoran implements Runnable { private List<Konobar> konobari = new ArrayList<Konobar>(); private List<Kuvar> kuvari = new ArrayList<Kuvar>(); private ExecutorService exec; private static Random slucajan = new Random(47); B1ocki ngQueue<Narudzba> narudzbe = new LinkedBlockingQueue<Narudzba>(); public Restoran(ExecutorService e, int nKonobara, int nKuvara) { exec = e; for(int i = 0; i < nKonobara; i++) { Konobar konobar = new Konobar(this); konobari.add(konobar); exec.execute(konobar);

}
for(int i = 0; i < nKuvara; i++) { Kuvar kuvar = new Kuvar(this); kuvari.add(kuvar);

1012

Misliti na Javi

exec.execute(kuvar);

} }
public void run() { try { while(!Thread.interrupted()) { // Doao je novi gost; dodeli rnu Konobara: Konobar knbr = konobari.get( slucajan.nextInt(konobari,size())); Gost k = new Gost(knbr); exec.execute(k); TimeUnit.MILLISECONDS.sleep(100);

}
} catch(InterruptedException e) { print("Restoran prekinut");

}
print("Restoran se zatvara");

} }
public class RestoranSRedovima { public static void main(String[] args) throws Exception { ExecutorService exec = Executors.newCachedThreadPool(); Restoran restoran = new Restoran(exec, 5, 2); exec.execute(restoran); if(args.length > 0) // Opcioni argument TimeUnit.SECONDS.sleep(new Integer(args)); el se { print("Pritisnite 'Enter' ako elite da prekinete izvravanje programa"); System.in.read();

}
exec.shutdownNow();

}
} /* Ispis: (primer) Konobar 0 primio PROLECNE_ROLNICE to posluuje gostu Gost 1 jede PROLECNE_ROLNICE Konobar 3 primio PROLECNE_ROLNICE to posluuje gostu Gost 0 jede PROLECNE_ROLNICE Konobar 0 primio PLJESKAVICU to posluuje gostu Gost Gost 1 jede PLJESKAVICU Konobar 3 primio PR0LECNE_R0LNICE to posluuje gostu Gost 2 jede PR0LECNE_R0LNICE Konobar 1 primio SUPU to posluuje gostu Gost 3 Gost 3 jede SUPU Konobar 3 primio L0VACKI_PIR to posluuje gostu Gost Gost 0 jede LOVACKI_PIR Konobar 0 primio VOCE to posluuje gostu Gost 1 Gost 1 Gost 0 1 Gost 2

*///:-

Poglavlje 21: Paralelno izvravanje

1013

Jedna od veom a vanih stvari na koje u ovom prim eru treba obratiti panju jeste upravljanje sloenou tako to su za kom unikaciju izm eu zadataka upotrebljeni redovi za ekanje. Ve sama ta tehnika uveliko pojednostavljuje postupak paralelnog program iranja tako to invertuje kontrolu: zadaci se ne obraaju jedan drugom neposredno, nego jedni drugim a alju objekte preko redova za ekanje. Zadatak koji je prim io objekat obrauje ga i tretira ga kao poruku, um esto da ga podvrgava poruci. Ukoliko se budete pridravali tog naina rada, imaete m nogo vie anse da pravite robusne paralelne sisteme. Veba 36: (10) Izm enite RestoranSRedovima.java tako da za svaki sto postoji po jedan objekat tip a Narudzbenica. Prepravite klasu narudzba u narudzbenica i dodajte klasu Sto, s vie G ostiju za stolom.

Raspodela posla
Naredni prim er je sim ulacija koja obuhvata vie koncepata iz ovog poglavlja. Zamislite robotizovanu m ontanu liniju za autom obile. Svaka Kola se proizvode u nekoliko faza, poev o d pravljenja asije, preko m ontae m otora, pogonskog sistema i tokova.
//: paralelno/ProizvodnjaKola.java // Sloen primer saradnje zadataka. import java.uti1 .concurrent import java.util.*; import static net.mindview.uti 1.Print.*; class Kola { private final int id; private boolean motor = false, pogonskiSistem = false, tockovi = false; public Kola(int idn) { id = idn; } // Prazan objekat tipa Kola: public Kola() { id = -1; } public synchronized int dajId() { return id; } public synchronized void dodajMotor() { motor = true; } public synchronized void dodajPogonskiSistem() { pogonskiSistem = true;

}
public synchronized void dodajTockove() { tockovi = true; } public synchronized String toStringO { return "Kola " + id + " [" + " motor: " + motor + " pogonskiSistem: " + pogonskiSistem + " tockovi: " + tockovi + " ]";

class RedKola extends LinkedBlockingQueue<Kola> {} class PravljenjeSasije implements Runnable { private RedKola redKola; private int brojac = 0;

1014

Misliti na Javi

public PravljenjeSasije(RedKola rk) { redKola = rk; } public void run() { try { while(!Thread.interrupted()) { TimeUni t.MILLISECONDS.sleep(500); // Napravi asiju: Kola k = new Kola(brojac++); print("Objekat tipa PravljenjeSasije napravljen " + k); // Umetni u red redKola.put(k);

}
} catch(InterruptedException e) { print("Prekinuto: PravljenjeSasije");

}
print("PravljenjeSasije iskljueno");

} }
class Monter implements Runnable { private RedKola redZaSasiju, redZaZavrsnu; private Kola kola; private CyclicBarrier barijera = new CyclicBarrier(4); private GrupaRobota grupaRobota; public Monter(RedKola rk, RedKola rzz, GrupaRobota gr) { redZaSasiju = gr; redZaZavrsnu = rzz; grupaRobota = gr;

}
public Kola kola() { return kola; } public CyclicBarrier barijera() { return barijera; } public void run() { try { while(!Thread.interrupted()) { // Blokira dok se asija ne napravi: kola = redZaSasiju.takef); // Unajmi robote da rade: grupaRobota.unajmi(RobotZaMotor.class, this); grupaRobota.unajmi(RobotZaPogonskiSistem.class, this); grupaRobota.unajmi(RobotZaTockove.class, this); barijera.await(); // Dok roboti ne zavre // Stavi kola u redZaZavrsnu za dalju obradu redZaZavrsnu.put(kola);

}
} catch(InterruptedException e) { print("Izlazim iz niti Monter pomou prekida"); } catch(BrokenBarrierException e) { // elim da dobijem obavetenje o ovome throw new RuntimeException(e);

Poglavlje 21: Paralelno izvravanje

1015

print("Monter iskljuen");

class Izvestilac implements Runnable { private RedKola redKola; public Izvestilac(RedKola gr) { redKola = gr; } public void run() { try { while(!Thread.interrupted()) { print(redKola.take());

}
} catch(InterruptedException e) { printC'Izlazim iz niti Izvestilac pomou prekida");

}
print("Izvestilac iskljuen");

abstract class Robot implements Runnable { private GrupaRobota grupa; public Robot(GrupaRobota g) { grupa = g; } protected Monter monter; public Robot dodeliMontera(Monter monter) { this.monter = monter; return this;

}
private boolean angazuj = false; public synchronized void angazuj() { angazuj = true; noti fyAl1 ();

}
// Ovaj deo metode run() razliit je za svakog robota: abstract protected void pruziUslugu(); public void run() { try { iskljuciSeO; // ekaj dok ne bude potreban whi1e(IThread.interrupted()) { pruzi Uslugu(); monter.barijera(),await(); // Sinhronizacija // Ovaj posao je zavren... i skljuci Se ();

}
} catch(InterruptedException e) { print ("Izl azim iz niti " + this + " pomou prekida"); } catch(BrokenBarrierException e) { // elim da dobijem obavetenje o ovome throw new RuntimeException(e);

1016

Misliti na Javi

print(this + " iskljuen");

}
private synchronized void iskljuciSe() throws InterruptedException { angazuj = false; monter = nul1; // Otkai od objekta Monter // Vraamo se u grupu dostupnih: grupa.release(this); while(angazuj == false) // Iskljui se wait();

}
public String toString() { return getClass().getName(); }

class RobotZaMotor extends Robot { public RobotZaMotor(GrupaRobota grupa) { super(grupa); } protected void pruziUslugu() { print(this + " instalira motor"); monter.kol a (). dodajMotor();

} }
class RobotZaPogonskiSistem extends Robot { public RobotZaPogonskiSistem(GrupaRobota grupa) { super(grupa); } protected void pruzillslugu() { print(this + " instalira PogonskiSistem"); monter.kola().dodajPogonskiSi stem();

class RobotZaTockove extends Robot { public RobotZaTockove(GrupaRobota grupa) { super(grupa); } protected void pruziUslugu() { print(this + " instalira Tokove"); monter.kolaO .dodajTockove();

} }
class GrupaRobota { // (Neujno) spreava identine stavke: private Set<Robot> grupa = new HashSet<Robot>(); public synchronized void add(Robot r) { grupa.add(r); noti fyAl1 ();

}
public synchronized void unajmi(Class<? extends Robot> tipRobota, Monter d) throws InterruptedException { for(Robot r : grupa) if (r.getClassO .equals(tipRobota)) {

Poglavlje 21: Paralelno izvravanje

1017

grupa.remove(r); r.dodeliMontera(d); r.angazuj(); // Ukljui ga da obavi posao return;

}
wait(); // Nema dostupnih unajmi(tipRobota, d); // Rekurzivno pokuaj ponovo

}
public synchronized void release(Robot r) { add(r); )

}
public class ProizvodnjaKola { public static void main(String[] args) throws Exception { RedKola redZaSasiju = new RedKola(), redZaZavrsnu = new RedKola(); ExecutorService exec = Executors.newCachedThreadPool(); GrupaRobota grupaRobota = new G r u p a R o b o t a O ; exec.execute(new RobotZaMotor(grupaRobota)); exec.execute(new RobotZaPogonskiSistem(grupaRobota)); exec.execute(new RobotZaTockove(grupaRobota)); exec.execute(new Monter( redZaSasiju, redZaZavrsnu, grupaRobota)); exec.execute(new Izvesti1ac(redZaZavrsnu)); // Pokreni sve proizvodnjom asije: exec.execute(new PravljenjeSasije(redZaSasiju)); TimeUnit.SEC0NDS.sleep(7); exec.shutdownNow();

}
} /* (Pokrenite da biste videli rezultat) *///:-

Kola se prenose s m esta na mesto pom ou objekta tipa RedKola, to je tip reda LinkedBlockingQueue. PravljenjeSasije pravi kostur Kola i sm eta ga u RedKola. Monter skida Kola i/. RedaKola i unajm ljuje Robote da na njim a rade. Barijera tipa CyclicBarrier om oguuje Monteru da eka dok svi unajm ljeni Roboti ne b u d u gotovi, a potom stavlja Kola u izlazni RedKoIa koji ih prenosi za sledeu operaciju. Potroa zavrnog objekta tipa RedKola jeste Izvestilac koji sam o ispisuje sastav Kola da bi pokazao da su svi zadaci pravilno izvreni. Robotima se upravlja u grupi, i kada neto treba da se uradi, odgovarajui Robot biva unajmljen iz grupe. Nakon dovretka posla, Robot se vraa u grupu. U m etodi m a in ( ) prave se svi potrebni objekti i inicijalizuju svi zadaci; poslednje se pokree PravljenjeSasije da bi se pokrenuo proces. (M eutim , ponaanje reda LinkedBIockingQueue je takvo da sam m ogao njega prvog da pokrenem .) O bratite panju na to da ovaj pm gram sledi sve sm ernice u vezi sa ivotuim ciklusom objekata i zadataka koje su spom enute u ovom poglavlju, pa je postupak gaenja bezbedan. Uoili ste da su sve m etode Kola sinhronizovane. Ispostavlja se da je to u ovom primeru redundantno, poto su Kola unutar proizvodnje uvek u nekom od redova za ekanje i na svakim kolima u svakom trenutku moe raditi sam o jedan zadatak. U sutini, redovi za ekanje nam eu serijsko pristupanje objektima tipa Kola. Aii upravo to i jeste zamka - mogli

1018

Misliti na Javi

biste rei: Hajde da optimizujemo perform anse tako to klasu Kola neem o sinhronizovati, poto izgleda da to ovde nije neophodno". Ali kasnije, kada ovaj sistem bude povezan s nekim drugim koji zahteva da Kola bud u sinhronizovana, sve e se raspasti. Brian Goetz komentarie: M nogo je lake reiKola m ogu biti upotrebljena iz vie niti, pa hajde da ih napravim o oigledno bezbednim za vienitno izvravanje". Evo kako ja to posm atram : na strm im m estim a u parkovim a postoje zatitne ograde i natpisi Z abranjeno naslanjanje na zatitnu ogradu. Naravno, prava svrha ovog pravila nije da sprei naslanjanje na ogradu, nego da sprei padanje niza strm inu. Ali je m nogo lake pridravati se pravila Zabranjeno naslanjanje na ogradu nego pravila Zabranjeno padanje niza strminu". Veba 37: (2) Izmenite program ProizvodnjaKola.java dodavanjem jo jedne faze u postupak pravljenja kola, u kojoj se dodaju izduvni sistem, karoserija i branici. Kao i u drugoj fazi, pretpostavite da te procese roboti m ogu da obavljaju istovremeno. Veba 38: (3) Koristei pristup iz program a ProizvodnjaKola.java, m odelujte priu o gradnji kue koja je data u ovom poglavlju.

Optimizacija performansi
Z natan broj klasa u biblioteci Jave SE5 java.util.concurrent postoji radi poboljanja perform ansi. Tokom prelistavanja biblioteke concurrent, teko ete razluiti klase nam enjene za redovnu upotrebu (kao to su redovi BlockingQueue) od klasa koje su sam o za poboljavanje perform ansi. U ovom odeljku razm otriem o neka od tih pitanja i klasa za optim izaciju performansi.

Poreenje tehnologija uzajamno iskljuivih brava (mutexaj


Poto Java sada obuhvata i staru rezervisanu re synchronized i nove klase Lock i Atomic Jave SE5, zanimljivo je uporediti te razliite pristupe da bism o bolje shvatili vrednost svakog od njih i saznali gde ih treba upotrebljavati. Bilo bi previe naivno napraviti po jedan jednostavan test za svaki od tih pristupa, kao u sledeem prim eru:
//: paralelno/JednostavnoMikroporeenjePerforinansi -java // Opasnosti od mikroporeenja performansi. import java.util.concurrent.locks.*; abstract class ImaMetoduPovecajZaJedan { protected long brojac = 0; public abstract void povecajZaJedan();

}
class TestSinhronizaci j e extends ImaMetoduPovecajZaJedan { public synchronized void povecajZaJedan() { ++brojac; }

}
class TestZakljucavanja extends ImaMetoduPovecajZaJedan { private Lock brava = new ReentrantLock();

Poglavlje 21: Paralelno izvravanje

1019

public void povecajZaJedan() { brava.lock(); try { ++brojac; } finally { brava.unlock();

} } }
public class JednostavnoMikroporeenjePerformansi { static long test(ImaMetoduPovecajZaJedan pzj) { long start = System.nanoTime(); for(long i = 0; i < 10000000L; i++) pzj.povecajZaJedanO; return System.nanoTime() - start;

}
public static void main(String[] args) { long vremeZaSynch = test(new TestSinhronizacije()); long vremeZaLock = test(new TestZakljucavanja()); System.out.printf("synchronized: %l$10d\n", vremeZaSynch); System.out.printf("Lock: %l$10d\n", vremeZaLock); System.out.printf("Lock/synchronized = %l$.3f", (double)vremeZaLock/(double)vremeZaSynch);

}
} /* Ispis: (75% podudaranja) synchronized: 244919117 Lock: 939098964 Lock/synchronized = 3.834

* ///:Iz rezultata vidite da je korienje poziva m etode sinhronizovane pom ou rezervisane rei sy n chron ized naizgled mnogo bre od upotrebe objekta tipa Lock. ta se tu desilo? Ovaj prim er pokazuje opasnosti od tzv. m ikroporeenja perform ansi.23 Taj pojam po pravilu upuuje na izolovano m erenje perform ansi, izvan konteksta. N aravno, ni tvrdnje kao to je Klasa Lock je m nogo bra od rezervisane rei synchronized ne m oete izricati bez p rethodnog merenja. Ali, kada piete takva ispitivanja, m orate biti svesni toga ta se dogaa tokom prevodenja i u vreme izvravanja. G ornji prim er je problem atian iz nekoliko razloga. Prvo i osnovno, stvarnu razliku u p erform ansam a videemo samo u sluaju da za uzajam no iskljuive brave (mutexe) postoji takmienje , dakle vie zadataka m ora pokuavati da pristupi delovim a koda zatienim mutexima. U gornjem prim eru, svaki m utex ispitujem o pom ou sam o jedne niti u m etodi m a in ( ), izolovano. U rugo, m oda prevodilac obavlja posebne optim izacije kada ugleda rezervisanu re synchronized, a moda prim eti da program im a sam o jednu nit. Prevodilac bi ak m ogao
Brian G oetz m i je fflnogo pom ogao da to shvatim . Vie info rm acija o m eren ju p erfo rm an si proitajte u njegovom lanku na aresi w w w -t28.ibm .com /devdoperw orks/librarY /j-jtpl2214.

1020

Misliti na Javi

da shvati kako se brojac poveava fiksan broj p u ta pa da unapred izrauna rezultat. Ima razliitih prevodilaca i sistema za izvravanje, pa je teko rei ta e se tano dogoditi, ali m oram o spreiti m ogunost da prevodilac predvidi ishod. D a bi testiranje bilo validno, program m ora biti sloeniji. Prvo m oram o napraviti vie zadataka, i to ne sam o onih koji m enjaju interne vrednosti, ve i onih koji te vrednosti itaju (inae b i optim izator m ogao prepoznati da se te vrednosti nikada ne koriste). Pored toga, izraunavanje m ora biti toliko sloeno i nepredvidljivo da prevodilac nem a anse da obavi agresivnu optim izaju. To em o postii prethodnim uitavanjem velikog niza pseudosluajnih celih brojeva (p reth od no uitavanje sm anjuje uticaj poziva m etode R andom .nextInt( ) na glavne petlje) i sabiranjem tih brojeva:
//: para'le'lno/PoredjenjeSinhronizacija.java // Poreenje performansi pri upotrebi eksplicitnih objekata tipa Lock // i Atomic i performansi pri korienju rezervisane rei synchronized. import java.util.concurrent.*; import java.util.concurrent.atomic.*; import java.util.concurrent.locks.*; import java.util.*; import static net.mindvievv.uti 1 .Print .*; abstract class Akumulator { public static long ciklusa = 50000L; // Broj Modifikatora i italaca tokom svakog testiranja: private static final int N = 4; public static ExecutorService exec = Executors.newFixedThreadPool (N*2); private static Cycl icBarrier barijera = new CyclicBarrier(N*2 + 1); protected volatile int indeks = 0; protected volatile long broj = 0; protected long trajanje = 0; protected String id = greka"; protected final static int VELICINA = 100000; protected static int[] unapredUcitani = new int[VELICINA]; static { // Uitaj niz sluajnih brojeva: Random slucajan = new Random(47); for(int i = 0; i < VELICINA; i++) unapredUcitani[i] = slucajan.nextInt();

}
public abstract void akumuliraj(); public abstract long citaj(); private class Mo difikator implements Runnable { public void run() { for(long i = 0; i < ciklusa; i++) akumuli raj(); try { bari jera.await(); ) catch(Exception e) {

Poglavlje 2 1: Paralelno izvravanje

1021

throw new RuntimeException(e);

} } }
private class Citalac implements Runnable { private volatile long broj; public void run() { for(long i = 0; i < ciklusa; 1++) broj = citaj(); try { bari jera.await(); } catch(Exception e) { throw new RuntimeException(e);

} } }
public void merenjeVremena() { long start = System.nanoTime(); forfint i = 0; i < N; i++) { exec.execute(new ModifikatorO); exec.execute(new CitalacO);

}
try { bari jera.await(); } catch(Exception e) { throw new RuntimeException(e);

}
trajanje = System.nanoTime() - start; printf("%-13s: %13d\n", id, trajanje);

}
public static void izvesti(Akumulator akul, Akumulator aku2) { printf("%-22s: %.2f\n", akul.id + + aku2.id, (double)akul.trajanje/(double)aku2.trajanje);

class OsnZaPor extends Akumulator { { id = "OsnZaPor"; } public void akumulirajO { broj += unapredllcitani [indeks++]; if (indeks >= VELICINA) indeks = 0;

}
public long citaj() { return broj; }

}
class TestRrSynchronized extends Akumulator { { id = "synchronized"; } public synchronized void akumulirajO {

1022

Misliti na Javi

broj += unapredllcitani [ i nd ek s+ +]; if(indeks >= VELICINA) indeks = 0; { } public synchronized long citaj() return broj; } } class TestKlaseLock extends Ak um ul at or { { id = "Lock"; } private Lock brava = new R e e n t r a n t L o c k ( ) ; public void akuitiul i raj () { brava.lockO; try { broj += u n ap re dU ci ta ni [i nd eks ++ ]; if(indeks >= VELICINA) indeks = 0; } finally { b r a v a. un lo ck (); } } public long citaj() br av a. lo ck (); try { return broj; } finally { brava.unlockO ; } } {

class TestKlaseAtomic extends A k um ul at or { { id = "Atomic"; } private At om iclnteger indeks = new A t o m i c I n t e g e r ( O ) ; private AtomicLong broj = new A t o m i c L o n g (0); public void a k u m u l i r a j () { // P a z i ! U svakom trenutku sme da radi samo jedan // objekat tipa Atomic. Ali ipak emo stei // neki uvid u performanse: int i = i n d e k s . g et An dI nc re men t( ); b r o j . g e t An dA dd (u na pre dU ci ta ni [i ]); i f(++i >= VELICINA) i n de ks .s et (O ); } public long citaj() { return broj.get(); }

public class Po redjenjeSinhronizacija { static OsnZaPor osno va Za Po re dj en je = new OsnZaPor(); static Te st RrSynchronized synch = new Te st Rr S y n c h r o n i z e d ( ) ; static TestKlaseLock brava = new T e s t K l a s e L o c k ( ) ;

Poglav[je 2 1: Paralelno izvravanje

1023

static TestKlaseAtomic atomic = new T e s t Kl as eA to mi c( ); static void test() printf("%-12s { "Ciklusa", A k u m ul at or .c ik lu sa );

: %13d\n",

osnovaZaPoredjenje.merenjeVremenaO; s y n c h . me re nj eV re me na( ); brava.merenjeVremenaO; atomic.merenjeVremenaO; Akumulator.izvesti(synch, o s n o va Za Po re dj en je ); Akumulator.izvesti(brava, os no va Za Po re dj en je ); Akumulator.izvesti(atomic, o s n o va Za Po re dj en je ); Ak umulator.izvesti(synch, brava); Ak umulator.izvesti(synch, a t o m i c ) ; Ak umulator.izvesti(brava, a t o m i c ) ; } public static void main(String[] args) { broj iteracija

int iteracija = 5; // Podrazumevani broj if(args.length > 0) // Opciono promeni iteracija = new Inte ge r( ar gs ); // Prvi put popunjava grupu niti: pr in t( "Z ag re va nj e" ); osnovaZaPoredjenje.merenjeVremenaO ; // Sada ovo poetno testiranje ne obuhvata trokove // prvog pokretanja niti. // Napravi vie mernih taaka: for(int i = 0; i < iteracija; i++) test (); Akumulator.ciklusa *= 2; } A k u m u l a t o r . e x e c .shutdown (); } /* Ispis: Zagrevanje OsnZ aP or Ciklusa OsnZ aP or synchronized Lock Atomi c Lock/OsnZaPor Atomi c/OsnZaPor synchroni zed/Lock synchronized/Atomic Lock/Atomi c Ciklusa OsnZaPor synchronized : : : : : : : : : 34237033 50000 20966632 24326555 53669950 30552487 1.16 2.56 1.46 0.45 0.79 1.76 100000 41512818 43843003 (primer) {

synchroni zed/OsnZaPc r

1024

Misliti na Javi

Lock Atomi c Lock/OsnZaPor Atomic/OsnZaPor synchronized/Lock synchronized/Atom 'c Lock/Atomic Ciklusa OsnZaPor synchronized Lock Atomic Lock/OsnZaPor Atomic/OsnZaPor synchronized/Lock synchroni zed/Atomi c Lock/Atomi c Ci klusa OsnZaPor synchronized Lock Atomi c Lock/OsnZaPor Atomi c/OsnZaPor synchroni zed/Lock synchroni zed/Atomi c Lock/Atomic Ci klusa OsnZaPor synchroni zed Lock Atomi c Lock/OsnZaPor Atomi c/OsnZaPor synchronized/Lock synchronized/Atomic L o c k /Atomic Ci klusa OsnZaPor synchroni zed Lock Atomi c

87430386 51892350 : 2.11 : 1.25 : 0.50 : 0.84 : 1.68 200000 80176670 5455046661 177686829 101789194 68.04 2.22 1.27 30.70 53.59 1.75 400000 160383513 780052493 362187652 202030984 4.86 2.26 1.26 2.15 3.86 1.79 800000 322064955 336155014 704615531 393231542 2.19 1.22 0.47 : 0.85 1.79 1600000 650004120 52235762925 1419602771 796950171

synchroni zed/OsnZaPor : 1.06

synchroni zed/OsnZaPor

synchronized/OsnZaPor

synchronized/OsnZaPor : 1.04

Poglavlje 2 1: Paralelno izvravanje

1025

sy nchronized/OsnZaPor : 80.36 Lock/OsnZaPor A tomic/OsnZaPor synchronized/Lock synchronized/Atomic Lock/Atomic Ciklusa OsnZaPor synchronized Lock Atomic Lock/OsnZaPor Atomic/OsnZaPor synchronized/Lock synchronized/Atomic Lock/Atomic *///:: : : : : : 2.18 : 1.23 : 36.80 : 65.54 : 1.78 3200000 1285664519 96336767661 2846988654 1590545726 : 2.21 : 1.24 : 33.84 : 60.57 : 1.79

synchronized/OsnZaPor : 74.93

U ovom programu upotrebljen je projektni obrazac Templatc M ethoif 4 da bi sav zajedniki kod bio u osnovnoj klasi, izolovan od promenljivog koda u realizacijama metoda ak u m u liraj( ) i c ita j( ) u izvedenim klasama. U izvedenim klasama TestRrSynchronized, TestKIaseLock i TestKlaseAtomic vidite kako metode ak um uliraj( ) i c ita j( ) izraavaju razliite naine realizacije uzajamnog iskljuivanja. U prethodnom programu, zadaci se izvravaju pom ou objekta tipa FbcedThreadPool u pokuaju da se sve niti naprave na poetku i kako bi se spreili eventualni dodatni trokovi tokom testiranja. Za svaki sluaj, poetno testiranje se ponavlja i prvi rezultati odbacuju, zato to obuhvataju i poetno pravljenje niti. CyclicBarrier je potrebna zato to obezbeuje da se svi zadaci zavre pre nego to svako testiranje proglasimo zavrenim. Oredba static je upotrebljena za prethodno uitavanje niza sluajnih brojeva, pre poetka testiranja. Na taj nain se tokom testiranja ne vide reijski trokovi generisanja sluajnih brojeva. Metoda ak u m u liraj( ) nakon svakog poziva pomera se na sledee mesto u nizu unapredUcitani (kada doe do kraja, vraa se na njegov poetak) i vrednosti broj dodaje jo jedan sluajno generisan broj. Vie zadataka Modifikator i Citalac nadmee se za dobijanje objekta Akumulator. O bratite panju na to da sam u TestuKlaseAtomic napomenuo kako je situacija previe sloena da bismo upotrebili objekte tipa Atomic - u sutini, ukoliko ima vie Atomic obiekata, morate da odustanete i koristite konvencionalnije uzajamno iskljuive brave (m utexe). (U dokumentaciji JDK izriito pieda je korienje objekata tipa Atomic dobro samo kada je kritino auriranje objekta svedeno na jednu promenljivu.) Meutim, test sam ipak ostavio da biste stekli uvid u poboljanje performansi koje prouzrokuju Atomic objekti.
Videti Ihinking in l'attcnis na aresi www.MindView.net.

1026

Misliti na Javi

U metodi m a in ( ), testiranje se ponavlja i moete odluiti da zatraite vie od pet (podrazumevanih) ponavljanja. U svakom ponavljanju, broj ciklusa testiranja se udvostruuje, pa moete videti kako se razne uzajamno iskljuive brave (m utexi) ponaaju kada se izvravaju sve due i due. Kao to vidite iz izlaza, rezultati su prilino iznenauju i. U prve etiri iteracije izgleda da je rezervisana re synchronized efikasnija od klasa Lock i Atomic. Ali iznenada neka granica biva prekoraena pa deluje da synchronized postaje veoma neefikasna, dok Lock i Atom ic kao da priblino odravaju svoju srazmeru prema testu OsnZaPor (osnova za poreenje) i stoga postaju mnogo efikasniji od synchronized. Im ajte u vidu da ovaj program daje tek naznaku razlike izmeu raznih realizacija uzajam no iskljuivih brava i da gornji izlaz pokazuje te razlike samo na m om konkretnom raunaru pod m ojim konkretnim okolnostima. Kao to ete videti kada budete eksperimentisali s ponaanjem, ono se znatno menja u zavisnosti od broja niti i trajanja izvravanja programa. Neke optimizacije vruih taaka pozivaju se tek nekoliko minuta nakon poetka izvravanja programa, a u sluaju serverskih programa, nakon nekoliko sati. Uza sve to, prilino je jasno da je klasa Lock najee znatno efikasnija od rezervisane rei synchronized, a izgleda i da se reijski trokovi upotrebe rezervisane rei synchronized znatno meniaju, dok oni klase Lock ostaju relativno ujednaeni. Da li to znai da rezervisanu re synchronize uopte ne treba upotrebljavati? U obzir morate uzeti dva inioca: prvo, u primeru PoredjenjeSinhronizacija.java tela mutex metoda su izuzetno mala. Uopte uzev, to je dobro - uzajamno iskljuite samo one delove programa koje apsolutno morate. Meutim, uzajamno iskljueni delovi u praksi mogu biti vei nego u gornjem primeru, te e i procentualni deo vremena koji se provodi u telu metoda verovatno biti znatno vei nego reijski trokovi od ulaska i izlaska iz uzajamno iskljuive brave, a to bi moglo da poniti eventualno poboljanje koje prouzrokuje ubrzanje uzajamno iskljuive brave. Naravno, istinu ete saznati tek kada isprobate razliite pristupe i vidite njihov uticaj, a to bi trebalo da radite tek prilikom optimizacije performansi. Drugo, ko proita kod u ovom poglavlju videe da rezervisana re synchronized proizvodi mnogo itljiviji kod od idioma zakljuaj-try/finally-otkljuaj, za koji su potrebne brave tipa Lock, i zato sam u ovom poglavlju uglavnom koristio synchronized. Na vie mesta u ovoj knjizi rekao sam da se kod mnogo ee ita nego pie u programiranju je vanije da kod razumeju drugi ljudi nego raunar - te je itljivost koda kljuna. Zato ima smisla poeti s rezervisanom rei synchronized i prei na objekte tipa Lock tek prilikom optim izacije performansi. Najzad, lepo je kada moete da upotrebite klase Atomic u paralelnom programu, ali morate voditi rauna o tome da su, kao to smo videli u programu PoreenjeSinhronizacija.java, Atomic objekti korisni samo u veoma jednostavnim sluajevima, po pravilu samo kada imate jedan Atom ic objekat koji menjate i kada je taj objekat nezavisan od svih
o s ta lih o b je k a ta . B e z b e d n ij e je k r e n u ti s tr a d i c i o n a l n ij im u z a ja m n o is k lju iv im bra\ 'ain a

i pokuati prelazak na Atoinic objekte kasnije, ukoliko to budete morali da uinite zbog performansi.

Poglavlje 2 1: Paralelno izvravanje

1027

Kontejneri bez zakljuavanja


U poglavlju uvanje objekata naglasio sam da su kontejneri osnovna alatka za celo program iranje, a to vai i za paralelno programiranje. Zato su prvi Java kontejneri (kao Vector i Hashtable) imali mnogo sinhronizovanih metoda koje su prouzrokovale neprihvatljive reijske trokove kada nisu bile koriene u vienitnim programima. U Javi 1.2 nova kontejnerska biblioteka bila je desinhronizovana, a klasa Collections je dobila razne statike ,,sinhronizovane dekorativne metode za sinhronizaciju razliitih tipova kontejnera. Iako je to bilo poboljanje jer je programer mogao da bira da li da koristi sinhronizaciju kontejnera, reijski trokovi su i dalje zavisili od zakljuavanja pom ou rezervisane rei synchronized. U Javu SE5 dodati su novi kontejneri ba da bi se poboljale performanse bezbednog vienitnog rada, a za to su upotrebljene pametne tehnike kojim a se izbegava zakljuavanje. Opta strategija u pozadini tih kontejnera bez zakljuavanja jeste sledea: modifikacija kontejnera je dozvoljena istovremeno kada i itanje njihovog sadraja, ukoliko itaoci mogu da vide samo rezultate zavrenih modifikacija. Modifikacija se obavlja na zasebnoj kopiji odreenog dela strukture podataka (katkada i cele strukture) i ta kopija je nevidljiva tokom postupka modifikacije. Modifikovana struktura se atomski zamenjuje glavnom strukturom podataka tek kada je modifikacija zavrena i nakon toga itaoci mogu da je vide. U listi CopyOnWriteArrayList, svako upisivanje prouzrokuje pravljenje kopije celog pripadnog niza. Originalni niz se ne dira, pa se tokom modifikacije kopiranog niza sva itanja mogu obaviti bezbedno. Kada se modifikacija zavri, atomska operacija e zameniti stari niz novim, pa e nova itanja videti nove informacije. Jedna od prednosti klase CopyOnW riteArrayList jeste to to ne baca izuzetak ConcurrentM odificationException kada tom listom istovremeno prolazi i modifikuje je vie iteratora, pa ne morate da piete specijalni kod za zatitu od takvih izuzetaka, kao to ste morali pre. Klasa CopyOnWriteArraySet upotrebljava CopyOnW riteArrayList da bi svoje ponaanje postigla bez zakljuavanja. Klase ConcurrentHashM ap i ConcurrentLinkedQueue koriste sline tehnike da bi om oguile paralelno itanje i upisivanje, ali kopiraju i modifikuju samo delove kontejnera, a ne ceo kontejner. Meutim, itaoci ne vide modifikacije pre nego to budu zavrene. ConcurrentHashM ap ne baca izuzetke ConcurrentM odificationException.

O performansama
Ukoliko iz kontejnera bez zakljuavanja uglavnom itate, bie to mnogo bre nego da itate iz njegove sinhronizovane verzije, zato to izbegavate reijske trokove od zakljuavanja i otkljuavanja. To vai i za mali broj upisivanja u kontejner bez zakljuavanja, ali bi bilo zanimljivo stei neku predstavu koliko je to ,,malo. U ovom odeljku daemo grubu
slik u o r a z lik a m a u p e r io r m a n s a m a tili k o n te jn e r a p o d r a z li itim u s lo v im a .

1028

Misliti na Javi

Poeu od generike strukture za testiranje svih tipova kontejnera, ukljuujui tu i Mape. Generiki parametar C predstavlja tip kontejnera:
//: paralelno/Tester.java // Osnovna struktura za te stiranje performansi paralelnih kontejnera. import java.util.concurrent.*; import net.mindview.util.*; public abstract class Te ster<C> { static int iteracijaTestiranja = 10; static int ciklusaTestiranja = 1000; static int velicinaKontejnera = 1000; abstract C i n i c i j a l iz at or Ko nt ejn er a( ); abstract void po kr en iC it a o c e I U p i s i v a c e ( ) ; C kontejnerZaTestiranje; String testld; int nCitalaca; int nUpisivaca; volatile long procitajRezultat = 0; volatile long vremeCit = 0; volatile long vremeUpis = 0; CountDownLatch krajnjaBrava; static ExecutorService exec = Ex ec ut or s. ne wC ac he dTh re ad Po ol(); Integer[] upisiPodatke; Tester(String testld, int nCitalaca, int nUpisivaca) this.testld = testld + " + nCitalaca {

+ " " + nUpisivaca + "p";

this.nCitalaca = nCitalaca; this.nUpisivaca = nUpisivaca; upisiPodatke = Generated.array(Integer.class, new R a n d o m Ge ne ra to r. In teg er (), veli ci na Ko nt ej ne ra ); for(int i = 0; i < iteracijaTestiranja; izvr si Te st ir an je (); vremeCit = 0; vremeUpis = 0; } } void izvrsiTestiranje() { i++) {

krajnjaBrava = new Co un tD ow nL at ch (n Ci tal ac a + nUpisivaca); ko nt ej ne rZ aT es tiranje = inicija li za to r K o n t e j n e r a ( ) ; pokreniCitaocelUpisivaceO; try { k r a j nj aB ra va.awai t (); } catch(InterruptedException izz) { S y s t e m . o u t .pr i n t l n ( krajnjaBrava p r e k i n u t a " ) ;

Poglavlje 2 1: Paralelno izvravanje

1029

System.out.printf("%-27s %14d %14d\n", testld, vremeCit, vremeUpis); if(vremeCit != 0 && vremeUpis != 0) System.out.printf("%-27s %14d\n", "vremeCit + vremeUpis =", vremeCit + vremeUpis); } abstract class ZadatakTestiranja implements Runnable { abstract void t e s t ( ) ; abstract void upisiRezultate(); long trajanje; public void run() test(); trajanje = System.nanoTime() synchronized(Tester.this) up is i R e z u l t a t e ( ) ; } k r a j n j a B r a va .c ou nt Dow n( ); } } public static void in ic M M a i n ( S t r i n g [] args) if(args.length > 0) iteracijaTestiranja = new In te ge r( ar gs[0]); if(args.length > 1) ciklusaTestiranja = new In te ge r( ar gs[1]); if(args.length > 2) velicinaKontejnera = new In te ge r( ar gs[2 ]); System.out.printf("%-27s %14s %14s\n", "Tip", "Vreme itanja", } "Vreme upis."); { { - vremePocetka; { long vremePocetka = S y st em .n an oT im e( );

} III-Apstraktna metoda inici;aIizatorK ontejnera( ) vraa inicijalizovan kontejner koji treba testirati i smeta ga u polje kontejnerZaTestiranje. Druga apstraktna metoda, pokreniCitaoceIU pisivace( ), pokree zadatke itanja i upisivanja koji e itati i modifikovati testirani kontejner. Razni testovi se obavljaju s razliitini brojem italaca i upisivaa da bi se video uticaj takm ienja za bravu (za sinhronizovane kontejnere) i upisivanja (za kontejnere bez zakljuavanja). Konstruktor dobija razne inform acije o testiranju (trebalo bi da su vam identifikatori argumenata jasni sami po sebi), zatim poziva metodu izvrsiTestiranje( ) iteracijaTestiranja broj puta. Metoda izvrsiT estiranje( ) pravi objekat tipa CountDownLatch (da bi test mogao da zna kada su se svi zaaci zavrili), inijalizuje kontejner, poziva metodu pokreniCitaoceIU pisivace( ) i zatim eka d o k se svi o n i n e z a v r e . Osnovu klasa Citalac ili Upisivac ini ZadatakTestiranja koji meri trajanje svoje apstraktne metode te s t ( ) i zatim unutar sinhronizovanog bloka poziva metodu upisiR ezultate( ) da bi sauvao rezultate.

1030

Misliti na Javi

Prethodnu strukturu (u kojoj ste prepoznali projektni obrazac Template Metho) koristite tako to iz klase Tester izvedete konkretni tip kontejnera koji elite da testirate i napravite odgovarajue klase Citalac i Upisivac:
//: paralelno/PoredjenjaListi .java // {Args: 1 10 10} (Brza verifikaciona provera tokom builda) lista bezbednih u vienitnom radu. // Grubo poreenje performansi import java.util.concurrent.*; import java.util import net.mindview.util.*; abstract class TestiranjeLista extends T e s t e r < L i s t < I n t e g e r { TestiranjeLista(String testld, int nCitalaca, int nUpisivaca) super(testld, nCitalaca, nUpisivaca); } class Citalac extends ZadatakTestiranja { long rezultat = 0; void test() { i++) indeks++) for(long i = 0; i < ciklusaTestiranja; {

for(int indeks = 0; indeks < v e l icinaKontejnera; rezultat += k o n t e j ne rZ aT es ti ra nje .g et(i n de ks ); } void upisiRezultate() {

procitajRezultat += rezultat; vremeCit += trajanje; } } class Upisivac extends ZadatakTestiranja { void test() { i++) indeks++) for(long i = 0; i < ciklusaTestiranja;

for(int indeks = 0; indeks < velicinaKontejnera; } void upisiRezultate() } } void po k r e n i C i t a o c e l U p i s i v a c e O exec.execute(new C i t a l a c O ) ; for(int i = 0; i < nUpisivaca; i++) exec.execute(new U p i s i v a c O ) ; } } TestSi nhroni zovanogObjektaTi paArrayLi st {

kontejnerZaTestir a n j e . s e t (indeks, upisiP od at ke [i nd ek s]); {

vremeUpis += trajanje;

for(int i = 0; i < nCitalaca; i++)

class Te st SinhronizovanogObjektaTipaArrayList extends Te st ir an je Lista { List<Integer> inicij a l i zatorKontejnera() return C o l1e c t i o n s .synchroni zedLi s t ( new ArrayList<Integer>( new CountinglntegerLi st (v el ic in aK on te jn era )) ); {

Poglavlje 2 1: Paralelno izvravanje

1031

Te st Si nhronizovanogObjektaTipaArrayList(int nCitalaca, int nUpisivaca) { super("Sinhro. ArrayList", nCitalaca, nUpisivaca); }

class TestKlaseCopyOnWriteArrayList extends TestiranjeLista { List<Integer> inicijalizatorKontejnera() { return new CopyOnWriteArrayList<Integer>( new CountingInte ge rL is t(v el ic in aK on te jn er a) ); } TestKlaseCopyOnWriteArrayList(int nCitalaca, int nUpisivaca) super("CopyOnWriteArrayList", nCitalaca, nUpisivaca); } } public class PoredjenjaLista { public static void main(String[] Tester.i ni cMMai n ( a r g s ) ; new TestSinhronizovanog0bjektaTipaArrayList(10. args) {

0 );

new TestSi nhroni zovanogObjektaTi paArrayLi s t (9, l); new TestSinhronizovanogObjektaTipaArrayList(5, 5); new TestKlaseCopy0nWriteArrayList(10, 0); new T e s t K l a s eC op yO nW ri teA rr ay Li st(9, 1); new TestKlaseCopyOnWriteArrayList(5, T e s t er .e xe c. sh ut do wn( ); } /* Ispis: Tip Sinhro. ArrayList 10c Ou Sinhro. ArrayList 9c lu vremeCit + vremeUpis = Sinhro. ArrayList 5c 5u vremeCit + vremeUpis = CopyOnWriteArrayList 10c Ou CopyOnWriteArrayList 9c lu vremeCit + vremeUpis = CopyOnWriteArrayList 5c 5u vremeCit + vremeUpis = */// =(primer) Vreme itanja 232158294700 198947618203 223866231602 117367305062 249543918570 758386889 741305671 877450908 212763075 68180227375 67967464300 132176613508 Vreme upis. 5);

0
24918613399

0
136145237

U programu TestiranjeLista, klase Citalac i Upisivac obavljaju odreene radnje u listi List<Integer>. U metodi Citalac.upisiR ezultate( ) skladiti se trajanje ali i rezultat, k a k o bi se spreilo da optim izacija izbaci izraunavanja. Zatim se definie metoda p o kreniC itao celUpisivace( ) k o ja p r a v i i iz v r a v a o d r e d e n e Citaoce i Upisivace. Nakon pravljenja klase TestiranjeLista, iz nje moramo da izvedemo novu klasu kako bismo spreili inicijalizatorK ontejnera( ) da napravi i inicijalizuje konkretne kontejnere za testiranje.

1032

Misliti na Javi

U metodi m a in ( ) vidite varijacije testiranja s razliitim brojevima italaca i upisivaa. Zbog poziva Tester.inicM M ain(args), promenljive testiranja moete prom eniti tako to ete zadati argumente na kom andnoj liniji. Svako testiranje se podrazumevano obavlja 10 puta; to stabilizuje izlazne rezultate, koji se mogu promeniti zbog delatnosti JV M -a kao to su optimizacija vruih taaka i sakupljanje smea.25 Prikazani prim er izlaznih rezultata izmenio sam tako da se vidi samo poslednja iteracija svakog testiranja. Iz izlaznih rezultata vidite da sinhronizovana ArrayList ima priblino iste performanse bez obzira na broj italaca i upisivaa - itaoci se nadmeu za brave sa drugim itaocima, isto kao upisivai. Meutim, CopyOnW riteArrayList je mnogo bri kada nema upisivaa i jo uvek znatno bri s pet upisivaa. Izgleda kao da CopyOnW riteArrayList moete bezbrino da koristite koliko god hoete; trokovi od upisivanja u listu kao da neko vreme ostaju manji nego trokovi od sinhronizacije ceie liste. Naravno, oba pristupa m orate isprobati u konkretnoj aplikaciji da biste pouzdano utvrili koji je bolji. Podseam da ovo ni priblino nije dobro poreenje performansi za dobijanje apsolutnih brojeva. Vi ete gotovo sigurno dobiti drugaije brojeve. Cilj mi je bio samo da steknete uvid u relativna ponaanja te dve vrste kontejnera. Poto skup CopyOnW riteArraySet upotrebljava listu CopyOnW riteArrayList, njegovo ponaanje e biti slino i ovde ga ne moramo zasebno ispitivati.

Poreenje realizacija Mapa


Istu strukturu moemo da upotrebimo za dobijanje grube slike performansi sinhronizovane klase HashMap i klase ConcurrentHashM ap:
//: paralel no /P or ed je nj aM ap a.java // {Args: 1 10 10} (Brza verifikaciona provera tokom builda) // Grubo poreenje performansi mapa bezbednih u vienitnom radu. import java.util.*;import ja v a . u t i 1 .concurrent import net.mindview.util.*; abstract class TestiranjeMapa extends T e s t e r < M a p < I n t e g e r , I n t e g e r { TestiranjeMapa(String testld, int nCitalaca, int nUpisivaca) { super(testld, nCitalaca, nUpisivaca); } class Citalac extends ZadatakTestiranja { long rezultat = 0; void test() { i++) indeks++) for(long i = 0; i < ciklusaTestiranja;

for(int indeks = 0; indeks < velicinaKontejnera; rezultat += kontejne rZ aT es ti ra nje .g et (i nd ek s); } void u p i siRezultate() {

procit aj Re zu ltat += rezultat;

U v o d u p o re e n je p e r fo r m a n s i p o d u tic a je m la v in o g d in a m i k o g p re v o d e n ja p r o ita jte u la n k u

www-128. ibtn.com/devehperworki/Ubrary/j-itpl2214.

Poglavjje 21: Paralelno izvravanje

1033

vremeCit += trajanje; } } class Upisivac extends ZadatakTestiranja { void test() { for(long i = 0; i < ciklusaTestiranja; i++) for(int indeks = 0; indeks < velicinaKontejnera; indeks++) ko nt ej nerZaTestiranje.put(indeks, up is iP odatke[indeks]) ; } void upisiRezultate() } } void po kr en i C i t a o c e l U p i s i v a c e O exec.execute(new C i t a l a c O ) ; for(int i = 0; i < nUpisivaca; exec.execute(new U p i s i v a c O ) ; } i++) { {

vremeUpis += trajanje;

for(int i = 0; i < nCitalaca; i++)

class TestiranjeSinhronizovaneHashMape extends TestiranjeMapa { M a p< In teger,Integer> inicijalizatorKontejnera() return Co l 1ecti o n s .synchroni z e d M a p ( new HashMap<Integer,Integer>( MapData.map( new Co un ti ng Ge ne ra to r. Int eg er (), new Counti ngGenerator. I n t e g e r O , veli ci naKontejnera))); } TestiranjeSinhronizovaneHashMape(int nCitalaca, } } class TestiranjeConcurrentHashMape extends TestiranjeMapa { M a p< In teger,Integer> inicijalizatorKontejneraf) return new ConcurrentHashMap<Integer,Integer>( MapData.mapf new Co un ti ng Generator.Integer(), new CountingGenerator. I n t e g e r O , vel ic in aK on te jn er a)); } TestiranjeConcurrentHashMape(int nCitalaca, } } public class PoredjenjaMapa { public static void m a i n( S t r i n g [] args) { Tester.i ni cMMai n (args); new TestiranjeSinhronizovaneHashMape(10, 0); int nUpisivaca) { { int nUpisivaca) { {

super("Sinhro. HashMap", nCitalaca, nUpisivaca);

super( "C on cu rr en tH ash Ma p", nCitalaca, nUpisivaca);

1034

Misliti na Javi

new TestiranjeSinhronizovaneHashMape(9, new TestiranjeConcurrentHashMape(10, 0); new TestiranjeConcurrentHashMape(9, Tester .e xe c. sh ut do wn( ); } } /* Ispis: (primer) Vreme itanja 306052025049 428319156207 476016503775 243956877760 487968880962 23352654318 18833089400 20374942624 12037625732 23888114831 1); new TestiranjeConcurrentHashMape(5, 5);

1);

new TestiranjeSinhronizovaneHashMape(5, 5);

Tip
Sinhro. HashMap 10c Ou Sinhro. HashMap 9c lu vremeCit + vremellpis = Sinhro. HashMap 5c 5u vremeCit + vremeUpis = ConcurrentHashMap 10c Ou ConcurrentHashMap 9c lu vremeCit + vremeUpis = ConcurrentHashMap 5c 5u vremeCit + vremeUpis *///:-

Vreme upis. 0 47697347568 244012003202 0 1541853224 11850489099

Uticaj dodavanja upisivaa na ConcurrentHashM ap manji je ak i od uticaja na CopyOnW riteArrayList, ali ConcurrentHashM ap upotrebljava drugaiju tehniku koja oigleno minimizuje uticaj (trokove) upisivanja.

Optimistiko zakljuavanje
Pored toga to objekti tipa Atomic izvravaju atomske operacije kao to je decrementAndGet( ), neke Atomic klase omoguuju i tzv. optimistiko zakljuavanje. To znai da prilikom izraunavanja ne koristite uzajamno iskljuivanje, ali da nakon zavretka izraunavanja za auriranje Atomic objekta koristite metodu com pareA ndSet( ). Prosleujete jo j staru i novu vrednost, i ako se stara vrednost ne podudara s vrednou pronaenom u Atomic objektu, operacija se zavrava neuspehom - to znai da je neki drugi zadatak u meuvremenu izmenio objekat. Ne zaboravite da inae uvek upotrebljavamo mutex (rezervisanu re synchronized ili objekat tipa Lock) i tako spreavamo da vie zadataka istovremeno modifikuje objekat, ali ovde se ponaamo optimistiki tako to podatke ostavljamo nezakljuane i nadamo se da drugi zaaci nee uleteti i modifikovati ih. Naravno, sve to radimo samo zbog performansi - korienjem objekta tipa Atomic umesto rezervisane rei synchronized ili objekta tipa Lock, verovatno ete poboljati performanse. ta se deava ako operacija com pareA ndSet( ) zakae? Stvari postaju upave, pa ovu tehniku moete da prim enjujete samo na projekte koje moete krojiti kako god potrebe nalau. Ako com pareA ndSet( ) zakae, morate da odluite ta da radite; to je veoma van o , p o t o u s lu a ju d a je o p o r a v a k n e m o g u , u m e s to o v e te h n ik e m o r a t e d a u p o tie b U e

neku od konvencionalnih brava za uzajamno iskljuivanje (m utexa). Moda moete pokuati da ponovite operaciju i nee smetati ako ona (tek) drugi put uspe. Ili moda moete da zanemarite neuspeh - u nekim simulacijama, gubitak jedne take ne znai nita za celinu, jer je broj taaka ogroman. (Naravno, model morate poznavati toliko dobro da znate je li prethodna hipoteza istinita.)

Poglav[je 2 !: Paralelno izvravanje

1035

Zamislimo fiktivnu simulaciju koja se sastoji od 100 000 ,,gena duine 30; recimo da je to poetak nekakvog genetikog algoritma. Pretpostavimo da je za svaku ,,evoluciju genetikog algoritma potreban veoma skup proraun, pa ste odluili da upotrebite vieprocesorski raunar, rasporedite zadatke meu procesorima i tako poboljate performanse. Sem toga, umesto objekata tipa Lock koristite Atomic objekte da biste izbegli reijske trokove uzajamnog iskljuivanja. (Naravno, program ste najpre napisali na najjednostavniji mogui nain, pomou rezervisane rei synchronized. Tek kada je takav program proradio, otkrili ste da je prespor i poeli da primenjujete tehnike za poboljavanje performansi!) Zbog prirode ovog modela, zadatak koji otkrije sudar tokom proruuna, moe da ga zanemari i da ne aurira svoju vrednost. Evo kako to izgleda:
//: pa ralelno/BrzaSimulacija.java import java.util.concurrent.*; import java.util.concurrent.atomic.*; import j a v a . u t i l .*; import static n e t . mi nd vi ew .u ti l.Print.*; public class BrzaSimulacija { static final static final static final int N_ELEMENATA = 100000; int N_GENA = 30; int N_EV0LVERA = 50;

static final AtomicInteger[] [] MATRICA = new At om ic In te ge r[ N_ EL EME NA TA ][ N_ GE NA ]; static Random slucajan = new Random(47); static class Evolver implements Runnable { public void run() { { while(!Thread.interrupted())

// Nasumino izaberi element na kojem e raditi: int element = slucajan.nextInt(N_ELEMENATA); for(int i = 0; i < N_GENA; i++) { int prethodni = element - 1; if(prethodni < 0) prethodni = N_ELEMENATA - 1; int sledeci = element + 1; if(sledeci >= N_ELEMENATA) sledeci = 0; int staravrednost = MATRICA[ el em en t] [i ].get (); // Nekakav proraun po modelu: int novavrednost = staravrednost + M A T R IC Af pr et ho dn i] [i].g e t () + MA TR IC A[ sl ed ec i] [i ].get(); novavrednost /= 3; // Prosek tri vrednosti i f ( !MATRICA[element] [i] .compareAndSet(staravrednost, no va vrednost)) { // Ovde dolazi strategija obrade neuspeha. Mi emo // samo izvestiti o neuspehu i zanemariti ga; model // e kad-tad morati da ga o b r a d i . print("Stara vrednost se razlikuje od " + s t a r av re dn os t);

1036

Misliti na Javi

public static void main(String[] for(int i = 0; i < N_ELEMENATA; MATRICA[i][j] for(int i = 0 ;

args) throws Exception { i++)

ExecutorService exec = Ex ec ut or s. ne wC ac he dTh re ad Po ol(); for(int j = 0; j < N_GENA; j++) = new At om ic l n t e g e r ( s l u c a j a n . n e x t T n t (1000)) ; i++) i < N_EVOLVERA;

exec.execute(new Evolver()); T i me Un it .S EC 0N DS .s lee p( 5); e x ec .shutdownNow(); } } /* (Pokrenite da biste videli rezultat) * / // :

Sve elemente smo stavili u niz, zato to sm atram o da e to poboljati performanse. (Tu pretpostavku emo testirati u jed noj od vebi.) Svaki objekat tipa Evolver uproseava svoju vrednost s prethodnom i sa sledeom, i ako auriranje ne uspe, on ispisuje tu vrednost i ide dalje. Imajte u vidu da u programu nema uzajamnog iskljuivanja. Veba 39: (6) Da li su pretpostavke u programu BrzaSimulacija.java razumne? Izmenite niz tako da sadri proste cele brojeve umesto objekata tipa Atom iclnteger i upotrebite Lock brave za uzajamno iskljuivanje (mutexe). Uporedite performanse te dve verzije programa.

ReadWriteLock
ReadVVriteLock optimizuje situaciju kada u strukturu podataka upisujete relativno retko, ali iz nje vie zadataka esto ita. Klasa ReadVVriteLock om oguuje da do prvog pokuaja upisivanja istovremeno radi vie italaca. Nakon zakljuavanja brave za pisanje, itaoci ne mogu da rade sve dok se brava za pisanje ponovo ne otkljua. Nemogue je unapred rei da li e ReadVVriteLock poboljati performanse vaeg programa; to zavisi od inilaca kao to su srazmera broja itanja i broja modifikovanja podataka, trajanje operacije itanja i upisivanja (brava je sloenija, pa kratke operacije ne daju da se vidi poboljanje), koliina takm ienja meu nitima i da li se program izvrava na vieprocesorskom raunaru. Na kraju krajeva, jedini nain da saznate da li ReadWriteLock poboljava va program jeste da ga isprobate. U narednom primeru prikazana je samo najjednostavnija upotreba klase ReadVVriteLock, tj. zasebnih brava za itanje i upisivanje:
//: paralelno/ListaCitalacalUpisivaca.java import ja v a . u t i 1 .concurrent.*; import java.util.concurrent.locks.*; import ja v a . u t i l .*; import static n e t . mi nd vi ew .u ti l.Print.*; public class ListaCitalacaIUpisivaca<T> { private ArrayList<T> zakljucanaLista; // Neka redosled bude poten: private ReentrantReadWriteLock brava =

Poglavlje 21: Paralelno izvravanje

1037

new R e en tr an tR ea dW ri te Loc k( tr ue ); public ListaCitalacaIUpisivaca(int velicina, T pocetnaVrednost) zakljucanaLista = new ArrayList<T>( Collections.nCopies(velicina, p o c e tn aV re dn os t) ); } public T set(int indeks, T element) br av aZ aU pi si va nj e. loc k( ); try { return zakljucanaLista.set(indeks, element); } finally { b r a v a Z a U pi si va nj e. unl oc k( ); } } public T get(int indeks) br a v aZ aC it an je .l oc k(); try { // Dokaz da vie italaca moe zakljuati // (pribaviti) bravu za itanje: if(brava.getReadLockCount() > 1) p r i n t ( br av a. ge tR ea dLo ck Co un t()); return zakljucanaLista.get(indeks); } finally { br av aZ aC it an je .u nl ock (); } } public static void main(String[] } } class TestListeCitalacalUpisivaca { ExecutorService exec = E x e c u t o r s . ne wC ac he dTh re ad Po ol(); private final static int VELICINA = 100; private static Random slucajan = new Random(47); private Li staCitalacalUpisivaca<Integer> lista = new ListaCitalacaIUpisivaca<Integer>(VELICINA, 0); private class Upisivac implements Runnable { public void run() try { for(int i = 0; i < 20; i++) { // test od 2 sekunde 1 i s ta .s et (i, s l u c a j a n .n ex tl nt () ); TimeUni t. M I L L I S E C O N D S .s l e e p (1 0 0 ) ; } } catch(InterruptedException e) // Prihvatljiv nain izlaska { { args) throws Exception { 1); { { {

Lock bravaZaUpisivanje = b r av a. wr it eL oc k( );

Lock bravaZaCitanje = b r a v a . r e a d L o c k ( ) ;

new Te stListeCitalacaIUpisivaca(30,

1038

Misliti na Javi

print("Upisivac gotov, gasim"); e x ec .s hu td ow nN ow (); } } private class Citalac implements Runnable { public void run() { try { while(!Thread.interrupted()) lista. ge t( i); Ti me Un it .M IL LI SE CO NDS .s le ep (l ); } } } catch(InterruptedException e) { // Prihvatljiv nain izlaska } } } public Te stListeCitalacaIUpisivaca(int citalaca, int upisivaca) for(int i = 0; i < citalaca; i++) exec.execute(new C i t a l a c O ) ; for(int i = 0; i < upisivaca; i++) exec.execute(new U p i s i v a c O ) ; } } /* (Pokrenite da biste videli rezultat) *///:{ { { for(int i = 0; i < VEUICINA; i++)

ListaCitalacalUpisivaca moe sadrati fiksan broj objekata proizvoljnog tipa. Njenom konstruktoru morate dati eljenu veliinu liste i poetnu vrednost kojom listu treba popuniti. Metoda s e t( ) zakljuava bravu za upisivanje kako bi mogla da pozove pripadnu metodu A rrayL ist.set( ), a metoda g e t( ) zakljuava bravu za itanje kako bi mogla da pozove metodu A rrayList.get( ). Pored toga, g e t( ) proverava da li je vie italaca pribavilo (zakljualo) bravu za itanje i ako jeste, prikazuje njihov broj da bi dokazala kako vie italaca moe tu bravu istovremeno da zakljua. Kao test klase ListaCitalacalUpisivaca, TestListeCitalacalUpisivaca pravi zadatke za itaoce i upisivae u objekat tipa ListaCitalacaIUpisivaca<Integer>. Vodite rauna o tome da je broj upisivanja mnogo manji od broja itanja. Proitajte dokumentaciju JDK za klasu ReentrantReadVVriteLock i videete da ona ima vie drugih metoda i da se spom inju nekakva ,,ravnopravnost i strateke odluke Ta alatka je prilino sofisticirana, pa je treba koristiti samo kada pokuavate da poboljate performanse. U prvoj verziji programa treba da koristite jednostavnu sinhronizaciju, a ReadVVriteLock primenjujte samo ako morate Veba 40: (6) Na osnovu primera ListaCitalacalUpisivaca.java, napravite objekat tipa M apaCitalacalU pisivaca p o m o u k lase HashMap. L sp itajte n je g o v e p e r tb r m a n s e pomou prilagodenog programa PoredjenjaMapa.java. Kakve su u odnosu na performanse sinhronizovanog objekta tipa HashMap odnosno ConcurrentHashM ap?

Poglavlje 21: Paralelno izvravanje

1039

Aktivni objekti
Kada prouite ovo poglavlje, moda ete stei utisak da je u Javi veoma sloeno i teko korektno sprovesti vienitni rad. Pored toga, izgleda i pomalo kontraproduktivno - iako zadaci rade paralelno, morate uloiti mnogo truda da realizujete tehnike za spreavanje tih zadataka kako ne bi smetali jedan drugom. Ako ste ikada pisali u asembleru, pisanje vienitnih programa izaziva isti oseaj: vana je svaka sitnica, sve morate sami da uradite i nema zatite u vidu provere koju sprovodi prevodilac. Da problem nije u samom modelu vienitnog rada? Na kraju krajeva, on je preuzet reIativno nepromenjen iz sveta proceduralnog programiranja. Moda postoji drugaiji model paralelnog rada, prikladniji za objektno orijentisano programiranje. Jedan od alternativnih pristupa nazvan je aktivni objekti ili aktori.26 Objekti su proglaeni ,,aktivnim zato to svaki objekat odrava sopstvenu radnu nit i red za poruke; svi zahtevi koji se odnose na taj objekat ulaze u njegov red za ekanje i izvravaju se jedan po jedan. Dakle, kod aktivnih objekata serijalizujemoporuke, a ne tnetode, to znai da vie ne moramo da se titimo od problema koji nastaju kada se zadatak prekine usred petlje. Kada aktivnom objektu poaljete poruku, ona se transformie u zadatak koji ulazi u objektov red za ekanje na kasnije izvravanje. Za realizaciju te eme podesna je klasa Future Jave SE5. Evo jenostavnog primera u kojem dve metode stavljaju pozive u red za ekanje:
//: paralelno/PrimerAktivnogObjekta.java // Moe da prosledi samo konstante, nepromenljive, // objekte" ili druge aktivne objekte kao argumente // asinhronim metodama. import j a v a .ut i 1 .co nc u r r e n t . import j a v a . u t i l .*; import static net.mindvievv.uti 1 .P r i n t .*; public class PrimerAktivnogObjekta { private ExecutorService izvrsilac = Executors,newSi n g le Th re ad Ex ec ut or (); private Random slucajan = new Random(47); // Ubaciu odlaganje nasuminog trajanja da bih postigao efekat // trajanja prorauna: private void sacekaj(int faktor) try { TimeUni t.MILLISECONDS.sleep( 100 + sl uc aj an .nextInt(faktor)); } catch(InterruptedException e) { print("sleep() prekinut"); } public Future<Integer> calculatelnt(final int x, final int y) { { return izvr si1ac.submit(new C a l1able<Integer>() { "nevezane

Hvala Allenu Holu bu to je naao vremena da m i to objasni.

1040

Misliti na Javi

public Integer c a l l () { print("pokreem " + x + 1 1 + " + y); s a c e k a j (500); return x + y; } }); } public Future<Float> calculateFloat(final float x, final float y) { { return izvrsilac.submit(new Callable<Float>() public Float call () { print("pokreem " + x + " + " + y); s a c e k a j (2000); return x + y; } }); } public void shutdown() { izvrsi la c. sh ut do wn (); } {

public static void main(String[] args)

Primer Ak tivnogObjekta pl = new Prim er Ak ti vn og Ob je kta (); // Spreava izuzetak ConcurrentModificationException: L i s t < F u t u r e < ? rezultati = new C o p y O n W r i t e A r r a y L i s t < F u t u r e < ? ( ) ; for(float f = O.Of; f < l.Of; f += 0.2f) r e z u l t a t i ,add(pl.calculateFloat(f, f ) ) ; forfint i = 0 ; i <5; i++) r e zu lt at i. ad d( pl .c alc ul at el nt (i, i ) ) ; print("Zavreni pozivi svih asinhronih metoda"); while(rezultati.size() > 0) if ( f .is D on e( )) { try { print(f.get()); } catch(Exception e) } re zu lt at i. re mo ve (f ); } } pl.shutdown(); } } /* Ispis: Zavreni (85% podudaranja) { throw new R u n t im eE xc ep ti on (e ); { fo r( Future<?> f : rezultati)

pozivi svih asinhronih metoda

pokr e em 0.0 + 0.0 pokreem 0.2 + 0.2

0.0
pokreem 0.4 + 0.4 0.4 pokreem 0.6 + 0.6

0.8

Poglavlje 2 1: Paralelno izvravanje

1041

p o kr e em 0.8 + 0.8

1.2
p o kr e em 0 + 0

1.6
p o kr e em 1 + 1

0
p o kr e em 2 + 2

2
p o kr e em 3 + 3

4
pokr e em 4 + 4

6 8
*///:-

Izvrilac jedne niti proizveden pozivom metode Executors.newSingleThreadE x e c u to r( ) odrava sopstveni neogranieni blokirajui red za ekanje i ima samo jednu nit koja vadi zadatke iz reda i izvrava ih do zavretka. U metodama ca lcu lateln t( ) i ca k u la te F lo a t( ) m oram o samo da pozovemo su b m it( ) da bismo novi obiekat tipa Callable dali kao odgovor na poziv metode, ime pozive pretvaramo u poruke. Telo metode nalazi se unutar metode c a ll( ) u anonim noj unutranjoj klasi. O bratite panju na to da sve metode aktivnih objekata imaju povratnu vrednost tipa Future, s generikim param etrom koji je stvarni povratni tip te metode. Na taj nain se poziv metode gotovo trenutno vraa, a pozivalac pomou tog objekta tipa Future saznaje kada je zadatak zavren i dobija stvarnu povratnu vrednost. Time je reen najsloeniji sluaj, a postupak je jo jednostavniji ukoliko poziv nema povratnu vrednost. U metodi m a in ( ), pravi se objekat tipa L is t< F u tu re < ? za hvatanje Future objekata koje vraaju poruke calcu lateF loat( ) i ca lcu lateln t( ) poslate aktivnom objektu. Za svaki Future, tu listu ispituje metoda isD o n e( ) koja ga uklanja iz liste kada zavri rad i obradi rezultate. Im ajte u vidu da klasa CopyOnW riteArrayList ini da listu ne moram o da kopiramo da bismo izbegli izuzetke ConcurrentM odificationException. Da bi se spreio sluajni meusobni uticaj niti, argumenti prosleeni pozivu metode aktivnog objekta sm eju biti: samo za itanje ili drugi aktivni objekti ili nevezani objekti (m oj term in), to su objekti koji nemaju veze s drugim zadacima. (To je teko sprovesti, poto Java to zasad ne podrava.) Za aktivne objekte vai sledee: 1 . Svaki objekat ima sopstvenu radnu nit. 2. Svaki objekat zadrava potpunu kontrolu nad svim svojim poljima (to je neto stroe nego kod obinih objekata koji imaju opciju kontrole svojih polja). 3. Sva k o n 'iu itik a ciia iz n ie u a k tiv n ih o b je k a ta o d v ija se u o b lik u p o r u k a . 4 . Sve poruke izmedu aktivnih objekata ulaze u redove za ekanje. Rezultati su veoma privlani. Poto poruka jednog aktivnog objekta drugom moe biti blokirana samo ako se odloi njen ulazak u red za ekanje, i poto je to odlaganje uvek veoma kratko i ne zavisi od drugih objekata, slanje poruke se zapravo ne moe blokirati

1042

Misliti na Javi

(najgore to se moe desiti je kratko odlaganje). Poto sistem aktivnih objekata kom unicira samo preko poruka, dva objekta ne mogu biti blokirana dok se nadmeu da pozovu metodu nekog treeg objekta, a to znai da ne moe nastati uzajamna blokada, to je veliki korak napred. Poto radna nit aktivnog objekta u svakom trenutku izvrava samo po jednu poruku, nema takm ienja za resurse i ne moram o da sinhronizujemo metode. Sinhronizaja i dalje postoji, ali na nivou poruka, zato to se pozivi metoda ubacuju u red za ekanje, pa se u svakom trenutku moe izvravati samo jedan od njih. Naalost, prevodilac ne daje neposrednu podrku za aktivne objekte, a runo programiranje po gornjem obrascu previe je zametno. Meutim, oblast aktivnih objekata i aktora se razvija, kao i oblast agentski orijentisanogprogram iranja koja je jo zanimljivija. Agenti su zapravo aktivni objekti, ali agentski sistemi se ne m enjaju u zavisnosti od raunara niti mree. Ne bi me iznenadilo da agentski orijentisano programiranje nasledi objektno orijentisano program iranje, zato to se u njemu kombinuju objekti i relativno lako reenje za paralelno izvravanje. Vie inform acija o aktivnim objektim a, aktorima i agentima moete nai na Webu; neke ideje realizovane u aktivnim objektim a moete nai naroito u C.A.R. Hoareovoj teoriji komunicirajuih sekvencijalnih procesa (Communicating Seciuential Processes, CSP). Veba 41: (6) Programu PrimerAktivnogObjekta.java dodajte deo za obradu poruka koji nema povratnu vrednost, a poziva se iz metode m a in (). Veba 42: (7) Izmenite VoskOMatik.java tako da realizuje aktivne objekte. Projekat:27 Pomou anotacija i Javassista napravite anotaciju klase @Active koja odredinu klasu transformie u aktivan objekat.

Saetak
Trebalo je da iz ovog poglavlja nauite osnove paralelnog programiranja pomou Javinih niti, kako biste shvatili sledee: 1. Moete izvravati vie nezavisnih zadataka. 2 . Morate uzeti u obzir sve mogue probleme prilikom gaenja tih zadataka. 3 . Zadaci se meusobno om etaju prilikom korienja deljenih resursa. Osnovna alatka za spreavanje takvih sudara je uzajamno iskljuiva brava (mutex). 4 . Ako zadatke ne projektujete paljivo, oni se mogu uzajamno blokirati. Neophodno je nauiti kada se koristi paralelno izvravanje, a lcada da ga izbegavati. Osnovni razlozi za upotrebu jesu: Da biste posao podelili na vie zadataka i tako efikasnije koristili raunar. Time se postie i (za programera nevidljiva) raspodela zadataka na vie procesora. Radi bolje organizacije koda. Radi vece pogodnosti za korisnika.

P rojekti su predlozi koji se m ogu koristiti (recim o ) za sem inarske radove. Vodi s reenjim a ne sadri reenja projckata.

Poglavjje 21: Paralelno izvravanje

1043

Klasian primer uravnoteenog korienja resursa jeste upotreba procesora dok se eka na zavretak ulazno/izlaznih operacija. Bolja organizacija koda obino se postie u sim ulacijama. Klasian primer pogodnosti za korisnika jeste nadziranje dugmeta za prekid tokom dugakih preuzimanja s mree. Dodatna prednost niti je to to ,,teka prebacivanja iz konteksta jednog procesnog okruenja u kontekst drugog (reda veliine nekoliko hiljada naredaba) zamenjuju ,,lakim prebacivanjem iz konteksta jednog izvrnog okruenja u kontekst drugog u okviru istog procesnog okruenja (reda veliine nekoliko stotina naredaba). Poto sve niti jednog procesa dele isti m em orijski prostor, pri lakom prebacivanju iz konteksta zamenjuju se samo promenljive izvravanja programa i lokalne promenljive. S druge strane, promena procesa - dakle, teka promena konteksta - mora da zameni ceo memorijski prostor. Glavni nedostaci vienitnog izvravanja su: T. Usporenje rada dok niti ekaju na deljene resurse. 2. Za upravljanje nitima troi se dodatno procesorsko vreme. 3. U sluaju loeg projektovanja, programi postaju neopravdano sloeni. 4 . Postoji verovatnoa da e biti patolokih pojava kao to su nemogunost dobijanja resursa, trka za resursima, uzajamna blokada i uzajamna blokada uprkos izvravanju (vie niti izvrava pojedinane zadatke koje sve zajedno ne mogu da zavre). 5. Uslovi za paralelno izvravanje menjaju se u zavisnosti od platforme. (Piui primere za ovu knjigu, otkrio sam uslove za trku koji su se brzo pokazali na nekim raunarima, a uopte se nisu pojavili na drugim.) Ukoliko program budete razvijali na jednom od raunara na kojem se Ioi delovi ne vide, doiveete neprijatno iznenaenje kada taj program distribuirate. Jedna od najveih potekoa pri vienitnom izvravanju nastaje zato to vie zadataka deli (istovrerneno koristi) neki resurs - kao to je m em orija objekta - a programer mora spreiti da vie zadataka istovremeno pokua da ita i menja taj resurs. To se moe postii prom iljenom upotrebom dostupnih mehanizama za zakljuavanje (npr. rezervisane rei synchronized). Te alatke jesu neophodne, ali ih morate dobro upoznati poto e inae utke proizvesti uzajamno blokiranje. Pored toga, za primenu niti je potrebna izvesna umenost. Java omoguuje pravljenje proizvoljnog broja objekata potrebnog za reavanje problema - barem u teoriji. (Na primer, pravljenje miliona objekata za inenjersku analizu metodom konanih elemenata u Javi nije izvodljivo bez projektnog obrasca Flyweight.) Meutim, izgleda da postoji gornja granica broja niti koje se mogu napraviti, poto od neke granice niti postaju zametne. Tu kritinu granicu nije lako otkriti. Ona se esto menja u zavisnosti od operativnog sistema i JV M -a; moe biti manja od sto, a i vea od nekoliko hiljada. Poto se za reenje problema esto pravi tek nekoliko niti, ovo najee i nije neko ogranienje, ali u optijem p r o ie k tu m o e vas n a te r a ti da u s v o iite n ek u od e m a paralelne saradnje. Bez obzira na to koliko jednostavno vienitno programiranje izgleda u odreenom jeziku ili biblioteci, smatrajte ga nekom vrstom magije. Uvek vas neto moe ujesti kada se najm anje nadate. Problem veere filozofa zanimljiv je zato to se moe napisati tako da se retko javlja uzajamna blokada i zato vam daje utisak da sve lepo radi.

1044

Misliti na Javi

Vienitni rad bi po pravilu trebalo primenjivati paljivo i tedljivo. Ukoliko problemi s vienitnim radom postanu veliki i sloeni, moda bi trebalo da ih reite na jeziku kao to je Erlang. To je jedan od nekoliko funkcijskih jezika specijalizovanih za vienitno programiranje. Na takvom jeziku treba napisati samo one delove programa u kojima je neophodno vienitno izvravanje - ako ih ima mnogo i ako su dovoljno komplikovani da opravdavaju takav pristup.

Literatura za dalje usavravanje


Naalost, o paralelnom programiranju postoji mnogo netanih informacija - to pokazuje koliko je ono samo po sebi zamreno i koliko je lako pomisliti da ste ga najzad shvatili. (Govorim iz vlastitog iskustva, poto sam ve vie puta pomiljao kako sam ga napokon savladao; i ne sumnjam da me u budunosti ekaju neprijatna otkria.) Kada uzmete u ruke bilo kakav nov dokument o paralelnom radu, uvek se morate zapitati koliko njegov autor zapravo zna o tome to pie. Ovo su neke od knjiga za koje smem kazati da su pouzdane: Jav a Concurrency in Practice, autori Brian Goetz, Tim Peierls, Joshua Bloch, Joseph Bowbeer, David Holmes i Doug Lea (Addison-Wesley, 2006). U sutini, ovo je ,,whos who u svetu vienitnog izvravanja u Javi. Concurrent Program m ing in Jav a (drugo izdanje), autor Doug Lea (Addison-Wesley, 2000). Mada je knjiga objavljena znatno pre Jave SE5, dobar deo nje Doug je pretoio u nove biblioteke java.util.concurrent, to je ini neophodnom za potpuno upoznavanje paralelnog programiranja. Ona prevazilazi paralelnost u Javi i razmatra trenutno stanje u vie jezika i tehnologija. lako moe biti teka, zasluuje da je proitate vie puta (najbolje na po nekoliko meseci, kako biste stigli da usvojite proitano). Doug je jedan od retkih ljudi koji zaista razumeju paralelnost, pa e vam se trud isplatiti. T he Jav a Language Specificatioti (tree izdanje), poglavlje 17, autori Gosling, Joy, Steele i Bracha (Addison-Wesley, 2005). Tehnika specifikacija dostupna je i u obliku elektronskog dokumenta, na adresi: http://java.su n .com /docs/books/jls.
Reenja o d ab ran ih vebi data su u elek tron sk om d o k u m en tu

The Thinking in Java Annotated Solution Guide, koji se m oe kupiti na lokaciji www.MindView.com.

Grafika korisnika okruenja


Jedan od osnovnih priticipa projektovanja glasi: neka to je jednostavno postane lako, a ono toje teko postane mogueV
P r v o b i t n o z a m i Sl j e n a s v r h a BIBLIOTEKE GRAFICKOG k o r i s n i k o g o k r u Z e n j a (GKO, engl. graphical user interface, GUI) u Javi 1.0 bila je da se programeru omogui da napravi grafike programe koji izgledaju dobro na svim platformama. Taj cilj nije ostvaren. Umesto toga, u Javi 1.0 postojao je komplet alatki za apstraktneprozore (engl. Abstract Window Toolkit, AWT) koji je davao grafiko okruenje podjednako osrednjeg izgleda na svim sistemima. Osim toga, ta biblioteka je jako ograniena: dozvoljeno je korienje samo etiri fonta, a ne moe se pristupiti nijednom naprednijem elementu grafikog okruenja iz nekog operativnog sistema. Programerski model AWT iz Jave 1.0 istovremeno je bio nezgrapan i nije bio objektno orijentisan. Polaznik jednog od m ojih seminara (koji je radio u kom paniji Sun tokom pisanja Jave) ob jasn ioje razlog: prvobitni AWT je smiljen, projektovan i realizovan za mesec dana. To je sigurno udo produktivnosti, ali i lekcija o tome zato je projektovanje vano. Situaja se poboljala nakon uvoenja modela AWT iz Jave 1.1 koji koristi nmogo jasniji, objektno orijentisan pristup, a podrava i zrna Jave, tj. model programiranja kom ponenata usmeren ka lakom pravljenju vizuelnih razvojnih okruenja. U Javi 2 (JD K 1.2) dovrena je transformacija starog AWT-a iz Jave 1.0 tako to je sve zamenjeno Javinim osnovnitn klasama (engl. Java Foundation Classes, JFC) iji se grafiki deo zove Swing. To je bogat skup Javinih zrna koja se lako koriste i razumeju, a pom ou njih se u vizuelnim razvojnim alatima (prevlaenjem i otputanjem , kao i runim program iranjem ) moe napraviti grafiko okruenje kojim moete da budete zadovoljni. Izgleda da pravilo tree prepravke" koje vai u industriji softvera (proizvod nije dobar sve dok se triput ne prepravi) vai i za programske jezike. Ovo poglavlje posveeno je iskljuivo modernoj biblioteci Swing iz Jave 2, pri emu se opravdano pretpostavilo da se grafika okruenja u Javi piu pomou Swinga.2Ako iz nekog razloga morate da koristite prvobitni, stari AWT (radi podrke starom kodu ili usled ogranienja koja namee ita), uvod u tu problematiku moete da pronaete u prvom izdanju ove knjige na adresi www.MindView.net. O bratitc panju na to da Java i dalje sari neke AWT komponente i da ih u nekim situacijama morate koristiti. Imajte u vidu da ovo nije sveobuhvatan pregled kom ponenata grafike biblioteke Swing, niti svih metoda opisanih klasa. Grafika biblioteka Swing je ogromna, a iz ovog poglavlja samo treba da nauite osnovne pojmove i da upoznate principe projekta. Ukoliko vam treba vie od toga, biblioteka Swing verovatno moe da prui ono to elite ako ste voljni da je istraujete. Ovde u pretpostavljati da ste s lokacije java.sun.com preuzeli i instalirali (besplatnu) dokumentaciju Javine biblioteke u HTM L formatu i da ete pregledati klase iz paketa

V arijanta ovoga se zove princip n ajm anjeg iznenaenja, to znai da korisnika ne bi trebalo iznenaivati. IBM j e za svoj program za ureivanje teksta Eclipse ( www.Eclipse.org) napravio novu G U I biblioteku O t v o r e n o g iz v o r n o g koda koja je alternativa Svvingu; b o lje je predstavljena u nastavku poglavlja.

1046

Misliti na Javi

javax.swing u toj dokumentaciji kako biste saznali sve detalje o biblioteci Swing i metode iz nje. Pomo moete potraiti i na Webu, ali najbolje je da krenete od Sunovog udbenika za Swing na adresi http://java.sun.com/docs/books/tutorial/uiswing. Postoji veliki broj (prilino debelih) knjiga posveenih iskljuivo bibliote Swing i njih ete sigurno prouavati ako vam zatreba ire znanje, ili ako poelite da promenite podrazumevano ponaanje biblioteke Swing. Kako budete savladavali biblioteku Swing, otkrivaete sledee:

1. Biblioteka Swing je mnogo bolji model za programiranje od onih koje ste verovatno
sretali u drugim jezicim a i razvojnim okruenjima. Osnovna struktura za tu biblioteku jesu zrna Jave (koja e biti objanjena pri kraju ovog poglavlja).

2. Alatke za pravljenje grafikih okruenja (vizuelna okruenja za programiranje)


obavezan su vid kompletnog razvojnog okruenja za Javu. Zrna Jave i Swing om oguuju razvojnom okruenju da pie kod umesto vas, dok vi razmetate komponente na obrasce pomou grafikih alatki. To u velikoj meri ubrzava projektovanje grafikog okruenja i om oguuje slobodnije eksperimentisanje i isprobavanje dizajna, to posledino dovodi do izbora boljeg okruenja.

3. Jednostavnost i dobar projekat grafike biblioteke Swing znai da e ak i ako koristite vizuelnu alatku, umesto da runo piete kod, dobijeni kod biti razumljiv. To reava veliki problem s korienjem ranijih vizuelnih alatki kojima se esto generisao nerazumljiv kod. Grafika biblioteka Swing sadri sve komponente koje se oekuju u modernom korisnikom okruenju, od dugmadi sa slikaina, preko stabala do tabela. To je velika biblioteka, ali sloenost odgovara zadatku koji se obavlja: ako je neto jednostavno, ne morate da piete mnogo koda, ali ukoliko pokuate da uradite neto tee, i kod postaje srazmerno sloeniji. Dobar deo privlanosti biblioteke Swing potie od neega to bi se moglo nazvati ,,ortogonalnost korienja". Odnosno, kada jednom steknete opte ideje o ovoj biblioteci, m oi ete da ih prim enjujete svuda. Dok sam pisao primere iz ovog poglavlja, najee sam ve na osnovu imena metode mogao da zakljuim ta ona radi, i to zahvaljujui standardu za imenovanje. To je, sasvim sigurno, obeleje dobre biblioteke. Pored toga, komponente se obino mogu dodavati drugim komponentama i sve e raditi ispravno. Navigacija pom ou tastature je automatska: aplikaciju napisanu pomou Svvinga inoete da pokrenete i bez mia, to ne zahteva nikakvo dodatno programiranje. Podrka za pom eranje sadraja je izvanredna - treba samo da om otate komponentu u objekat klase JScrollPane dok je dodajete u obrazac. Za dodavanje komponenata poput prirunih saveta (engl. tooltips) obino je dovoljan samo jedan red koda. Cela biblioteka Swing napisana je na Javi radi prenosivosti. Swing podrava i prilino radikalnu mogunost nazvanu prilagodljiv izgled i ponaanje (engl. pluggable look & feel), to znai da izgled grafikog okruenja moe dinamiki
d a sc p r o m e n i d a b i se p r il a g o d io o e k i v a n j im a k o ris n ik a n a r a z li itim p l a t t o r m a m a

i operativnim sistemima. Mogue je (mada teko) napraviti ak i sopstveni izgled okruenja. Neka takva okruenja moete nai i na Webu. '
M eni se najvie svia izgled i ponaanje Salveta (engl. nnpkin) autora Kena A rnolda, u kojem prozori izgledaju kao da su skicirani na salveti. Posetite http://mipkinlaJ.sourceforge.iiet.

Poglavlje 22: Grafika korisnika okruenja

1047

Uprkos svim svojim pozitivnim aspektima, Swing nije za svakoga, niti je sve probleme s korisnikim okruenjima reio onako kako su njihovi projektanti zamislili. Na kraju poglavlja razmotriemo dve alternative za Swing: IBM -ov SWT, razvijen za editor teksta Eclipse, ali besplatno dostupan kao samostalna GUI biblioteka otvorenog izvornog koda, i Flex kom panije Adobe, alatku za razvoj klijentskih okruenja Web aplikacija u Flashu.

Apleti
Kada se Java tek pojavila, dobar deo halabuke oko tog jezika izazvao je a p le t - program koji se preuzima sa Interneta da bi se izvravao u itau Weba (unutar tzv. bazena s peskom, da ne bi nakodio klijentskom raunaru). Predvialo se da e Java aplet postati sledea faza u evoluciji Interneta i u mnogima od prvih knjiga o Javi pretpostavljalo se da je italac zainteresovan za taj jezik prvenstveno zato to eli da pie aplete. Iz raznih razloga, ta revolucija se nije odigrala. Dobar deo problema inilo je to to veina raunara nema potreban Java softver za izvravanje apleta, a veina korisnika ne eli da preuzme i instalira paket od 10 M B da bi prikazali neto to su sluajno pokupili s Weba. Ve i spominjanje neeg slinog dovoljno je da uplai mnoge korisnike. Java apleti nikada nisu dostigli kritinu masu kao sistem za distribuciju aplikacija klijentima, i premda se apleti i alje povremeno sreu, po pravilu pripadaju prolosti raunarstva. To ne znai da apleti nisu zanimljiva i vredna tehnologija. Ukoliko moete obezbediti da svi korisni imaju instaliran JRE (recim o u jednoj kom paniji), onda bi apleti (ili JNLP/Java Web Start koje emo opisati u nastavku poglavlja) mogli biti savreni za istribuciju klijentskih programa i automatsko auriranje svih raunara i pri tom bi se izbegli trokovi i trud koji obino prate instalaciju i distribuciju novog softvera. Uvod u tehnologiju apleta nai ete u mrenim dodacima ove knjige, na adresi

www.MindView.net.

Osnove Svvinga
Veina Svving aplikacija, tj. grafikih korisnikih okruenja, gradi se unutar jednostavnog objekta tipa JFram e, koji pravi prazan prozor (ili okvir, engl . fram e) u operativnim sistemima svih korisnika. Naslov prozora se ovako zadaje pomou konstruktora objekta tipa JFram e:
//: gui/ZdravoSwing.java import javax.swing.*; public class ZdravoSwing { public static void main(String[] args) { JFrame prozor ~ new JFrame("Zdravo Swing"); prozor.setDefaultCloseOperati o n (JFrame.E X IT _0 N_ CL 0S E); prozor.setSize(300, } 100); pr oz or .s et Vi si bl e( tru e);

} ///:-

1048

Misliti na Javi

Metoda setD efaultC loseO peration( ) kazuje objektu tipa JFram e ta da radi kada korisnik izvede manevar gaenja. Konstanta EXIT_O N_CLO SE saoptava mu da izae iz programa. Podrazumevano se u tom sluaju ne radi nita, pa ako ne pozovete metodu setD efaultC loseO peration( ) ili ne napiete ekvivalentan kod za va prozor, aplikaja se nee zavriti. setS ize( ) zadaje veliinu prozora u pikselima. O bratite panju na poslednji red:
p r oz or .s et Vi si bl e( tru e);

Bez njega, na ekranu ne biste videli nita. U prazan prozor (objekat tipa JFram e) dodaemo natpis (objekat tipa JLabel):
//: gu i/ZdravoNatpis.java import javax.swing.*; import java.util.concurrent.*; public class ZdravoNatpis { public static void main(String[] JLabel args) throws Exception { JFrame prozor = new JFrame("Zdravo, Swing"); natpis = new JLabel("Jedan natpis"); p r o z o r . a dd (n at pi s); prozor.setDefaultCloseOperati o n (JF ra me .E X I T _ O N _ C L O S E ) ; prozor.setSize(300, 100); p r oz or .s et Vi si bl e( tru e); Ti me Un it .S EC ON DS .s lee p( l); n a tp is .s e t T e x t ( " E j ! Ovo se p r o m e n i l o ! "); } 1 ///:-

Tekst JLabel natpisa menja se nakon jedne sekunde. Iako je to zabavno i u tako jednostavnom programu bezbedno, ba i nije dobro da glavna nit m a in ( ) pie neposredno u GUI komponente. Swing ima sopstvenu nit za prim anje UI dogadaja i auriranje ekrana. Ako sadraj ekrana ponete da menjate pomou drugih niti, moete izazvati sudare i uzajam nu blokadu opisane u poglavlju Paralelno izvravanje. Umesto toga, druge niti - kao prethodno spomenuta nit m a in () - treba Svvingovoj niti za otpremu dogaaja (engl. event dispatch thread) da alju (engl. submit) zadatke na izvravanje.4 To se postie predajom zadatka metodi SwingU tilities.invokeLater( ) koja zadatak smeta u redza ekanje dogadaja (engl. event queue), iz koga ih nit za otpremu dogaaja (u nekom trenutku) vadi i izvrava. U prethodnom primeru to bi izgledalo ovako: /'/: gui /P odnosenjeZadatkaZaRukovanjeNatpi som.java
import javax.swing.*; import ja v a . u t i 1.c o n c u r r e n t .*;

Strogo uzev, nit za otprem u dogaaja pripada biblioteci AWT.

Poglavlje 22: Grafika korisnika okruenja

1049

public class PodnosenjeZadatkaZaRukovanjeNatpisom { public static void main(String[] args) throws Exception { JFra me prozor = new JFrame("Zdravo, Swing"); final JLabel natpis = new JLabel("Jedan natpis"); prozor.add(natpis); prozor.setDefaultCloseOperati o n (J F r a m e .EX IT _0 N_ CL 0S E); prozor.setSize(300, 100); p r o z o r . s et Vi si bl e( tru e); Timellnit.SECONDS.sleep(l); Sw in gU tilities.invokeLater(new Runnable() public void run() ) }); } { {

na tp is .s e t T e x t ( " E j ! Ovo se promenilo!");

} III-Sada natpisom vie ne rukujemo neposredno, nego aljemo objekat koji realizuje interfejs Runnable, a stvarno rukovanje obavlja nit za otpremu dogaaja kada u reu za ekanje dogaaja doe do tog zadatka. Kada bude izvravala taj zadatak, ona nee raditi nita drugo, pa ne moe nastati sudar - ukohko se sav kod u vaem programu bude pridravao tog pristupa, tj. rukovao ekranom pomou metode SwingUtiIities.invokeLater(). To se odnosi i na pokretanje samog programa - m a in ( ) ne bi trebalo da poziva Swing metode kao u gornjem programu, nego da podnese zadatak redu za otpremu dogaaja.5 Dalde, pravilno napisan program izgiedao bi otprilike ovako:
//: gui/S1anjeSwi n g P r o g r a m a .java import javax.swing.*; import j a v a .ut i 1 .c oncurrent.*; public class SlanjeSwingPrograma extends JFrame { JLabel natpis; public S1 an je Sw in gP ro gr am a() { super("Zdravo, Swing"); natpis = new JLabel("Jedan na tpis "); add(natpis); setDefaultCl oseOperati on(JFram e. EX IT _0 N_ CL0 SE ); setSize(300, 100); se tV i s i b l e ( t r u e ) ; } static SlanjeSwingPrograma psp; public static void m a i n ( S t r i n g [] args) throws Exception { Sw i n g U t i 1 it i e s .invokeLater(new Runnablef) public void run() }); { { psp = new Slan je Sw in gP ro gr am a(); }

Ova praksa jc novina u Javi SE5, pa je gom ila starih program a ne podrava. To ne znai da su njihovi autori bili neznalice, nego da se preporuena praksa stalno m enja.

1050

Misliti na Javi

Time Un it .S EC ON DS .s lee p( l); Sw ingUtilities.invokeLater(new Runnable() public void run() } 0; } } ///{ {

ps p. na tp is .s et Te xt ("E j! Ovo se p r o m e n i l o ! ");

Obratite panju na to da poziv metode sleep ( ) rtije unutar konstruktora. Ako ga stavite tarno, prvobitni tekst natpisa (Jedan natpis) uopte nee biti prikazan, poto u tom sluaju konstruktor nee zavriti dok se ne zavri metoda sleep ( ) i novi natpis ne bude umetnut. Ali ako je poziv metode slee p ( ) unutar konstruktora ili bilo koje UI operacije, to znai da tokom spavanja koje prouzrokuje metoda sle e p ( ) zadravate izvravanje niti za otpremu dogaaja, to je po pravilu loe. Veba 1: (1) Izmenite ZdravoSwing.java tako da dokaete da se aplikacija ne gasi bez poziva metode setD efauItCIoseO peration( ). Veba 2: (2) Izmenite ZdravoNatpis.java tako da pokaete da je doavanje natpisa dinamiko - stavite u prozor pseudosluajan broj natpisa.

Alatka za prikazivanje
Objediniem o navedene ideje i smanjiti koliinu redundantnog koda sledeom alatkom za prikazivanje koju em o koristiti u ostalim primerima biblioteke Svving u ostatku poglavlja:
/ / : net/ mindview/uti 1 / S w i ng Ko nz ola .java // Alatka za pokretanje Swing primera, // bilo apleta bilo objekata tipa JFrame, s konzole. package net.mindview.util; import j a v a x . s w i n g .*; public class SwingKonzola { public static void run(final JFrame f, final public void run() { int sirina, final int visina) { { SwingUti 1 it ie s.invokeLater(new R u n n a b l e O

f.setTi t l e ( f . g e t C l a s s ().g et Si mp le Na me ()); f .setDefaultC lo se Op era ti on (J Fr am e. EX IT _O N_C LO SE ); f.setSize(sirina, visina); f .setVi si b l e ( t r u e ) ;

Poglavlje 22: Grafika korisnika okruenja

1051

Ovu alatku ete moda poeleti da koristite na vie mesta, pa je smetena u biblioteku net.mindview.util. Da bi mogla da je koristi, aplikacija mora biti u objektu tipa JFram e (kao svi primeri u ovoj knjizi). Statina metoda r u n ( ) zadaje naslov prozora jednak Class imenu objekta tipa JFrame. Veba 3: (3) Izmenite SlanjeSwingPrograma.java tako da upotrebljava program SwingKonzola.

PravIjenje dugmeta
Pravljenje dugmeta je prilino jednostavno: samo pozovite konstruktor klase JButton i prosledite mu natpis koji treba da se pojavi na dugmetu. Kasnije ete videti da moete uraditi i neto naprednije, npr. staviti slike na dugmad. Referenca na dugme se obino uva unutar klase da bi kasnije moglo ponovo da jo j se pristupi. JButton je Swing kom ponenta sa sopstvenim prozoriem koja se automatski iscrtava tokom auriranja prikaza. To znai da dugme ili neki drugi kontrolni objekat ne crtate runo, ve ih samo postavljate na obrazac i om oguujete im da se automatski iscrtavaju. Dugme se na obrazac postavlja obino unutar konstruktora:
//: gui/Dugmel.java // Stavljanje dugmadi u Swing aplikaciju. import javax.swing.*; import java.awt.*; import static ne t. m i n d v i e w . u t i l .SwingKonzola.*; public class Dugmel extends JFrame { private JButton dl = new JB ut to n( "D ug me 1"), d2 = new JB utton("Dugme 2"); public Dugmel() add (dl); add(d2); } public static void main(String[] run(new D u g m e l O , } } ///:200, 100); args) { { setLayout(new Fl ow La yo ut () );

Ovde je dodato neto novo: pre nego to se element postavi u okno sadraja, dodeljuje mu se novrasporeiva tipa FlowLayout. Rasporediva (engl. Layout manager) opisuje
n a in n a koji o k n o im p l ic itn o o d lu u je o t o m e g d e e p o s ta v iti k o n tr o ln i o b je k a t na

obrascu. Aplet obino koristi rasporediva tipa BorderLayout, ali to nam u ovom sluaju ne odgovara zato to se na taj nain (kao to ete saznati u nastavku poglavlja) svaki kontrolni objekat potpuno prekriva novim objektima u istoj oblasti. Meutim, rasporeiva

1052

Misliti na Javi

tipa FlowLayout prouzrokuje ravnomerno reanje kontrolnih objekata po obrascu, sleva udesno i odozgo nanie. Veba 4: (1) Pokaite da se bez poziva metode setL ay o u t( ) u programu D ugm el.java prikazuje samo jedno dugme.

Hvatanje dogaaja
Ako prevedete i izvrite prethodni program, primetiete da se nee deavati nita kada pritiskate dugmad. U tu svrhu morate sami da napiete kod. Sutina programiranja upravljanog dogaajima, to ini veliki deo programiranja grafikih korisnikih okruenja, jeste povezivanje dogaaja s kodom koji odgovara na njih. Ovo se u grafikoj biblioteci Swing postie jasnim razdvajanjem okruenja (grafikih kom ponenata) i realizacije (koda koji elite da izvravate kada se desi dogaaj). Svaka komponenta moe da prijavljuje dogaaje, i svaki od njih moe da prijavljuje zasebno. Dakle, ako niste zainteresovani, na primer, za to da li je pokaziva mia preao preko dugmeta, neete oslukivati taj dogaaj. To je veoma jednostavan i elegantan nain za pisanje programa voenih dogaajima. Kada shvatite osnovne pojmove, lako ete koristiti Swing komponente koje nikada niste videli. Zapravo, ovaj model se prim enjuje na sve to se moe okarakterisati kao zrno Jave (o emu ete saznati vie u nastavku poglavlja). Za poetak, usredsrediemo se na glavne ogaaje kom ponenata. Za dugme tipa JButton, glavni dogaaj je njegovo pritiskanje. Da biste objavili svoje zanim anje za dogadaj pritiskanja dugmeta, pozivate metodu addActionListener( ) klase JButton. Ta metoda oekuje argument koji realizuje interfejs ActionListener to sadri samo jednu metodu action P erform ed ( ). Dakle, da biste povezali kod s dugmetom tipa JButton, treba samo da realizujete interfejs ActionListener u klasi i da prosledite objekat te klase dugmetu metodom addA ctionListener( ). Metoda prosledenog objekta pozivae se kad god se pritisne dugme (to se obino naziva povratni poziv , engl. callback). ta bi trebalo da bude rezultat pritiskanja tog dugmeta? eleli bismo da vidimo da se neto menja na ekranu, pa e biti predstavljena nova Swing komponenta: JTextFieId. To je jednoredno polje za unos teksta, ili, u ovom sluaju, polje u kome program moe menjati tekst. Iako postoji vie naina da se napravi objekat klase JTextFieId, najjednostavniji nain je da se samo saopti konstruktoru koliko treba da bude iroko to polje. Kada se polje za tekst postavi na obrazac, njegov sadraj moete da menjate metodom setT ext( ) (klasa JTextField sadri i mnoge druge metode, ali njih morate da potraite sami u HTM L dokumentaciji na adresi http://java.sun.com). Evo kako to izglea:
//: gui/Dugme2.java // Reagovanje na pritiskanja dugmeta. import javax.swing.*; import java.awt.*; import java.awt.event.*; import static net.mindvie w . u t i 1 .S wi ngKonzola.*;

Poglavlje 22: Grafika korisnika okruenja

1053

public class Dugine2 extends JFrame { private JButton dl = new JButton("Dugme 1"), d2 = new JButton("Dugme 2"); private JTextField txt = new JT e x t F i e l d ( 1 0 ) ; class PrijemnikDugmeta implements Ac ti on Li st en er { public void actionPerformed(ActionEvent e) { String ime = ((JButton)e.getSource()) .get Te xt(); tx t. se tT ex t( im e); } } private PrijemnikDugmeta pd = new P r i j e m ni kD ug me ta (); public Dugme2() { dl.adAc ti on Li st en er( pd ); d2 .a dd Ac ti on Li st en er( pd ); setLayout(new FlowLayout()); add(dl); a d d (d2); a d d (t x t ); } public static void main(String[] args) run(new Duqme2(), 200, 150); } ! ///:{

Jenoredno polje za tekst pravi se i smeta na obrazac na isti nain kao dugme ili neka druga Swing komponenta. Gornji program se razlikuje po pravljenju pomenute klase PrijemnikDugmeta koja realizuje interfejs ActionListener. Argument metode actionPerformed( ) tipa je ActionEvent i sadri sve inform acije o dogaaju i mestu gde je nastao dogadaj. U ovom sluaju, eleo sam da opiem dugme koje je pritisnuto: metoda g etS ou rce( ) vraa objekat gde se esio dogaaj, a ja sam (eksplicitnom konverzijom) uinio da njegov tip bude JButton. Metoda g etT ext( ) vraa tekst koji se nalazi na dugmetu, i ja sam ga stavio u polje tipa JTextField kako bih dokazao da se kod zaista poziva kada se dugme pritisne. U konstruktoru se metoda addA ctionListener( ) koristi za registrovanje objekata klase PrijemnikDugmeta kao prijemnika dogaaja oba dugmeta. esto je zgodnije da interfejs ActionListener bude realizovan kao anonimna unutranja klasa, naroito zato to se obino koristi samo jedna instanca svake klase ovog tipa. Program Dugme.java moe se promeniti tako da koristi anonim nu unutranju klasu na sledei nain:
//: gui/Duame2b.java // Korienje anonimnih unutranjih klasa. import javax.swing.*; import java.awt.*; import java.awt.event.*; import static ne t. mi nd vi ew .u ti l.SwingKonzola.*;

1054

Mis/iti na Javi

public class Dugme2b extends JFrame { private JButton dl = new JButton("Dugme 1"), d2 = new JButton("Dugme 2"); private JTextField txt = new JT ex tF ie ld (1 0); private ActionListener pd = new ActionListener() public void actionPerformed(ActionEvent e) { String ime = ((JButton)e.getSource()).getText(); t x t. se tT ex t( im e); } }; public Dugme2b() { {

bl.addAc ti on Li st en er( pd ); b2.addAc ti on Li st en er( pd ); setLayout(new F l ow La yo ut () ); add(dl); add(d2); add(txt); } public static void main(String[] run(new Dugme2b(), 200, 150); } args) {

} III-U primerima iz ove knjige nadalje e se koristiti anonimne unutranje klase (kad god je to mogue). Veba 5: (4) Napiite aplikaciju uz korienje kiase SvvingKonzola. Neka sadri polje za tekst i tri dugmeta. Kada se pritisne neko dugme, u polju treba da se pojavi nekakav tekst.

Vieredna polja za tekst


Klasa JTextArea slina je klasi JTextField, ali moe da sadri vie redova teksta i da ima vie funkcija. Naroito korisna metoda je appen d ( ); pomou nje moete lako da smestite izlaz programa u polje za tekst. Poto unutar vierednog polja za tekst moete da se kreete i unazad, Swing program je poboljanje u poreenju sa onim to je dosad moglo da se postigne pomou programa s komandne linije koji ispisuju rezultate na standardnom izlaznom uredaju. Kao primer, razmotrimo sledei program koji popunjava vieredno polje za tekst izlaznim podacima generatora Countries iz poglavlja Detaljno razmatranje kontejnera:
//: gui/TextArea.java // Korienje kontrole JTextArea. import j a v a x . s w i n g .*; import java.awt.*; lmport java.awt.event.*; import j a v a . u t i 1 .*; import net.mindview.util.*; import static ne t. mi nd vi ew .u ti1 .SwingKonzola.*;

Poglavlje 22: Grafika korisnika okruenja

1055

public class TextArea extends JFrame { private JButton b = new JButton("Dodaj podatke"), c = new JButton("Obrii podatke"); private JTextArea t = new JTextArea(20, 40); private Map<String.String> m = new Ha sh Map<String,String>(); public TextArea() { // Iskoristi sve podatke: m.putAll ( C o u n t r i e s . c a p i t a l s O ) ; b.addActionListener(new ActionListener() for(Map.Entry me : m.entrySet()) t.append(me.getKey() + ": "+ m e . g et Va 1u e( )+ "\ n" ); } }); c.addActionListener(new ActionListener() t. se tText(""); } }); setLayout(new F1owLa yo ut()); add(new J S cr ol1Pa ne(t)) ; add(b); add(c); } public static void main(String[] run(new TextArea(), 475, 425); } } /// =args) { { { public void actionPerformed(ActionEvent e) {

public void actionPerformed(ActionEvent e) {

U konstruktoru sc kontejner tipa Map popunjava imenima svih zemalja i njihovih glavnih gradova. Obratite panju na to da se prijemnik tipa ActionListener za oba dugmeta pravi i dodaje bez definisanja posredne promenljive, poto se on ne poziva nigde vie u programu. Dugme Dodaj podatke formatira i dodaje sve podatke, dok dugme Obrii podatke koristi metodu setT ext( ) za uklanjanje sadraja iz polja za tekst JTextArea. Kada se polje tipa JTextArea dodaje u JFram e prozor, treba ga omotati u okno s klizaima ( JScrollPane) da bi se mogao pomerati sadraj kada se na ekranu pojavi previe teksta. To je sve to treba da uradite da biste potpuno omoguili pomeranje sadraja na ekranu. Poto sam probao to da uradim i pom ou nekih drugih alata za programiranje grafikih korisnikih okruenja, bio sam oduevljen jednostavnou i dobrim dizajnom kom ponenata kao to je JScrollPane. Veba 6 : (7 1 P r e t v o r it e znakovninizovi/TestReguIarExpression.java u interaktivan Svving program k o ji omoguuje umetanje ulaznog niza znakova u jedno vieredno tekstualno polje i regularnog izraza u jedno jednoredno tekstualno polje. Rezultate (primene regularnog izraza na umetnuti znakovni niz) prikaite u drugom vierednom tekstualnom polju.

1056

Misliti na Javi

Veba 7: (5) Napravite aplikaciju pom ou klase SwingKonzoIa i dodajte sve Swing komponente koje imaju metodu ad dA ctionListener( ). (Potraite ih u HTM L dokumentaciji na adresi http://java.siin.com. Savet: upotebite indeks.) Uhvatite njihove dogaaje i za svaki prikaite odgovarajuu poruku u polju za tekst. Veba 8: (6) Gotovo sve kom ponente biblioteke Swing izvedene su iz klase Component koja ima metodu se tC u rso r( ). Potraite je u H TM L dokumentaciji za Javu. Napiite aplikaciju i promenite pokaziva mia u neki iz klase Cursor.

Rasporeivanje elemenata
Nain na koji se komponente rasporeuju u Javi verovatno je drugaiji nego u svim ostalim sistemima za grafika okruenja koje ste koristili. Prvo, sve se obavlja programski: ne postoje ,,resursi koji upravljaju postavljanjem komponenata. Drugo, nainom na koji se komponente postavljaju na obrazac ne upravlja apsolutno pozicioniranje, ve rasporeiva koji odluuje o poloaju komponenata na osnovu redosleda kojim ih dodajete. Veliina, oblik i poloaj komponenata znatno se razlikuju od jednog rasporeivaa do drugog. Pored toga, rasporeivai se prilagodavaju dimenzijama apleta ili prozora aplikacije, pa ako se promeni dimenzija prozora, u saglasnosti s tim mogu da se promene veliina, oblik i poloaj komponenata. Klase JApplet, JFram e, JWindow, JDialog JPanel itd. mogu da sadre i prikazuju komponente. Klasa Container sadri metodu setLayout( ) koja omoguuje zadavanje rasporedivaa. U ovom odeljku prouiem o raziiite rasporeivae tako to emo postavljati dugmad (jer je to najjednostavnije). Neemo obraivati nikakve dogaaje, poto ovi primeri samo pokazuju kako se kom ponente rasporeuju.

Rasporeiva BorderLayout
Prozor podrazumevano koristi rasporeiva tipa BorderLayout. Ako ne zadate drugaije, on uzima sve objekte koje mu dodajete i smeta ih u centar, i pri tom ih proiruje sve do ivica. Ovaj rasporeiva koristi etiri granine oblasti i centralnu oblast. Kada na pano koji koristi rasporeiva BorderLayout dodate neto, moete da upotrebite preklopljenu metodu a d d ( ) iji je prvi argument konstantna vrednost. Ta vrednost moe da bude neto od sledeeg:
Borderl_ayout.NORTH BorderLayout. SOUTH BorderLayout. EAST BorderLayout.WEST BorderLayout.CENTER fvrh) |dno) (desno) (ievoj Ipopunjavanje sredine, do ostaiih komponenata ili ivica)

Poglavlje 22: Grafika korisnika okruenja

1057

Ako ne zadate oblast za postavljanje objekta, podrazumevano se koristi CENTER. Evo jednostavnog primera. Rasporeiva se ne zadaje izriito jer JFram e podrazumevano koristi rasporeiva tipa BorderLayout:
//: gu i/ Bo rderLayoutl.java // Prikazuje rasporeiva B o r d e r L a y o u t . import javax.swing.*; import java.awt.*; import static n e t. mi nd vi ew .u ti l.SwingKonzola.*; public class BorderLayoutl extends JFrame { public BorderLayoutl() { add(BorderLayout.NORTH, new JB u t t o n C ' S e v e r 1 1 )); add(BorderLayout.SOUTH, new J B u t t o n ( " J u g " ) ) ; add(BorderLayout.EAST, new JB ut t o n ( " I s t o k " ) ) ; add(BorderLayout.WEST, new J B ut to n( "Z ap ad ") ); add(BorderLayout.CENTER, new J B ut to n( "C en ta r" )); } public static void main(String[] } } ///-args) {

run(new Bo r d e r L a y o u t l (), 300, 250);

Ako se element postavlja u bilo koju oblast osim CENTER, saima se da bi se uklopio u najm anji mogui prostor du jedne dimenzije, a maksimalno se produava du druge dimenzije. Oblast CENTER, meutim, proiruje objekat u obe dimenzije da bi zauzeo sredinu.

Rasporeiva FlowLayout
Ovaj rasporeiva samo ,,reda komponente na obrazac, sleva udesno, sve dok se ne popuni jedan red, a zatim prelazi u sledei red i nastavlja redanje. Evo primera koji bira rasporeiva tipa FlowLayout i zatim postavlja dugmad na obrazac. Prim etiete da u ovakvom nainu rasporeivanja komponente zadravaju svoju ,,prirodnu veliinu. Dugme je, na primer, taman toliko da se moe prikazati znakovni niz koji sadri.
//: g u i / F l o w L a y o u t l .java // Prikazuje rasporeiva FlowLayout. import javax.swing.*; import java.awt.*; import static net. mi nd vi ew .u ti1 .SwingK on zo la .*; public class FlowLayoutl extends JFrame { public FlowLayoutl() { setLayout(new F1o w L a y o u t ()); for(int i = 0; i < 20; i++) add(new JB ut to n( "D ug me " + i));

1058

Misliti na Javi

public static void main(String[] args)

run(new FlowLayoutl(), 300, 300);


}

} III-Rasporeiva tipa FlowLayout potpuno sm anjuje sve komponente, pa su rezultati pomalo iznenaujui. Na primer, poto e natpis tipa JLabel biti veliine znakovnog niza koji sadri, pokuaj da se tekst poravna udesno nema efekta ako se koristi rasporeiva FlowLayout. Vodite rauna o tome da e rasporeiva prerasporediti komponente ukoliko promenite veliinu prozora.

Rasporeiva GridLayout
GridLayout omoguuje izradu tabele kom ponenata, a kako ih dodajete, one se smetaju sleva udesno i odozgo nanie u tabeli. U konstruktoru se zaaje potreban broj redova i kolona i one se prikazuju u jednakim proporcijam a.
//: gui/GridLayoutl.java // Prikazuje rasporeiva GridLayout. import javax.swing.*; import java.awt.*; import static ne t. mi nd vi ew .u ti l.SwingKonzola.*; public class GridLayoutl extends JFrame { public GridLayoutl() { setLayout(new Gr id La yo ut (7 ,3 )); for(int i = 0; i < 20; i++) add(new JButton("Dugme " + i )); } public static void main(String[] args) run(new Gr id L a y o u t l (), 300, 300); } {

} III-U ovom sluaju postoji 21 polje i samo 20 dugmadi. Poslednje polje je ostalo prazno zato to GridLayout ne vri preraspodelu.

Rasporeiva GridBagLayout
GridBagLayout obezbeduje potpunu kontrolu nad odluivanjem o rasporedu i formatiranju oblasti u prozoru kada mu se promeni veliina. Meutim, to je istovremeno i najsloeniji rasporeiva koji je veoma teko razumeti. Namenjen je prvenstveno
a u t o m a ts k o m g e n e ris a n ju k o d a p o m o u a la tk e za p r a v lje n je g r a fi k ih a p lik a c ija (d o b r i

vizuelni alati koriste GridBagLayout umesto apsolutnogpozicioniranja). Ako je program toliko sloen da mislite kako treba koristiti rasporeiva tipa GridBagLayout, trebalo bi da upotrebite vizuelnu alatku. Ako ba elite da saznate sve detalje o ovome, potraite ih u nekoj knjizi potpuno posveenoj grafikoj biblioteci Svving.

Poglavlje 22: Grafika korisnika okruenja

1059

Umesto rasporeivaa GridBagLayout mogli biste da upotrebite TableLayout koji nije deo biblioteke Swing ali se moe preuzeti na adresi http://java.sun.com. Ta komponenta je izgraena povrh rasporeivaa GridBagLayout i skriva najvei deo njegove sloenosti, pa uveliko pojednostavljuje njegovo korienje.

Apsolutno pozicioniranje
Apsolutan poloaj grafikih kom ponenata mogue je zadati i na sledei nain: 1 . Uklonite rasporeiva metodom Container.setLayout(null). 2 . U zavisnosti od verzije jezika, za sve komponente pozovite metodu setB ou nd s( ) ili resh ap e ( ), prosleujui jo j okvirni pravougaonik ije su koordinate izraene pikselima. To moete da uradite u konstruktoru ili u metodi p a in t( ), u zavisnosti od toga ta elite da postignete. Neke alatke za pravljenje grafikih okruenja esto koriste ovakav pristup, ali to obino nije najbolji nain za generisanje koda.

Rasporeiva BoxLayout
Poto korisnici nisu razumevali klasu GridBagLayout i teko su je primenjivali, u biblioteku Swing dodata je i klasa BoxLayout koja prua mnoge prednosti rasporedivaa GridBagLayout, a nije toliko sloena. Ovakvo rasporeivanje esto moete da koristite pri runom razmetanju elemenata (i ovde, ako program postane previe sloen, upotrebite vizuelnu alatku). Rasporediva BoxLayout om oguuje i vertikalno i horizontalno kontrolisanje poloaja komponenata, a za kontrolisanje prostora izmedu komponenata koriste se ,,podupirai i ,,lepak. Elementarne primere upotrebe rasporedivaa BoxLayout potraite u mrenim dodacima ove knjige, na adresi www.MindView.net.

Kojije pristup najbolji?


Biblioteka Swing je mona i moe da uradi mnogo toga uz samo nekoliko redova koda. Primeri u ovoj knjizi prilino su jednostavni, a radi uenja vredi ih napisati runo. Prilino mnogo se moe postii kombinovanjem jednostavnih rasporeivaa. Meutim, u odreenom trenutku, runo pisanje koda za grafiko okruenje postaje besmisleno, previe sloeno i predstavlja traenje vremena. Projektanti Jave i Swinga orijentisali su jezik i biblioteke tako da podravaju alatke za pravljenje grafikih okruenja i te alatke znatno ubrzavaju programiranje. Pod uslovom da razumete ta se deava u rasporeivau i kako da se izborite s dogaajima (to e biti objanjeno u nastavku), nije naroito vano da zaista znate sve detalje runog rasporeivanja komponenata. Dozvolite odgovarajuoj alatki da to uradi umesto vas (konano, Java je i projektovana da bi poveala produktivnost programera).

Model dogaaja grafike biblioteke Swing


U modelu dogadaja biblioteke Swing, komponenta moe da izazove dogaaj (engl.fire an event). Svaka vrsta dogaaja predstavljena je zasebnom klasom. O dogaaju se obavetava jedan prijemnik (engl. listener) ili vie njih a oni potom reaguju na njega. To znai da izvor

1060

Misliti na Javi

dogaaja i mesto na kom se on obrauje mogu da se razlikuju. Komponente grafike biblioteke Swing obino se koriste bez izmene, ali je neophodno pisati kod koji se poziva kada kom ponente izazovu dogaaj. To je odlian primer razdvajanja interfejsa i realizacije. Svaki prijem nik dogaaja je objekat klase koja realizuje odreen tip prijemnikog interfejsa. Zbog toga treba samo da napravite objekat prijemnika i da ga prijavite kom ponenti koja pokree dogaaj. Prijavljivanje se obavlja metodom addXXXListener( ) komponente iji se dogaaj obrauje, pri emu ,,X X X predstavlja tip dogaaja koji se oslukuje. itanjem imena metoda tipa addListener" lako ete zakljuiti koje se vrste dogaaja mogu obraivati. Ako pokuate da primate pogrene dogaaje, dobiete greku tokom prevoenja. U nastavku poglavlja videete da zrna Jave takoe koriste metode tipa ,,addListener za odreivanje dogaaja koje zrno moe da izazove. Prema tome, sva logika za obradu dogaaja smeta se u klasu prijemnika. Kada pravite takvu klasu, jedino ogranienje je da ona mora realizovati odgovarajui interfejs. Prijem nika klasa moe biti globalna, ali u takvim sluajevima obino su pogodnije unutranje klase. Ne samo da one logiki grupiu prijemnike klase unutar korisnikog okruenja ili klasa poslovne logike kojim a slue, nego i uvaju referencu na roditeljski objekat, to predstavlja lep nain za prevazilaenje granica oblasti vaenja klase i podsistema. U svim dosadanjim prim erim a u ovom poglavlju koristio sam model dogaaja grafike biblioteke Swing, a sada em o detaljnije razmotriti taj model.

Tipovi dogaaja i prijemnika


Sve Swing komponente sadre metode tipa ad dX X X Listen er( ) i rem oveX X X Listen er(), pa se odgovarajui tipovi prijemnika mogu doavati u sve komponente i uklanjati iz njih. Primetiete da oznaka X X X u razliitim sluajevima odreduje i argument metode, na primer: addMojPrijemnik (M ojPrijemnik m). U sledeoj tabeli nabrojani su osnovni dogaaji, prijemnici i metode, kao i osnovne komponente koje metodama ad dX X X Listen er() i rem oveX X X Listener( ) podravaju pojedine dogaaje. Uvek treba imati u vidu da se model dogaaja moe proirivati, pa ete moda naii na druge tipove prijemnika koji nisu navedeni u ovoj tabeli.
Dogaaj, prijemniki interfejs i m etode add- i removeActionEvent ActionListener addActionListenerf ) removeActionListener( ] A djustm entEvent Adjustm entListener addAdjustm entListenerf ) rem oveAdjustm entListener| ) Com ponentEvent Com ponentListener addCom ponentListener) ) rem oveCom ponentListener( ) Kom ponenta koja podrava taj dogadaj JB u tto n JList JTextField JM enu ltem i klase izvedene iz ovih, kao to su JCheckBoxM enultem , JM en u i JRadioButtonM enultem JScrollbar i sve to napravite, a realizuje interfejs Adjustable

Klasa Com ponent i klase izvedene iz nje, medu kojima su JB u tto n JCheckBox JCom boBox, Container JP a n e l JA p p let JScrollPane, \X/indow, JDialog, JFileD ialog JFram e, JLabel, JList, JScrollbar, JTextArea i JTextField

Poglavlje 22: Grafika korisnika okruenja

1061

D ogaaj, prijemniki interfejs i m etode add- i removeContainerEvent ad d C on tainerListener() rem oveC ontainerListener() FocusEvent FocusListener addFocusListener( ) rem oveFocusListener() K eyEven t KeyListener addKeyListener( ) rem oveKeyListener M ouseEvent |za pomeranje i pritiskanje tastera mia) MouseListener ad d M o u seListener() removeMouseListenerf ) M ouseEvent6 (za pomeranje i pritiskanje tastera mia) MouseMotionListener addM ouseM otionListener rem oveM ouseM otionListener( ) W in d o w E v e n t W ind o w Listener ad d W ind o w Listen er( ) remove\X/indowListener( ) Item Event ItemListener addltem Listener( ) rem oveltem Listener( ) TextEvent TextListener addTextListener( ) removeTextListener( )

Kom ponenta koja podrava taj dogaaj (nastavak) Klasa Container i klase izvedene iz nje. JScrollPane, W indow , JDialog, JFileDialog, i JFram e Klasa Com ponent i klase izvedene iz nje*.

Klasa Com ponent i klase izvedene iz nje*. Klasa Com ponent i klase izvedene iz nje*.

Klasa Com ponent i klase izvedene iz nje*.

Klasa Com ponent i klase izvedene iz nje*.

Klasa W in d o w i klase izvedene iz nje. meu kojima su i JDialog JFileD ialog i JFram e

JCheckBox, JCheckBoxM enultem , JCom boBox, JList i sve to realizuje interfejs ItemSelectable Sve toje izvedeno iz klase JTextComponent, ukljuujui i JTextArea i JTextField

Vidi se da svaki tip komponente podrava samo odredene tipove dogaaja. Ispostavlja se da je prilino teko traiti sve dogaaje koje podrava svaka komponenta. Laki pristup je da se prilagodi program PrikaziMetode.java iz poglavlja Podaci o tipu tako da prikazuje sve prijem nike ogaaja koje podrava bilo koja uneta Swing komponenta. U p o g la v lju l\nlnci o tipu o b ja n je n a je rcjlcksija a p o t o m je k o r i e n a i za lista n je m e toda odredene klase (ceo spisak nietoda ili podskup metoda u ijem se imenu nalazi zadata rezervisana re). Ovaj postupak je moan zato to automatski prikazuje svemetode
D ogaaj M o u seM o tio n E v en t ne postoji m ada izgleda kao da bi m o rao postojati. Pom eran je m ia uz dranje pritisnu tog tastera ob jed in jen o je u dogaaj M ou seE v ent, pa drugo pojavljivanje klase M ou seE v en t u tabeli n ije greka.

1062

Misliti na Javi

klase, a da ne morate da prolazite kroz hijerarhiju nasleivanja i ispitujete osnovne klase na svakom nivou. Zato ta vredna alatka tedi vreme pri programiranju: poto su imena najveeg broja metoda u Javi opisna i dugaka, traite metode ija imena sadre odreenu re. Kada pronaete metodu koja bi mogla biti ono to traite, proitajte njen opis u dokum entaciji na Webu. Meutim, do poglavlja Podaci o tipu nije se pom injala grafika biblioteka Swing, pa je alatka u tom poglavlju razvijena kao aplikacija koja se izvrava s komandne linije. Evo korisnije verzije za grafiko okruenje koja trai metode tipa ,,addListener u Swing komponentama:
//: gui/Pri kaziMetodeAddListener.java // Prikazuje metode "addXXXListener" bilo koje Swing klase. import javax.swing.*; import java.avvt.*; import java.avrt.event.*; import java.lang.reflect.*; import java.util.regex.*; import static net.mindview.util .SvvingKonzola.*; public class PrikaziMetodeAddListener extends JFrame { private JTextField ime = new JT e x t F i e l d ( 2 5 ) ; private JTextArea rezultati = new JTextArea(40, 65); private static Uzorak addListener = U z orak.compi1e("(add\\w+?Li s t e n e r \ \ (.* ? \ \ ) )"); private static Uzorak kvalifikator = Uzor ak .c om pi le (" \\ w+\ \. "); class ImeL implements A c t i onListener { public void actionPerformed(ActionEvent e) { String im = ime.getText() , t ri m( ); i f (i m.1e n g t h () == 0) return; } Class<?> vrsta; try { vrsta = Class.forName("javax.swing." + im) } catch(ClassNotFoundException ex) return; } Method[] meto de = vr st a. ge t M e t h o d s ( ) ; re zu lt at i. se tT ex t( ""); for (Method m : metode) Ma tcher matcher = addLi stener.matcher(m.toStri n g ()) ; if ( m a t c h e r . f i n d O ) rezultat i. ap pe nd (k valifikator.matcher( ma tc he r. gr ou p( l)) . r e p l a c e A U ("") + "\n"); { { rezultati,setText("Ne p o s t o j i " ) ; { re zultati.setText("Ne p o s t o j i ");

Poglavlje 22: Grafika korisnika okruenja

1063

} } } public PrikaziMetodeAddListener() ImeL imeListener = new ImeL(); ime.addActionListener(imeLi st en er ); JPanel gornji = new JPanel(); gornji .add(new JLabelC'Ime Swing klase(pritisnite Enter):'1)); go rn ji . a d d ( i m e ) ; add(BorderLayout.NORTH, gornji); add(new JScrollPane(rezultati)); // Poetni podaci i test: ime.setText("JTextArea''); imeLi stener.actionPerformed( new Ac ti o n E v e n t ( " , 0 ,"")); } public static void main(String[] args) { run(new Pr i kaziMetodeAddLis t en er (), 500,400); } } ///:{

Glavni prozor ovog programa sadri polje ime tipa JTextFiel; u njega treba da unesete ime Swing klase koju traite. Rezultati se vade pomou regularnog izraza i prikazuju u polju tipa JTextArea. Prim etiete da nema dugmai niti drugih komponenata kojima bi se naznailo kako elite da ponete pretraivanje. Razlog je to to dogaaje polja tipa JTextField nadgleda prijem nik tipa ActionListener. Kad god neto izmenite i pritisnete taster Enter, spisak se trenutno aurira. Ako polje nije prazno, njegov sadraj se koristi za pokuaj pronalaenja klase metodom C lass.forN am e( ). Ukoliko je ime nepravilno, metoda C lass.forN am e( ) nee nai klasu, pa generie izuzetak koji se hvata, a u objektu klase JTextArea prikazuje se znakovni niz ,,Ne postoji". Medutim, ako otkucate ispravno ime (vaan je i raspored velikih i malih slova), metoda Class.forN am e( ) nalazi klasu i getM ethods( ) vraa niz objekata klase Method. Ovde se koriste dva regularna izraza. Prvi, addListener, trai re add iza koje slede slova pa re Listener i spisak argumenata u zagradi. Ceo regularan izraz je u obinim zagradama (koje nisu pretvorene u izlazne sekvence), to znai da e biti pristupaan kao ,,grupa regularnog izraza kada bude pronaden. Unutar metode Im eL.A ctionPerform ed( ), svaki objekat Method prosleuje se metodi Uzorak.matcher( ), ime se pravi objekat tipa Matcher. Kada se za taj objekat tipa Matcher pozove metoda fin d ( ), ona vraa true ako pronae podudaran niz znakova i u tom sluaju pozivom metode g ro u p (l) moete odabrati prvu podudarnu grupu unutar zagrada. Taj znakovni niz jo uvek sadri kvalifikatore, pa se za n iih o v o o b a c iv a n ie u p o tr e b lia v a o b je k a t kvalifikator Uzorak, k a o u PrikaiMetode.java. Na kraju konstruktora, u ime se smeta poetna vrednost i izvrava se reakcija na dogadaj (ActionEvent) koja ispitivanje snabdeva poetnim podacima.

1064

Misliti na Javi

Ovaj program je zgodan za ispitivanje mogunosti neke Swing kom ponente. Kada znate koje dogaaje podrava odredena Swing kom ponenta, ne morate da traite nita posebno da bi ona reagovala na taj dogaaj; samo uinite sledee: 1 . Iz imena klase dogaaja uklonite re Event. Dodajte re Listener onom e to preostane. To je interfejs prijemnika koji morate da realizujete u unutranjoj klasi. 2. Realizujete pomenuti interfejs i napiete metode za obradu dogadaja. Na primer, moda elite da pratite pokrete mia; u tom sluaju treba da napiete kod za metodu m ouseM oved( ) interfejsa M ouseM otionListener. (Naravno, morate da realizujete i druge metode tog interfejsa, ali za to esto postoji preica, to ete uskoro saznati.) 3. Napravite objekat prijemnike klase iz drugog koraka. Prijavite ga svojoj komponenti metodom ije ete ime dobiti ako ispred imena prijem nika dodate add, npr. addMouseMotionListener. Evo nekih prijemnikih interfejsa:
Prijemniki interfejs i adapter ActionListener AdjustmentListener ComponentListener Com ponentAdapter M etode u interfejsu actionPerform ed(ActionEuent) adjustm entValueChanged(Adjustm ent Event) com ponentH idden(Com ponentEvent) com ponentShow n(Com ponentEvent) com ponentM oved(Com ponentEvent) com ponentResized(Com ponentEvent) com ponentAdded(ContainerEvent) com ponentRem oved(ContainerEvent) focusGained(FocusEvent) focusLost(FocusEvent) keyPressed(KeyEvent) keyReleased(KeyEvent) keyTyped(KeyEvent) mouseClicked(MouseEvent) m ouseEntered(M ouseEvent) m ouseExited(M ouseEvent) m ousePressed(MouseEvent) m ouseReleased(M ouseEvent) m ouseDragged(M ouseEvent) mouseM oved (MouseEvent) w in d o w O p e n e d (W in d o w Eve n t) w indow Closing (\X/indowEvent)
w m d o w C lo sed (\X /m d o w E ve n t)

ContainerListener ContainerAdapter FocusListener FocusAdapter KeyListener KeyAdapter MouseListener MouseAdapter

MouseMotionListener MouseMotionAdapter \X/indowListener W in dow A d apter

windowActivated(\X/indowEvent| w in do w D eacti vated (\X/indowEvent) windowlconified(\X/indowEvent) windowDeiconified(\X/indowEvent) ItemListener item StateChanged|ltem Event)

Poglavlje 22: Grafika korisnika okruenja

1065

Ovo nije sveobuhvatan spisak, elimino i zbog toga to vam model dogadaja dozvoljava da pravite sopstvene tipove dogadaja i prijemnike za njih. Zato ete esto viati biblioteke u kojim a postoje novi dogaaji, a znanje koje ste stekli itajui ovo poglavlje om oguie vam da smislite kako ete koristiti te dogaaje.

Korienje prijemnikih adaptera radi jednostavnosti


Iz gornje tabele vidi se da neki interfejsi prijem nika sadre samo jednu metodu. Oni se veoma lako realizuju, poto se to radi samo kada elite da napiete metodu koju sadre. Meutim, interfejsi prijemnika ponekad sadre vie metoda koje nije tako prijatno koristiti. Na primer, kada poelite da uhvatite pritisak na taster mia (koji nije ve uhvaen, recimo, nekim dugmetom), morate da napiete metodu m ouseC licked( ). Ali poto je MouseListener interfejs, morate da realizujete i sve ostale metode, ak i ako vam one u tom sluaju ne koriste. To ume da iznervira. Da bi se reio taj problem, neki (ali ne i svi) prijemniki interfejsi koji sadre vie od jedne metode imaju adaptere , ija imena vidite u prethodnoj tabeli. Svaki adapter obezbeuje podrazumevane prazne metode za sve metode interfejsa. U tom sluaju treba samo da izvedete klasu iz adaptera i da redefiniete samo one metode koje moraju da se promene. Na primer, tipian interfejs MouseListener koji ete koristiti izgleda ovako:
class MojPrijemnikMisa extends Mo useAdapter { public void mouseClicked } } (MouseEvent e) { // Odgovor na pritisak tastera mia...

Smisao adaptera i jeste da olaka pravljenje prijemnikih klasa. Ipak, adapteri imaju i jedan nedostatak. Pretpostavimo da ste nasledili klasu MouseAdapter na ovaj nain:
class MojPrijemnikMisa extends Mo useAdapter { public void MouseClicked } } (MouseEvent e) { // Odgovor na pritiskanje tastera mia...

Ovo ne radi, a vi ete izludeti pokuavajui da otkrijete razlog. Sve se lepo prevodi i izvrava, osim to nakon pritiska na taster mia vaa metoda nee biti pozivana. Vidite li ta je problem? Ime metode: navedeno je M ouseClicked( ) umesto m ouseC licked( ). Malo zanemarivanje veliine slova prouzrokuje dodavanje potpuno nove metode. Meutim, to nije metoda koja se poziva kada se prozor zatvara, pa neete dobiti eliene rezultate. I pored n e p o g o d n o s ti, in te r ie js e g a r a n t o v a ti d a su m e to d e p r a v i ln o re a liz o v a n e .

Izvesno ete reefinisati odredenu metodu ukoliko u gornjem kodu upotrebite ugraenu anotaciju @Override. Veba 9: (5) Na osnovu programa PrikaziMetodeAddListener.java napiite program sa svim funkcijama programa podaciotipu.PrikaziM etode.java.

1066

Misliti na Javi

Praenje vie dogaaja


Da bismo dokazali da se ovi dogadaji zaista deavaju, napisaemo program koji prati dodatno ponaanje dugmeta JButton (a ne samo to da li je pritisnuto ili nije). Ovaj primer pokazuje i kako da sopstveno dugme izvedete iz klase JB u tton .7 Klasa MojeDugme je unutrania klasa u klasi PratiDogadjaj, pa MojeDugme ima pristup roditeljskom prozoru i moe da radi s njegovim poljim a za tekst, to je neophodno da bi statusne informacije mogle da se upisuju u polja roditelja. Naravno, ovo reenje ima ogranienja, poto objekat te klase moe da se koristi samo zajedno sa objektom klase PratiDogadjaj. Ovakva vrsta koda ponekad se naziva visoko spregnuta (engl. highly coupled):
//: gu i/ Pr at iD og ad ja j.java // Prikazuje dogaaje koji se deavaju. import javax.swing.*; import java.awt.*; import java.awt.event.*; import ja va .u ti l.*; import static net.mindview.util .SwingKonzola.*; public class PratiDogadjaj extends JFrame { private HashMap<String, JTextField> h = new HashMap<String, JT e x t F i e l d > ( ) ; private String[] dogadjaj = { "focusGained", "keyReleased", "focusLost", "keyTyped", "keyPressed", "m ou se C l i c k e d " ,

"mouseEntered", "mouse Ex it ed "," m o u se Pr es se d", "mouseReleased", }; private MojeDugme dl = new MojeDugme(Color.BLUE, d2 = new MojeDugme(Color.RED, class MojeDugme extends JButton { void prijavi(String polje, String poruka) (h.g e t (polj e ) ) ,setText(poruka); } FocusListener fl = new FocusListener() { { "testl"), "test2"); "m ou se Dr ag ge d", "mouseMoved"

public void focusGained(FocusEvent e) { prijavi ("focusGained", e . p a r a m S t r i n g O ) ; } public void focusLost(FocusEvent e) { prijavi ("focusLost", e . p a r a m S t r i n g O ) ; } }; KeyListener kl = new KeyListener() { {

public void keyPressed(KeyEvent e) }

pr ij av i( "k ey Pr es se d", e . pa ramStrin g ( ) ) ;

U Javi 1.0/1.1 ra)'ebilo m ogue izvesti korisne klase iz dugm eta. To je bio sam o jedan od m nogih velikih propusta u projektovanju.

Poglavlje 22: Grafika korisnika okruenja

1067

public void keyReleased(KeyEvent e) } public void keyTyped(KeyEvent e) {

prijavi ("keyReleased", e . p a r a m S t r i n g O ) ;

prijavi ("keyTyped", e . p a r a m S t r i n g O ) ; } }; MouseListener ml = new MouseListener() {

public void mouseClicked(MouseEvent e) { pri javi ("mouseCl i cked", e . p a r a m S t r i n g O ) ; } public void mouseEntered(MouseEvent e) { pri javi ("mouseEntered", e . p a r a m S t r i n g O ) ; } public void mouseExited(MouseEvent e) { prijavi ("mouseExited", e . p a r a m S t r i n g O ) ; } public void mousePressed(MouseEvent e) { pri javi ("mousePressed", e . p a r a m S t r i n g O ) ; } public void mouseReleased(MouseEvent e) { pri javi (" mo useReleased", e . p a r a m S t r i n g O ) ; } }; Mo us eMotionListener mml = new MouseMotionListener() public void mouseDragged(MouseEvent e) } public void mouseMoved(MouseEvent e) { prijavi ("mouseMoved", e . p a r a m S t r i n g O ) ; } }; public MojeDugme(Color boja, String natpis) su pe r( na tp is ); se tB ac kg ro un d(boja); addFocusLi sten er ff1 ) ; addKeyLi stener(kl); addMouseLi stener(ml); addMouseMoti onLi stener (m ml) ; } } public Prat iD og ad ja j() { setLayout(new G r i d La yo ut (d og ad ja j.1ength + 1, 2)); for(String dgd : dogadjaj) t.setE di ta bl e( fa ls e); add(new JL ab e l ( d g d , J L a b e l .RIGHT)); add(t); { JTextField t = new JTextField(); { { pri javi ("mouseDragged", e . p a r a m S t r i n g O ) ; {

1068

Misliti na Javi

h.put(dgd, t ) ; 1 add(dl); add(d2); } public static void main(String[] } 1 ///=args) {

run(new P r at iD og ad ja j(), 700, 500);

U konstruktoru klase MojeDugme boja dugmeta se zadaje metodom SetBackground(). Prijemnici su instalirani preko poziva metoda. Klasa PratiDogadjaj sadri mapu tipa HashMap. Ona povezuje znakovne nizove koji predstavljaju tip dogaaja i polja tipa JTextField u kojima se nalaze informacije o dogadaju. Naravno, ova polja su mogla da budu i statika, ali sloiete se da se ovako mnogo lake koriste i m enjaju. Poseban je sluaj ako dodajete ili uklanjate nov tip ogaaja u objektu klase PratiDogadjaj: treba samo da dodate ili uklonite znakovni niz iz niza dogadjaj; sve ostalo se odvija automatski. Metodi p rija v i( ) prosleduje se iine dogaaja i parametarski znakovni niz iz dogaaja. Ova metoda koristi HashMap mapu h iz spoljanje klase da bi pronala tekstualno polje povezano s im enom dogaaja, a zatim smeta parametarski znakovni niz u to polje. Ovaj prim er je koristan poto moete da vidite ta se u programu stvarno deava sa dogadajima. Veba 10: (6) Napravite aplikaciju uz pomo klase SwingKonzola s dugmetom tipa JButton i poljem tipa JTextField. Napiite i poveite odgovarajui prijem nik koji u polju JTextField prikazuje sadraj dugmeta kada je ono u fokusu. Veba 11: (4) Izvedite nov tip dugmeta iz klase JButton. Kad god se to dugme pritisne, trebalo bi da promeni boju u neku nasumino izabranu. Primer kako da generiete nasum inu boju potraite u programu ObojeneKutije.java u nastavku poglavlja. Veba 12: (4) Obradite nov tip dogaaja u programu PratiD ogadjaj.java dodavanjem novog koda za obradu dogaaja. Moraete sami da smislite tip dogaaja koji elite da obraujete.

Primeri Svving komponenata


Sad razumete rasporeivae i model dogaaja i spremni ste da vidite kako se mogu koristiti kom ponente grafike biblioteke Swing. Ovaj odeljak ukratko prikazuje Swing komponente i njihove mogunosti koje ete verovatno najee koristiti. Primeri nisu previe obim ni, pa kod moete da iskoristite u sopstvenim programima. Im ajte u vidu sledee: 1. Lako ete videti kako svaki primer izgleda u praksi ako pregledate HTML strane koje se nalaze u paketu sa izvornim kodom za ovu knjigu (moete ga preuzeti s lokacije www.MindView.net).

2. U H TM L dokumentaciji na adresi java.sun.com opisane su sve klase i metode biblioteke Swing (ovde su prikazane samo neke).

Poglavlje 22: Grafika korisnika okruenja

1069

3 . Zbog pravila za imenovanje koja se prim enjuju na Swing dogaaje, prilino se lako pogaa kako se pie i registruje prijem nik za odredeni tip dogadaja. Upotrebite program PrikaziMetodeAddListener.java iz ovog poglavlja da biste sebi olakali traenje odreene komponente. 4 . Kada stvari postanu komplikovanije - vreme je da koristite vizuelne alatke.

Dugmad
Biblioteka Swing sadri veliki broj dugmadi razliitih tipova. Sva dugmad, polja za potvrdu, radio-dugmad, ak i stavke menija, izvedeni su iz klase A bstractButton (kojoj bi, poto obuhvata i stavke menija, bolje odgovaralo ime AbstractSelector" ili neto optije). Uskoro ete saznati kako se koriste stavke menija, a sledei primer prikazuje razliite dostupne tipove dugmadi:
//: gui/ Du gm ad .java // Razna dugmad iz biblioteke Svving. import javax.swing.*; import javax.swing.border.*; import javax.swing.plaf.basic.*; import java.awt.*; import static n e t. mi nd vi ew .u ti l.SwingKonzola.*; public class Dugmad extends JFrame ( private JButton jb = new JBut to nC 'J Bu tt on "); private BasicArrowButton up = new Ba si cA rr ow Bu tt on (BasicArrowButton.NORTH), down = new Ba si cArrowButton(BasicArr ow Bu tt on .S OU TH ), right = new Ba si cA rr ow Bu tt on (B asi cA rr ow Bu tt on .E AS T), left = new BasicArrowButton(B asi cA rr ow Bu tt on .W ES T); public Dugmad() add(jb); add(new JToggleBut to n( "J To ggl eB ut to n" )); add(new J C h e c k B o x( "J Ch ec kB ox" )); add(new JRadioBu tt on (" JR ad ioB ut to n" )); JPanel jp = new JPanel(); jp .s et Bo rd er (n ew Ti tl ed Bo rd er (" Pr av ci")); jp.add(up); jp.add(down); j p .a d d (1 e f t ) ; jp.add(right); add(jp); public static void main(String[] run(new Dugmad(), 350, 200); } args) { { setLayout(new F l ow La yo ut () );

} ///:-

1070

Misliti na Javi

Program prvo prikazuje klasu BasicArrowButton iz biblioteke javax.swing. plaf.basic, a zatim razne tipove dugmadi. Kada izvrite program, videete da dugme za ukljuivanje i iskljuivanje zadrava svoj poslednji poloaj (ukljueno ili iskljueno). Ali polja za potvrdu i radio-dugmad ponaaju se jednako - mogu da budu potvreni ili nepotvreni (izvedeni su iz klase JToggleButton).

Grupe dugmadi
Ako elite da radio-dugmad iskljuuju jedno drugo, morate ih smestiti u grupu dugmadi. Meutim, kao to prikazuje sledei primer, svako dugme tipa AbstractButton moe se dodati grupi tipa ButtonGroup. Da bi se izbeglo esto ponavljanje koda, u ovom primeru koristi se refleksija za pra vljenje grupa dugmadi razliitih tipova. To se vidi u metodi n ap rav iP an o( ) koja pravi grupu dugmadi i objekat klase JPanel. Drugi argument metode n apraviP ano( ) jeste niz objekata tipa String. Za svaki element tog niza na pano se dodaje dugme, a tip dugmadi odreuje prvi argument metode:
//: giri/GrupeDugmadi.java // Primenjuje refleksiju za pravljenje grupa // razliitih tipova dugmadi AbstractButton. import javax.swing.*; import javax.swing.border.*; import java.awt.*; import java.lang.reflect.*; import static n e t .m in dview.uti1 .SwingKonzola.*; public class GrupeDugmadi extends JFrame { static String[] ids = { "Miki", "Silja", "Pluton", " G a j a " , " R a j a " , "Vlaja", h static JPanel napraviPano( Class<? extends Ab st ra ct Bu tt on > vrsta, String[] ButtonGroup bg = new B u t t o n G r o u p O ; JPanel jp = new JPanel(); String naslov = v r s t a . g e t N a m e O ; naslov = nasl o v . substring(nasl ov. 1 a s t l n d e x 0 f ( ' . 1) + 1); j p . setBorder(new T i tl ed Bo rd er (n as lo v)); for(String id : ids) try { // Pozivanje dinamikog konstruktora // koji prihvata argument tipa String: Co ns tructor ctor = vr st a. ge tC on st ru ct or( St ri ng .c la ss ); .// Pravi nov objekat: ab = (Abstrac tB ut to n) ct or. ne wI ns ta nc e( id ); ) catch(Exception ex) { Sy st em .e rr .p ri nt ln ("N e mogu da napravim" + vrsta); { AbstractButton ab = new J B u t t o n ( " n e u s p e n o " ) ; ids) {

Poglavlje 22: Grafika korisnika okruenja

1071

bg.add(ab); jp.add(ab); } return jp; } public G r u p e D u g m a d i () { setLayout(new Flow La yo ut () ); add(makeBPanel(JButton.class, ids)); ids)); ids)); add(makeBPanel(JToggleButton.class, add(makeBPanel(JCheckBox.class, } public static void main(String[] } } ///:-

add(makeBPanel(JRadioButton.class, i d s)); args) {

run(new G r u p e D u g m a d i (), 500, 350);

Naslov se pravi na osnovu imena klase, uz izbacivanje svih informacija o putanji. Na poetku je dugme ab tipa AbstractButton u stvari JButton sa oznakom ,,neuspeno, pa ete lako uoiti eventualne probleme, ak i ako zanemarite poruke o izuzecima. Metoda getC onstructor( ) vraa objekat tipa C onstructor koji opisuje konstruktor ija lista argumenata odgovara nizu tipova u spislcu ldasa prosleenom toj metodi. Nakon toga treba samo da pozovete metodu newlnstance( ) kojoj ete proslediti niz objekata koji sadri stvarne argumente; u ovom sluaju, samo objekte tipa String iz niza ids. Da biste postigli iskljuivo" ponaanje dugmadi, napravite grupu dugmadi i dodajte jo j svu dugmad koja tako treba da se ponaaju. Kada izvrite program, videete da svi tipovi dugmadi osim JButton realizuju iskljuivo" ponaanje.

Ikonice
Klasu Icon moete da koristite unutar natpisa tipa JLabel ili bilo koje klase koja se izvoi iz klase AbstractButton (ukljuujui JButton, JCheckBox, JRadioButton i razne vrste JM enuItem ). Korienje ikonica s klasom JLabel sasvim je jednostavno (kasnije ete videti prim er). U sledeem primeru istrauju se drugi naini korienja ikonica s dugmadima i objektim a koji su iz njih izveeni. Moete da koristite proizvoljne datoteke u formatu GIF, a one koje se upotrebljavaju u ovom primeru nai ete u paketu sa izvornim kodom, dostupnom na adresi www.MindVicw.net. Da biste otvorili datoteku i prikazali sliku, napravite objekat klase Im agelcon i prosledite mu ime datoteke. Nakon toga, moi ete u programu da koristite dobijeni objekat tipa Icon.
//: g u i / L i k o v i .java

j j Ponaanje lkonica na duyraadima tipa JButton.


import javax.swing.*; import java.awt.*; import java.awt.event.*; import static net. mi nd vi ew .u ti1 .Sw in gK o n z o l a . * ;

1072

Misliti na Javi

public class Likovi extends JFrame { private static Icon[] likovi; private JButton jb, jb2 = new JB ut to nC ' O n e m o g u c i "); private boolean besan = false; public Likovi() { { likovi = new Icon[]

new ImageIcon(getClass() .getResourceC'Face0.gif")) new Imagel co n( ge tC Ia ss().getResource("Facel.gi f ")) new ImageIcon(getClass().getResource("Face2.gif")) new Imag el co n( ge tC la ss().ge t R e s o u r c e ( " F a c e 3 .g i f ")) new ImageIcon(getClass(),getResource("Face4.gif")) }; jb = new JButton("JButton", setLayout(new F l ow La yo ut () ); jb .a ddActionListener(new ActionListener() if(besan) { { public void ac tionPerformed(ActionEvent e){ jb.setlcon(likovi[0]); besan = false; } else { jb.setlcon (1 i kcvi [0]); besan = true; } jb.s et Ve rt ic al A1 ig nme nt (J Bu tt on .T OP ); j b .s et Ho ri zo nt al A1ign m e n t ( J B u t t o n .L E F T ) ; } }); j b . s e t Ro ll ov er En ab led (t ru e); jb.setRolloverIcon(likovi [1]); jb.setPressedIcon(likovi [2]); jb.setDi sabledIcon(li k o v i [4]); j b .s et To ol Ti pT ex t( "Ja o! "); add(jb); jb 2. ad dA ct io nL is te ner (n ew ActionListener() if(jb.isEnabled()) { { public void actionPerformed(ActionEvent e ) { jb .s et E n a b l e d ( f a l s e ) ; j b 2 . s e tT ex t( "D oz vo li"); } else { jb .s et En a b l e d ( t r u e ) ; j b 2 . se tT ex t( "0 ne mo guc i"); } } }); add(jb2); } public static void main(String[] run(new Likovi(), 250, 125); args) { likovi[3]),

Poglavlje 22: Grafika korisnika okruenja

1073

O bjekat klase Icon moe se koristiti u m nogim konstruktorim a grafikih elemenata, ali za dodavanje ili uklanjanje ikonice moete da koristite i metodu se tIco n ( ). Ovaj primer pokazuje i kako dugme tipa JButton (ili bilo koji objekat klase AbstractButton) moe da prikazuje razliite vrste ikonica u zavisnosti od dogaaja: kada se pritisne, iskljui ili kada se pokaziva mia zadri iznad njega bez pritiskanja. Videete da se tako dobija zanimljivo, animirano dugme.

Priruni saveti
U prethodnom primeru dugmetu je dodat i priruni savet (engl. tool tip). Gotovo sve klase grafikih elemenata izvedene su iz klase JCom ponent koja sadri metodu setToolTipText(String). Zbog toga za doslovno sve to stavite na obrazac, treba samo da napiete (za objekat jc bilo koje klase izvedene iz klase JC om ponent):
jc.setToolTipText("Moj sa v e t " ) ;

i kada se pokaziva mia zadri iznad objekta klase JCom ponent zadato vreme, pored pokazivaa e se pojaviti malo polje s vaim tekstom.

Jednoredna polja za tekst


Ovaj primer prikazuje dodatno ponaanje polja za tekst tipa JTextField:
//: g u i/ Po lj aZ aT ek st.java // Polja za tekst i Javini import javax. sw in g.*; import j a v a x . s w i n g .e v e n t .*; import javax.swing.text.*; import java.awt.*; import java.awt.event.*; import static net.mindview.uti1 .SwingKonzola.*; public class PoljaZaTekst extends JFrame { JButton dl = new JButton("Uzmi private JTextField tl = new J T e x t F i e l d (30), t2 = new J T e x tF ie ld (3 0), t3 = new J T ex tF ie ld (3 0); private String s = private DokumentSVelikimSlovima dvs = new D o k u m e nt SV el ik im Sl ovi ma (); publi c PoljaZaTek () { t 1.se tD oc um en t( dv s); dvs.addDocumentl_istener(new T1 ()); dl.addflctionListener(new B1 ()) ; d2 .addActionListener(new B 2 ()); tekst"), d2 = new JButton("Zadaj tekst"); dogaaji.

1074

Mis/iti na Javi

tl.addActionListener(new T I A ()); setLayout(new FlowLa yo ut () ); add(dl); add(d2); add(tl); add(t2); add(t3); ) class T1 implements DocumentListener { public void changedllpdate(DocumentEvent e){) public void insertUpdate(DocumentEvent e){ t 2 .s et Te xt(tl.getText()); t3.setText("Tekst: "+ tl .g e t T e x t ( ) ) ; } public void removeUpdate(DocumentEvent e){ t 2 .s et Te xt(tl.getText()); } } class TIA implements Ac ti on Li st en er { private int broj = 0; public void actionPerformed(ActionEvent e) { t3.setText("tl dogadjaj " + broj++); } } class B1 implements Ac tionListener { public void actionPerformed(ActionEvent e) { if(tl.getSelectedText() == null) s = tl .g et Te xt (); el se s = tl .g et Se le ct ed Te xt (); tl.set Ed it ab le (t ru e); } } class B2 implements Ac tionListener { public void actionPerformed(ActionEvent e) { dvs.zadajP re tv ar an je( fa ls e); tl.setText("Ubaceno dugmetom 2: " + s ) ; dvs.zada jP re tv ar an je( tr ue ); tl.s et Ed it ab le (f al se); } } public static void main(String[] } ) class DokumentSVelikimSlovima extends PlainDocument { boolean pretvaranje = true; args) {

run(new Po lj aZ aT ek st (), 375, 200);

Poglavlje 22: Grafika korisnika okruenja

1075

public void zadajPretvaranje(boolean indikator) pretvaranje = indikator;

} public void insertString(int offset, String str, AttributeSet attSet) throws BadLocationException { if(pretvaranje) string = s t ri ng .t oU pp er Ca se (); super.insertString(offset, string, at tr ib ut eS et ); } } ///:-

Polje t3 tipa JTextFieId slui za izvetavanje o dogaajima polja t l tipa JTextFieId. Videete da se prijemnik dogaaja za polje JTextField pokree samo kada pritisnete taster Enter. S poljem t l povezano je nekoliko prijemnika. T1 je tipa Docum entListener i odgovara na sve promene ,,dokumenta (u ovom sluaju, sadraja polja). On automatski kopira sav tekst iz t l u t2. Pored toga, dokument polja t l je tipa DokumentSVelikimSIovima koji je izveden iz klase PlainDocument, a pretvara sva slova u velika. Automatski se otkrivaju znakovi za brisanje, brie sadraj i pomera pokaziva, pa sve radi kao to se i oekuje. Veba 13: (3) Promenite program PoljaZaTekst.java tako da znakovi u t2 zadravaju prvobitni raspored malih i velikih slova kako su bila unesena, umesto da se automatski pretvaraju u velika slova.

Ivice
Klasa JComponent sadri metodu se tB o rd e r( ) koja om oguuje postavljanje zanimljivih ivica oko svih vidljivih komponenata. Sledei primer prikazuje razne ivice - metodom p rik azilvicu ( ) pravi se objekat klase JPanel i na njega postavlja ivica. Prim enjuje se i tehnika prepoznavanja tipa tokom izvravanja da bi se ustanovilo ime ivice koja se koristi (uz izbacivanje svih informacija o putanji), a zatim se to ime stavlja u objekat klase JLabel, u sredinu panoa.
//: gui/Ivice.java // Razne ivice iz biblioteke Swing. import javax.swing.*; import javax.swing.border.*; import java.awt.*; import static ne t. mindview.uti1 .SwingKonzola.*; public class Ivice extends JFrame { static JPanel p r ika ziIvicu(Border b) { JPanel jp = new JPanel(); jp .s e t L a y o u t (new B o r d er La yo ut()); String nm = b . g e t C l a s s O . t o S t r i n g O ; nm = nm.substring(nm. I a st ln de x0 f( 1. 1) + 1); jp.add(new J L a b e l (nm, J L a b e l .C E N T E R ) , Bo rd er La yo ut .C EN TE R);

1076

Misliti na Javi

jp.setBorder(b); return jp;


} public Ivice() { setLayout(new G r i d L a y o u t ( 2 , 4 ) ) ; add(prikazilvicu(new Ti t l e d B o r d e r ( " N a s l o v " ) ) ) ; add(prikazilvicu(new E t c h e d B o r d e r ( ) ) ) ; add(pri kaziIvi cu(new Li n e B o r d e r ( C o l o r .B L U E ) )); add(prikaziIvicu( new M a t t e B o r de r( 5, 5, 30 ,30 ,C ol or .G RE EN )) ); add(prikazilvicu( new Bevel B o r d e r ( B ev el Bo rd er .RA IS ED ))); add(prikazilvicu( new SoftBevel Border(Bevel B o r d e r .L 0W ER ED ))); add(pri kaziIvicu(new CompoundBorder( new Et ch ed Bo rd er (), new L i n e Bo rd er (C ol or .R ED) )) ); } public static void main(String[] run(new Ivice(), 500, 300); } } ///:args) {

Moete da napravite i sopstvene ivice koje ete staviti oko dugmadi, natpisa ili bilo ega to je izvedeno iz klase JCom ponent.

Mali program za ureivanje teksta


Klasa JTextPane obezbeduje najvei deo podrke za obradu teksta bez mnogo truda. Sledei primer ilustruje veoma jednostavnu upotrebu, bez mnogih funkcija ove klase:
//: gui/TekstualniPano.java // Klasa JTextPane kao mali program za ureivanje teksta. import javax.swing.*; import java.awt.*; import java.awt.event.*; import net.mindview.util.*; import static ne t. m i n d v i e w . u t i l .SwingKonzola.*; public class TekstualniPano extends JFra me { private JButton b = new JButton("Dodaj tekst"); private JTextPane tp = new JTextPane(); private static Ge nerator sg = new R a n d o m Ge ne ra to r.S t r i n g (7); public Teks tu aln i P a n o () { b.addActionListener(new ActionListener() for(int i = 1; i < 10; i++) tp.setText(tp.getText() + sg.next() + "\n"); { public void ac ti on Pe rf or me d( Ac tio nE ve nt e ) {

Poglavjje 22: Grafika korisnika okruenja

1077

}); add(new JScrollPane(tp)); add(BorderLayout.SOUTH, b ) ; } public static void main(String[] } } ///:args) {

run(new Te ks t u a l n i P a n o ( ) , 475, 425);

JTextPane tr e b a d a ap p en d ( ). U o v o m s lu a ju ( p r i z n a j e m , b e d n o u o d n o s u n a m o g u n o s t i k lase JTextPane),tekst m o r a d a se p r o i t a , p r o m e n i i v r a ti n a z a d u p a n o p o m o u m e to d e setT ext( ). P o d r a z u m e v a n i r a s p o r e iv a z a JFram e je s te BorderLayout i o n m u d o d a je e le m e n te . P o to je JTextPane d o d a t u p a n o JScroIlPane b e z z a d a v a n ja o b la s ti, o n se p o s ta v lja u c e n t a r p a n o a i r a z v la i p r e m a iv ic a m a . D u g m e je d o d a t o n a d o le ( u o b la s t SOUTH), p a e se u k lo p iti u tu o b la s t. U o v o m s lu a ju , d u g m e e se u g n e z d iti n a d n u e k r a n a . O bratite panju na ugraene mogunosti klase JTextPane kao to je automatsko prelamanje redova. Postoje i brojne druge funkcije o kojima moete mnogo saznati iz dokum entacije razvojnog programskog paketa JDK.
D u g m e s a m o d o d a je s lu a jn o g e n e r is a n te k s t u p o lje . P o lje tip a o m o g u i o b r a d u te k s ta n a lic u m e s t a , p a e te p r i m e t i t i d a n e p o s to ji m e to d a

Veba 14: (2) Promenite program TekstualniPano.java tako da koristi polje tipa JTextArea umesto JTextPane.

Polja za potvrdu
Polje za potvrdu om oguuje pravljenje opcije koja moe da bude potvrdena ili nepotvrena; sastoji se od malog polja i oznake. Polje obino sadri malo x (ili neto drugo to pokazuje da je opcija potvrena) ili je prazno, u zavisnosti od toga da li je stavka koju oznaava potvrena ili nije. O bjekat klase JCheckBox obino se pravi pom ou konstruktora iji je argument natpis. Stanje polja moete da itate i zadajete, ali moete i da itate i menjate njegov natpis nakon pravljenja. Kad god se polje tipa JCheckBox potvrdi ili se potvrda ukloni, nastaje dogaaj koji moete da uhvatite na isti nain kao dogaaj dugmeta, pomou objekta klase ActionListener. U sledeem primeru koristi se tekstualno polje za brojanje potvrenih polja:
//: gui/PoljaZaPotvrdu.java // Korienje klase JCheckBoxes. import javax.swing.*; import java.awt.*; iinport j a v a , a w t .e v e n t . * ; import static ne t. mi nd vi ew .u ti1 .SwingKonzola.*; public class PoljaZaPotvrdu extends JFrame { private JTextArea t = new JTextArea(6, private JCheckBox 15);

1078

Misliti na Javi

cbl = new JCheckBox("Polje za potvrdu 1"), cb2 = new JCheckBox("Polje za potvrdu 2"), cb3 = new JCheckBox("Polje za potvrdu 3"); public PoljaZaPotvrdu() { { cbl.addActionListener(new ActionListener() obradi("l", c b l ) ; ) )); cb2.addActionListener(new A c t i o n L i s t e n e r O obradi("2", cb2); ) }); cb3.addActionListener(new ActionListener() obradi("3", cb3); ) }); setLayout(new F l ow La yo ut () ); add(new J S cr ol1Pa ne (t )); ad d( cb l); ad d ( c b 2 ) ; ad d ( c b 3 ) ; } private void o b r a d i (String b, JCheckBox cb) if(cb.isSelected()) t.append("Polje " + b + " je po t v r e n o \ n " ) ; else t.append("Polje " + b + " nije po t v r d e n o \ n " ) ; } public static void main(String[] } } ///:args) { { { {

public void ac tionPerformed(ActionEvent e){

public void ac tionPerformed(ActionEvent e){

public void ac tionPerformed(ActionEvent e ) {

run(new Polj a Z a P o t v r d u ( ) , 200, 300);

Metoda o b ra d i( ) dodaje ime polja i njegovo tekue stanje u sadraj vierednog polja za tekst metodom ap p en d ( ), pa e se prikazati zbirni spisak odabranih polja i njihovih stanja. Veba 15: (5) Dodajte polje za potvrdu u aplikaju napravljenu u vebi 5, uhvatite dogaaj i umetnite drugaiji tekst u polje.

Radio-dugmad
Ime radio-ugme u programiranju grahkih korisnikih okruenja izvedeno je asocijacijom na radija iz starinskih autom obila koji su imali mehaniku dugmad. Kada bi se jedno dugme pritisnulo, iskakalo bi dugme koje je ranije bilo pritisnuto. Na taj nain mogla je da se odabere samo jedna mogunost.

Poglav[je 22: Grafika korisnika okruenja

1079

Da biste napravili povezanu grupu dugmadi tipa JRadioButton, treba samo da ih dodate u grupu tipa ButtonGroup. (U obrascu moe da postoji proizvoljan broj grupa dugmadi.) Jednom dugmetu moe da se (drugim argumentom konstruktora) zada poetno stanje tako da bude potvreno. Ako vie radio-dugmadi u grupi ukljuite konstruktorom, poslednje dugme e iskJjuiti sva prethodna. Evo jednostavnog prim era korienja radio-dugmadi. O bratite panju na to da se dogaaji radio-dugmadi obrauju isto kao i svi drugi dogaaji:
//: gui/Radiougmad.java // Korienje klase JRadioButtons. import javax.swing.*; import java.awt.event.*; import java.awt.*; import static ne t. m i n d v i e w . u t i l .SwingKonzola.*; public class RadioDugmad extends JFrame { private JTextField t = new J T e x t F i e l d ( 1 5 ) ; private ButtonGroup g = new B u t t o n G r o u p O ; private JRadioButton rbl = new JRadioButton("jedan", false), rb2 = new JR a d i o B u t t o n ( " d v a " , false), rb3 = new J R a d io Bu tt on (" tr i", false); private Ac tionListener al = new ActionListenerf) public vond actionPerformed(ActionEvent e) { t.setText("Radio-dugme " + ((JRadi o B u t to n) e. ge tS ou rc e()).g e t T e x t ()); 1 h public R a d i o D u g m a d O { {

rbl.addActionLi s t e n er (a l); r b 2. ad dA ct io nL is te ner (a l); r b 3 .addActionLi sten er (a l); g.add(rbl); g.add(rb2); g.add(rb3); t . se tE di ta bl e( fa ls e); setLayout(new Fl ow La yo ut () ); add(t); add(rbl); add(rb2); add(rb3); } public static void main(String[] args) run(new Ra di oD ug ma d( ), 200, 125); } {

Z a p r ik a z iv a n je s ta n ja k o ris ti se te k s t u a ln o p o lje . M e n ja n je s a d r a ja to g p o lja je is k lju e n o , z a to to se u n j e m u p r ik a z u ju p o d a c i a n e u n o s e se u n je g a . T a k v o te k s tu a ln o p o lje je a lt e r n a tiv a z a n a tp is e tip a

JLabel.

1080

Misliti na Javi

Kombinovane liste (padajue liste)


Poput grupe radio-dugmadi, padajua lista omoguava da se korisnik navede da izabere samo jedan element iz grupe mogunosti. Meutim, lista je kom paktnije reenje da se to postigne, a elemente liste je lake m enjati tako da se korisnik ne iznenadi. (Radio-dugmad moete menjati dinamiki, ali to ume i da smeta.) Klasa JCom boBox podrazumevano se ne ponaa kao kombinovana lista u Windowsu koja omoguuje da korisnik odabere stavku iz liste ili da u nju sam unese neku vrednost, ako nije zadovoljan ponuenim opcijama. Da biste omoguili korisnicim a da unose vrednosti u objekat tipa JCom boBox, potrebno je da pozovete metodu setEditable. Iz liste tipa JCom boBox korisnik moe odabrati jedan i samo jedan element. U sledeem primeru, polje tipa JCom boBox na poetku sadri odreen broj stavki, a zatim se nakon pritiskanja dugmeta u polje dodaju novi elementi.
//: gui/PadajuceListe.java // Korienje padajuih lista. import javax.swing.*; import java.avvt.*; import java.awt.event.*; import static net.mi nd vi ew .u ti l.SwingKonzola.*; public class PadajuceListe extends JFrame { private S t ri ng[] opis = { "ivahan", "Dosadan", "Tvrdoglav", "Sjajan", "Uspavan", "Iskvaren" }; "Zastraujui" , "Ispunjen",

private JTextField t = new J T ex tF ie ld (1 5); private JComboBox c = new JComboBox(); private JButton b = new JButton("Dodaj elemente"); private int broj = 0; public P a d a j u c e L i s t e O { for(int i = 0; i < 4; i++) c. addItem(opis[broj++]); t. se tE di ta bl e( fa ls e); b.addActionListener(new ActionListener() if(broj < opis.length) c.addItem(opi s[ broj++]); } }); c.addActionListener(new ActionListenerf) t.setText("indeks: } { " + { public void actionPerformed(ActionEvent e ) {

public void actionPerformed(ActionEvent e){ "+ c.getSelectedIndex() + 1 1 ((JComboBox)e.getSource()).ge tS el ec te dl te m( )) ;

1);
setLayout(new F l ow La yo ut () ); add(t); add(c);

Poglavjje 22: Grafika korisnika okruenja

1081

add(b); } public static void main(String[] } args) {

run(new Padaju ce Li st e( ), 200, 175);

} III-Tekstualno polje prikazuje odabrani indeks (redni broj elementa koji je trenutno izabran u nizu), kao i tekst stavke koja je odabrana u kom binovanoj listi.

Grafike liste
Polja tipa JList prilino se razlikuju od polja tipa JCom boBox, i to ne samo po izgledu. Za razliku od polja tipa JCom boBox koje pada kada ga aktivirate, JList uvek zauzima tano odreen broj redova na ekranu i ne m enja se. Ako elite da vidite elemente liste, samo pozovite metodu getSelectedValues( ) koja vraa niz izabranih stavki. Lista JList omoguuje viestruki izbor: ako uz pritisnut taster Ctrl miem pritisnete vie od jedne stavke, stavka koj u ste prvu izabrali ostae istaknuta, a nakon toga moete da izaberete jo proizvoljan broj elemenata. Ukoliko izaberete neki element, a zatim uz pritisnut taster Shift miem pritisnete jo jedan element, bie izabrani svi elementi izmeu njih. Da biste uklonili stavku iz izabrane grupe, pritisnite je miem, istovremeno drei pritisnut taster Ctrl.
//: g u i/ Lis t a .java import javax.swing.*; import javax.swing.border.*; import javax.swing.event.*; import java.awt.*; import java.awt.event.*; import static ne t. mindview.uti1 .Swing Ko nz ol a.*; public class Lista extends JFrame { private Stringf] "okolada", ukusi= { "Vanila", "Pepermint", "Jagoda",

"Moka", "Rum grozice", "Praline krem", "Pita od blata" }; private Defa ultListModel elementiListe=new De fa ul tL is tM od el();

private JList lst = new J L i s t (elemen tiL i s t e ) ; private JTextArea t = new J T e x t A r e a ( u k u s i .1e n g t h ,20); private JButton b = new JButton("Dodaj element"); private ActionListener bl = new ActionListener() public void actionPerformed(ActionEvent e) { if(broj < u k u s i .1ength) } else { // Iskljui, poto nema vie ukusa // koji bi se dodali u listu { elementiListe.add(0, ukusi [bro j+ +] ); {

1082

Misliti na Javi

b. se tE nab1ed(false); } } }; private ListSelectionListener 11 = new ListSelectionListener() { { return; public void valueChanged(ListSelectionEvent e) if (e .g et Va lu el sA dj ust in gO ) t.se tT ex t( "" ); for (Object stavke : Is t. ge t S e l e c t e d V a l u e s O ) t.append(stavke[i] + "\n"); } }; private int broj = 0; public Lista() { t.setEditable(false); setLayout(new FlowLayout()); // Pravi ivice za komponente: Border brd = BorderFactory.createMatteBorder( 1, 1, 2, 2, Co lo r. BL AC K); 1 s t .se tB or de r( br d); t.se tB or de r( br d); // Dodavanje prve etiri stavke u listu for(int i = 0; i < 4; i++) elementi Liste.addElem en t( uk us i[ br oj ++ ]); // Dodavanje stavki u pano sadraja radi prikaza add(t); ad d ( l s t ) ; add(b); // Registrovanje slualaca dogaaja 1 st.addLi stSelectionLi st en er (1 1); b.ad dA ct io nL is te ne r(b l); } public static void m a i n ( S t r i n g [] args) run(new Lista(), 250, 375); } } ///:{

Primeujete i da su listama dodate ivice. Ako elite samo da smestite niz objekata tipa String u listu tipa JList, postoji mnogo jednostavnije reenje: niz treba da prosledite konstruktoru klase JList i on e automatski napraviti listu. Model liste u gornjem primeru koristi se jedino zato da bi se lista menjala tokom izvravanja programa. Liste tipa JList ne obezbeuju automatski direktnu podrku za pomeranie sadraja po ekranu. Naravno, treba samo da stavite listu u okno tipa JScroiiP ane i njemu prepustite da se brine o detaljima. Veba 16: (5) Pojednostavite program Lista.java prosledivanjem niza konstruktoru i uklanjanjem dinamikog dodavanja elemenata u listu.

Poglavjje 22: Grafika korisnika okruenja

1083

Okno s jezicima
Klasa JTabbedPane om oguuje pravljenje okna u kom e se du gornje ivice prikazuju jezici. Pritiskom na odredeni jeziak prikazaete njegov poddijalog.
//: gui/ OknoSaJeziccima l.java // Prikazuje okno s jezicima. import javax.swing.*; import javax.swing.event.*; import java.awt.*; import static ne t, mindview.uti1 .SwingKonzola.*; public class OknoSaJeziccimal extends JFrame { private String[] ukusi = { "okolada", "Moka", "Jagoda", "Vanila", "Pepermint", "Praline krem", "Pita od blata" "Rum grozice",

};
private JTabbedPane jezicci = new JTabbedPane(); private JTextFie1d txt = new JTextField(20); public OknoSaJeziccimalO { int i = 0; for(String ukus : ukusi) jezicci.addTabfukusi [i], new JButton("0kno s jezicima" + i++)); jezicci.addChangeListener(new ChangeListener(){ public void stateChanged(ChangeEvent e) { txt.setText("Izabrani jeziak kartice: " + jezicci,getSelectedIndex());

} });
add(BorderLayout.SOUTH, txt); add(jezicci);

}
public static void main(String[] args) { run(new OknoSaJeziccimal(), 400, 250);

} } ///: Kada izvrite program, videete da JTabbedPane autom atski prenosi jezike u sledei red ako ih ima previe da bi stali u isti red. To ete vidieti kada u toku rada prom enite veliinu prozora.

Okviri za poruke
G r a f i k o o k r u e n j e o b i n o s adri s t a n d a r d a n s k u p o k v i r a za p o r u k e koji o m o g u u j u b r z o

dostavljanje informacija korisniku ili uzim anje inform acija od njega. U grafikoj biblioteci Svving, polja za poruke realizuje klasa JOptionPane. Postoje brojne m ogunosti (od kojih su neke vrlo napredne), ali najee ete koristiti dijalog poruke i dijalog za potvrdu koji se dobijaju pozivom statikih m etoda JO ptionPane.showM essageDialog( ) i JOptionPane.show C onfirm D ialog( ). Sledei primer prikazuje neke od okvira za poruke koji postoje u klasi JOptionPane:

1084

Misliti na Javi

//: gui/OkviriZaPoruke.java // Prikazuje klasu JOptionPane. import javax.swing.*; import java.awt.*; import java.awt.event.*; import static net.mindview.util.SwingKonzola.*; public class OkviriZaPoruke extends JFrame { private JButton[] b = { new JButton("Upozorenje"), new JButton("Da/Ne"), new JButton("Boja"), new JButton("Ulaz"), new JButton("3 vrednosti")

};
private JTextField txt = new JTextField(15); private ActionListener al = new ActionListener() { public void actionPerformed(ActionEvent e){ String id = ((JButton)e.getSource()) .getText(); if (id.equal sC'Upozorenje")) JOptionPane.showMessageDialog(nul1, "Imate greku!", "Hej!", JOptionPane.ERROR_MESSAGE); else if(id.equals("Da/Ne")) JOptionPane.showConfirmDialog(nul1, "ili ne", "izaberi da", JOptionPane.YES_NO_OPTION); else if(id.equals("Boja")) { Object[] opcije = { "Crvena", "Zelena" }; int izabrana = JOptionPane.showOptionDialog( null, "Izaberite boju!", "Upozorenje", JOptionPane.DEFAULT_OPTION, JOptionPane.WARNING_MESSAGE, nul 1, opcije, opcije[0]); if(izabrana != JOptionPane.CLOSED_OPTION) txt.setText("Izabrana boja: " + opcije[izabrana]); } else if(id.equals("Ulaz")) { String vrednost = JOptionPane.showInputDialog( "Koliko prstiju vidite?"); txt.setText(vrednost); } else if(id.equals("3 vrednosti")) { Object[] izbor = { "Prva", "Druga", "Treca" ); Object vrednost = JOptionPane.showInputDialog( null, "Izaberite jednu", "Ulaz", JOptionPane.INFORMATION_MESSAGE, nul 1, izbor, i zbor[0]); if(vrednost != nulI) txt.setText(vrednost.toStringO);

} }

Poglavlje 22: Grafika korisnika okruenja

1085

};
public OkviriZaPoruke() { i++) { setl_ayout(new FlowLayout()); for(int i = 0; i < b.length; b[i] . a d d A c t i o n L i s t e n e r ( a O ; a d d( b[ i]);

}
add(txt);

}
public static void main(String[] args) { run(new O k v i r i Z a P o r u k e O , 200, 200);

} } ///= Da bih napisao sam o jedan prijem nik ogaaja, koristio sam donekle rizinu proveru natpisa na dugm adim a. Pri takvoj proveri esto se pogrei u pisanju, obino u rasporedu velikih i m alih slova, a takva greka se teko uoava. Obratite panju na to da su rezultati m etoda sh ow O ption D ialog( ) i showInputDialo g ( ) objekti s vrednou koju je uneo korisnik.

Veba 17: (5) Napravite aplikaciju p o m ou klase SwingKonzola. U HTML dokum entaciji na lokaciji java.sun .com pronaite klasu JPasswordField i dodajte je u program. Ako korisnik otkuca tanu lozinku, prikaite poruku o uspehu p om ou objekta klase JOptionPane. Veba 18: (4) Prom enite program PoljaZaPotvrdu.java tako da ima zaseban prijemnik tipa ActionListener za svako dugm e (um esto da trai tekst na dugm etu).

Meniji
Svaka kom ponenta koja m oe da sadri m eni, ukljuujui JApplet, JFrame, JDialog i klase koje su iz njih izvedene, im a m etodu setJMenuBar( ). Toj m etodi se prosleuje linija menija - objekat klase JMenuBar (m oe da postoji sam o jedan objekat klase JMenuBar za odreenu k om p on entu ). M eniji tipa JMenu dodaju se na liniju menija, a objekti klase JMenuItem su stavke menija i oni se dodaju u m enije. Sa svakim elem entom tipa JMenuItem m oe da bude povezan poseban prijem nik dogaaja, i on se aktivira kada se izabere ta stavka menija. Kada koristite Javu i biblioteku Swing, m orate runo da definiete sve m enije u izvorn om kodu. Evo vrlo jednostavnog primera menija:
//: gui/JednostavniMeniji.java import javax.swing.*; impnrt java.awt.*; import java.awt.event.*; import static net.mindview.uti1.SwingKonzola.*; public class JednostavniMeniji extends JFrame { private JTextField t = new JTextField(15); private ActionListener al = new ActionListener()

1086

Misliti na Javi

public void actionPerformed(ActionEvent e) { t.setText(({JMenuItem)e.getSource()).getText());

} };
private JMenu[] meniji = { new JMenuC'Zeka"), new JMenu("Pera''), new JMenu("Ana")

};
private JMenuItem[] stavke = { new JMenuItemC'Mirko"), new JMenuItemC'Joca"), new JMenuItem("Rade"), new JMenuItem("Luka"), new JMenuItemC'Slavko"), new JMenuItem("Vlada"), new JMenuItem("Mile"), new JMenuItem("Ivan"), new JMenuItem("Nada")

};
public JednostavniMeniji () { for(int i = 0; i < stavke.length; i++) { stavkefi].addActionListener(al) ; meniji[i % 3] .add(stavke[i]);

}
JMenuBar mb = new JMenuBar(); for(JMenu jm : meniji) mb.add(jm); setJMenuBar(mb); setLayout(new FlowLayout()); add(t);

}
public static void main(String[] args) { run(new JednostavniMenij i (), 200, 150);

} } ///:Zbog korienja operatora m oulo u izrazu i%3 ravnom erno se rasporeuju stavke na tri menija. Sa svakom stavkom m ora da bude povezan prijem nik tipa ActionListener; ovde se isti prijem nik koristi svuda, ali je ob in o potreban poseban prijemnik tipa za svaku stavku. Klasa JMenuItem je izvedena iz klase AbstractButton, pa se ponaa donekle slino dugm etu. Ona obezbeuje stavku koja se m oe staviti u m eni. Postoje i tri tipa izvedena iz klase JMenuItem: JMenu za uvanje drugih stavki tipa JMenuItem (m ogue je napraviti kaskadne m enije), JCheckBoxMenuItem koja sadri polje za potvrdu to pokazuje da li je stavka m enija izabrana i JRadioButtonMenuItem koja sadri radio-dugm e. Kao napredniji prim er za pravljenje menija p o n o v o em o razmotriti razliite ukuse sladoleda. Ovaj prim er prikazuje i kaskadne m enije, preice s tastature, stavke menija s poljim a za potvrdu i nain za dinam iko m enjanje menija:
//: gui/Meniji.java // Podmeniji, polja za potvrdu u meniju, kaskadni // meniji, preice i komande za pokretanje. import javax.swing.*;

Pog!av[je 22: Grafika korisnika okruenja

1087

import java.avvt.*; import java.awt.event.*; import static net.mindview.util.SwingKonzola.*; public class Meniji extends JFrame { private String[] ukusi = { "okolada", ''Jagoda'V'Vanila", "Pepermint", "Moka", "Rum grozice", "Praline krem", "Pita od blata"

};
private JTextField t = new JTextField{"Bez ukusa", 30); private JMenuBar mbl = new JMenuBar(); private JMenu f = new JMenu("Datoteka"), m = new JMenu("Ukusi"), s = new JMenu("Bezbednost"); // A1ternativni pristup: private JCheckBoxMenuItem[] bezbednost = { new JCheckBoxMenuItem("Cuvar"), new JCheckBoxMenuItem("Sakrij")

};
private JMenuItem[] datoteka = { new JMenuItem("Otvori), }; // Druga linija menija, za zamenu: private JMenuBar mb2 = new JMenuBar(); private JMenu fooBar = new JMenu("fooBar"); private JMenuItem[] ostalo = { // Dodavanje preice za meni vrlo je // jednostavno, ali njih mogu da imaju samo // stavke JMenuItems u svojim konstruktorima: new JMenuItem("Foo", KeyEvent.Vk_F), new JMenuItem("Bar", KeyEvent.VK_A), // Nema preice: new JMenuItem("Baz"),

};
private JButton b = new JButton("Kaskadni meniji"); class BL implements ActionListener { public void actionPerformed(ActionEvent e) { JMenuBar m = getJMenuBar(); setJMenuBar(m == mbl ? mb2 : mbl); validate(); // Osveavanje slike

class ML implements ActionListener { public void actionPerformed(ActionEvent e) { JMenuItem cilj = (JMenuItem)e.getSource();


String actionCommand - c i 1j. g e t A c t i o n C o m m a n d (); if( ac tionCommand.equals ("Otvori")) {

String s = t.getText(); boolean izabrana = false;

1088

Misliti na Javi

for(String ukus : ukusi) if(s.equals(ukusi)) izabrana = true; if(lizabrana) t.setText(''Prvo izaberite ukus!"); else t.setText("Otvaranje"+ s +". Mmm, mtn!");

} } }
class FL implements ActionListener { public void actionPerformed(ActionEvent e) { JMenuItem cilj = (JMenuItem)e.getSource(); t.setText(cilj.getText()) ;

} }
^

// Moete i da napravite zasebnu klasu // za svaku stavku tipa Menultem. // Tada neete morati da otkrivate o kojem tipu se radi: class FooL implements ActionListener { public void actionPerformed(ActionEvent e) { t.setText("Izabran Foo");

} }
class BarL implements Actionlistener { public void actionPerformed(ActionEvent e) { t.setText("Izabran Bar");

} }
class BazL implements ActionListener { public void actionPerformed(ActionEvent e) { t.setText("Izabran Baz");

class CMIL implements ItemListener { public void itemStateChanged(ItemEvent e) { JCheckBoxMenuItem cilj = (JCheckBoxMenuItem)e.getSource(); String actionCommand = ci1j.getActionCommand(); if(actionCommand.equals("Cuvar")) t.setText("uvaj sladoled! " + "uva se " + ci1j.getState()); else if(actionCommand.equals("Sakrij")) t .setText("Sakrij sladoled! " + "Da li je skriven? " + cil j . g etStateO);

public Meniji() { ML ml = new ML();

Poglavlje 22: Grafika korisnika okruenja

1089

CMIL cmi1 = new CMIL(); bezbednost.setActi onCommand("Cuvar") ; bezbednost.setMnemonic(KeyEvent.VK G ) ; bezbednost.addItemListener(cmil); bezbednost .setActionCommand("Sakri j 1 '); bezbednost.setMnemonic(KeyEvent.VK H); bezbednost.addltemListener(cmi1); ostalo.addActionListener(new FooL()); ostalo.addActionListener(new BarL()); ostalo.addActionListener(new BazL()); FL fl = new FL(); int n = 0; for(String ukus : ukusi) { JMenuItem mi = new JMenuItem(ukusi); mi.addActionListener(fl); m.add(mi);

// Dodavanje razmaka u odreenim intervalima: if( (n++ + 1) % 3 == 0) m. addSeparator(); } for(JCheckBoxMenuItem bbdn : bezbednost) s.add(bbdn); s.setMnemonic(KeyEvent.VK_A); f.add(s); f.setMnemonic(KeyEvent.VK_F); for(int i = 0; i < datoteka.length; i++) { datoteka[i].addActionListener(fl); f.add(datoteka[i]); } mbl.add(f); mbl.add(m); setJMenuBar(mbl); t.setEditable(false); add(t, BorderLayout.CENTER); // Podeavanje sistema za kaskadne menije: b.addActionListener(new BL()); b.setMnemonic(KeyEvent.VK_S); add(b, BorderLayout.NORTH); for(JMenuItem ost : ostalo) fooBar.add(ost); fooBar.setMnemonic(KeyEvent,VK_B); m b2.add(fooBar); }
public static void iriain(String[] args) {

run(new Meniji(), 300, 200); } } ///:U ovom programu sm estio sam stavke menija u nizove, a zatim sam svaku stavku niza prosledio m etodi add( ). Zbog toga je dodavanje ili uklanjanje stavke m enija neto lake.

1090

Misliti na Javi

Ovaj program ne pravi jednu, ve dve linije menija tipa JMenuBar da bi pokazao kako se linije m enija m ogu aktivno menjati dok se program izvrava. Vidite da je linija sastavljena od objekta klase JMenu, o d n o sn o da je svaki objekat tipa JMenu napravljen od stavki tipa JMenuItem, JCheckBoxItem ili ak od drugih menija tipa JMenu (p od m en ija). Kada se linija menija sastavi, m oe se prikazati u tekuem prozoru m etodom setJMenuBar( ). Obratite panju na to da se - nakon pritiska dugm eta - m etodom getJM enuBar( ) proverava koji m eni je trenutno prikazan, a zatim se on zamenjuje drugom linijom menija. Pravopis i raspored m alih i velikih slova presudno su vani prilikom ispitivanja natpisa O tvori, ali Java ne ukazuje na greku ako ne nae odgovarajuu stavku. Ovakva vrsta poreenja znakovnih nizova est je izvor greaka u programiranju. Potvrivanje i odjavljivanje stavki m enija obavlja se autom atski. Kod kojim se obrauju stavke tipa JCheckBoxMenuItem prikazuje dva naina za odreivanje onoga to je potvreno: pronalaenje istih znakovnih nizova (kao to je reeno, to nije naroito bezbedan pristup iako se s vrem ena na vrem e koristi) i pronalaenje istih ciljnih objekata dogaaja. Kao to je pokazano, m etoda getState( ) m oe se koristiti za ustanovljavanje stanja. Stanje stavke JCheckBoxMenuItem m oete da prom enite i m etodom setS tate(). Dogaaji za m enije su donekle nedosledni i m ogu da izazovu zabunu: stavke tipa JMenultem koriste prijem nike tipa ActionListener, dok stavke tipa JCheckboxMenuItem koriste prijem nike tipa ItemListener. Objekti tipa JMenu m ogu da podre i prijemnik tipa ActionListener, ali to ob in o nije korisno. Po pravilu, prijem nici se povezuju sa svim stavkama JMenuItem, JCheckBoxMenuItem ili RadioButtonM enuItem, ali je u prim eru pokazano kako se objekti klasa ItemListener i ActionListener povezuju s raznim kom ponentam a menija. Biblioteka Swing podrava preice s tastature, to znai da m oete aktivirati bilo koji objekat klase izvedene iz klase AbstractButton (dugm e, stavku menija itd.) korienjem tastature um esto mia. To je prilino jednostavno: za stavku JMenuItem m oete da upotrebite preklopljeni konstruktor iji je drugi argum ent identifikator tastera. M eutim , veina dugm adi tipa AbstractButton nem a takve konstruktore, pa je optiji nain za reavanje problem a da se koristi m etoda setM nem on ic( ). U prethodnom primeru dodate su preice u neke stavke menija; indikatori preica autom atski se pojavljuju na kom ponentam a. Vidi se i korienje m etode setA ctionC om m and( ) kojom se zadaje kom anda akcije. To je m alo neobino zato to je u svim sluajevim a komanda akcije" ista kao natpis na kom p onenti m enija. Zato se ne upotrebi postojei natpis um esto novog znakovnog niza? Zbog internacionalizacije. Ako ovaj program prilagodite drugom jeziku, menjaete sam o natpise u m eniju, ali ne i kod (to bi bez sum nje prouzrokovalo nove greke). Da bi se kod koji trai znakovne nizove povezane s kom ponentam a menija lake izvravao, komanda akcije je neprom enljiva, dok se natpis menija m oe menjati. Ceo kod radi skom andom akcije", pa na njega ne utiu prom ene natpisa menija. Obratite panju na to da se u ovom programu ne ispituju sve kom pon en te m enija, pa se onim a koje se ne ispituju ne dodeljuje kom anda akcije.

Poglavlje 22: Grafika korisnika okruenja

1091

Najvei deo posla odvija se u prijem nicim a. Klasa BL zamenjuje linije m enija (objekata tipa JMenuBar). Klasa ML prim enjuje pristup otkrij ko je zvonio" tako to ispituje izvor dogaaja (ActionEventa) i konvertuje ga u JMenuItem, a p otom ispituje znakovni niz kom ande akcije kroz nadovezane naredbe if. Prijem nik FL je jednostavan, m ada obrauje sve razliite ukuse u m eniju ukusa. Ovaj pristup je koristan ako je logika dovoljno prosta, ali ete po pravilu prim enjivati pristup upotrebljen za FooL, BarL i BazL u kojem se prijem nici povezuju sam o s jedn om stavkom m enija, pa nije potrebna nikakva dodatna logika za otkrivanje i zna se tano ko je pozvao prijem nik. ak i ako se broj klasa koje su napravljene na ovaj nain povea, kod unutar njih nije dugaak pa je ovakav postupak bezbedniji. V idite da kod menija brzo narasta i postaje neuredan. I u ovom sluaju treba koristiti vizuelnu razvojnu alatku. Dobra alatka te vrste istovrem eno e i odravati menije.

Veba 19: (3) Prom enite program Meniji.java tako da se u m eniju koriste radio-dugm ad
um esto polja za potvrdu. Veba 20: (6) N apiite program koji tekstualnu datoteku deli na rei. Stavite te rei u m enije i podm enije kao stavke.

Iskaui meniji
Najjednostavniji nain za prim enu iskaueg menija tipa JPopupMenu jeste da se napravi unutranja klasa koja proiruje klasu MouseAdapter, i da se zatim po jedan objekat te unutranje klase pridrui svakoj kom ponenti koja treba da izaziva iskakanje menija:
//: gui/Iskakanje.java // Pravljenje iskauih menija pomou biblioteke Swing. import javax.swin g . import java.awt.*; import java.awt.event.*; import static net.mindview.uti1 .SwingKonzola.*; public class Iskakanje extends JFrame { private JPopupMenu meni = new JPopupMenu(); private JTextField t = new JTextField(10); public IskakanjeO { setLayout(new F1owLayout()); add(t); ActionListener al = new ActionListener() { public void actionPerformed(ActionEvent e) { t.setText(((JMenuItem)e.getSource()) .getText());

JMenuIteiii m

= new J M en uI te m("J u c e " ) ;

m.addActionLi s t e ne r (a l ); m e n i .add(m); m = new JMenuItem("Danas) ;

1092

Misliti na Javi

m.addActionListener(al); meni.add(m); m = new JMenuItem("Sutra"); m.addActionl_istener(al); meni.add(m); meni,addSeparator(); m = new JMenuItem("Nikad"); m.addActionListener(al) ; meni.add(m); PrijemnikMenija pl = new PrijemnikMenija(); addMouseLi stener(pl); t.addMouseListener(pl);

}
class PrijemnikMenija extends MouseAdapter { public void mousePressed(MouseEvent e) { iskociAkoTreba(e);

}
public void mouseReleased(MouseEvent e) { iskociAkoTreba(e);

}
private void iskociAkoTreba(MouseEvent e) { if(e.isPopupTrigger()) { meni.show(e.getComponent(), e.getX(), e.getY());

} } }
public static void main(String[] args) { run(new Iskakanjef), 300, 200);

} } ///:Isti prijemnik tipa A ction L isten er povezuje se sa svim stavkama menija (objektim a tipa JM enuItem ). Prijemnik uzim a tekst natpisa m enija i um ee ga u tekstualno polje.

Crtanje
U dobroj biblioteci za projektovanje grafikih okruenja crtanje bi trebalo da bude prilino lako, a u biblioteci Swing tako i jeste. Problem sa svakim prim erom crtanja jeste to to su izraunavanja kojima se zadaje gde e se ta crtati ob ino m n ogo sloenija od poziva procedura za crtanje. Izraunavanja su esto pom eana s p ozivim a procedura, pa pristup izgleda sloeniji nego to stvarno jeste. Radi jednostavnosti, razm otrim o problem s predstavljanjem podataka na ekranu; ovde e podatke obezbeivati ugraena m etoda M a th .s in f), ti. matematika funkcija sinus. Ua bi sve biio zammljivije, i da bi se jo jed n om pokazalo koliko se kom ponente biblioteke Swing lako koriste, na dno obrasca bie postavljen kliza za dinam iko kontrolisanje broja prikazanih perioda sinusoide. Pored toga, ako prom enite veliinu prozora, videete da se sinusoida prilagoava toj novoj veliini.

Poglav[je 22: Grafika korisnika okruenja

1093

Iako se bilo koja kom ponenta tipa JComponent m oe koristiti kao platno za crtanje, ako elite jednostavnu povrinu (pano) za crtanje, obino treba izvesti klasu iz klase JPanel. Jedino je potrebno redefinisati m etodu paintC om ponent( ) koja se poziva kad god se kom ponenta iscrtava na ekranu. (O bino ne m orate da brinete o tom e kada e ova m etoda biti pozvana, poto o tom e odluuje biblioteka Swing.) Swing prosleuje ovoj m etodi objekat tipa Graphics, a taj objekat zatim m oete da koristite za crtanje ili slikanje p o povrini. U sledeem prim eru se sva logika povezana s crtanjem nalazi u klasi CrtajSinusoidu; klasa Sinusoida sam o podeava program i kliza. M etoda zadajCikluse( ) om oguuje drugom objektu, u ovom sluaju klizau, da kontrolie broj ciklusa.
//: gui/Sinusoida.java // Crtanje u biblioteci Swing, korienjem klizaa JSlider. import javax.swing.*; import javax.swing.event.*; import java.awt.*; import static net.mindview.uti 1 .SwingKonzola.*; class CrtajSinusoidu extends JPanel { private static final int FAKTORSKALE = 200; pri vate int ci kl usi; private int tacke; private double[] sinusi; private int[] tck; public CrtajSinusoidu() { zadajCikluse(5); } public void paintComponent(Graphics g) { super.pai ntComponent(g); int maksimalnaSirina = getWidth(); double hkorak = (double)maksimalnaSirina/(double)tacke; int maksimalnaVisina = getHeight(); tck = new int[tacke]; for(int i = 0; i < tacke; i++) tck[i] = (int) (sinusi [i] * maksimalnaVisina/2 * .95 + maksimalnaVisina/

2) ;
g .setColor(Color.RED); for(int i = 1; i < tacke; i++) { int xl = (int)((i - 1) * hkorak); int x2 = (int)(i * hkorak); int yl = tck[i-l]; int y2 = tck[i]; g.drawLine(xl, yl, x2, y2);

public void zadajCikluse(int noviCiklusi) { ci klusi = novi Ciklusi; tacke = FAKTORSKALE * ciklusi * 2; sinusi = new double[tacke]; for(int i = 0; i < tacke; i++) {

1094

Misliti na Javi

double radijani = (Math.PI/FAKTORSKALE) * i; sinusi [i] = Math.sin(radijani);

}
repaint();

} }
public class Sinusoida extends JFrame { private CrtajSinusoidu sinusi = new CrtajSinusoidu(); private JSlider ciklusi = new JS1ider(l, 30, 5); public Sinusoida() { add(sinusi); ciklusi.addChangeListener(new ChangeListener(){ public void stateChanged(ChangeEvent e) { sinusi.zadajCi kluse( ((JSlider)e.getSourceO) . g e t V a l u e O ) ;

} });
add(BorderLayout.SOUTH, ciklusi);

}
public static void main(String[] args) { run(new Sinusoida(), 700, 400);

} } lll- ~ Svi podaci lanovi i nizovi koriste se u izraunavanjima taaka sinusoide: promenljiva

ciklusi oznaava broj potpunih ciklusa sinusoida koji treba da budu prikazani, tacke sadri ukupan broj taaka, sinusi sadri vrednosti sinusne funkcije, a tck sadri Y koordinate taaka koje se crtaju na panou. M etoda zaajCikIuse( ) pravi nizove na osnovu broja potrebnih taaka i brojevima popunjava niz sinusi. Pozivanjem m etode rep ain tf ) iz m etode zadajCilduse( ) prouzrokuje se poziv m etode p ain tC om p on en t( ) u kojoj se dovrava izraunavanje i pon ovn o iscrtavanje. Kada redefiniete m etodu paintCom ponent( ), prvo m orate da pozovete verziju te m etode iz natklase. Nakon toga m oete da radite ta god elite; ob in o se za crtanje i bojenje piksela na panou JPanel koriste m etode klase Graphics koje m oete da pronaete u dokum entaciji za biblioteku java.awt.Graphics (na adresi h ttp ://ja va.sun .com ). Primetiete da je ovde najvei deo koda posveen izraunavanjima; jedine dve m etode koje zaista crtaju su setC olor( ) i drawLine( ). V erovatno e tako biti i u vaim program im a koji prikazuju grafike podatke: najvei deo vrem ena provodiete smiljajui ta elite da nacrtate, dok e stvarni postupak crtanja biti sasvim jednostavan. D ok sam pisao ovaj program, pu n o vrem ena sam utroio na to da postignem da se sinusoida prikae. im sam uspeo, palo m i je na pam et da bi bilo lepo kada bi broj perioda m ogao inam iki d.i se meni.i. Zbog iskustava iz drugih p n lgramskih jezika oklevao sam da to uradim, ali se ispostavilo da je to najlaki deo posla. Napravio sam kliza - objekat klase JSlider (argum enti su krajnja leva vrednost, krajnja desna vrednost i poetna vrednost klizaa, ali ima i drugaijih konstruktora) i ubacio sam ga u klasu JFraine. Zatim sam pregledao HTML dokum entaciju i prim etio da je jedini prijem nik klizaa addChangeListener. On dobija informaciju kad god se poloaj klizaa prom eni. Jedina m etoda tog

Poglav[je 22: Grafika korisnika okruenja

1095

interfejsa je stateC hanged( ), to se m oglo zakljuiti po im enu prijemnika. Njen argum ent je objekat tipa ChangeEvent koji om oguuje da se pregleda izvor prom ene i proita nova vrednost. Pozivanjem m etode zadajCikluse( ) objekta sinusi p on ovo se iscrtava pano s novom sinusoidom . V ideete da se veina problem a u biblioteci Swing m oe reiti slinim postupkom , to je ob in o dosta lako, ak i ako neku k om p on en tu nikada ranije niste koristili. Ako su vai problem i sloeniji, postoje i naprednije m etod e za crtanje, ukljuujui i zrna Jave nezavisnih proizvoaa i interfejs za programiranje Java 2D. Ta reenja prevazilaze okvire ove knjige, ali bi trebalo da ih potraite ako va kod za crtanje postane previe problem atian. Veba 21: (5) Izm enite program Sinusoida.java tako to ete objekat klase S in u soid a pretvoriti u zrno, i to tako to ete m u dodeliti m etode za itanje i zadavanje svojstava.

Veba 22: (7) Napravite aplikaciju p o m o u klase SwingKonzoIa. Trebalo bi da im a tri klizaa, po jedan za crvenu, zelenu i plavu boju u java.awt.Color. Ostatak obrasca bi trebalo da bude objekat tipa JPanel koji prikazuje boju zadatu s tri klizaa. Postavite i polja za tekst iji se sadraj ne m oe menjati i koja prikazuju tekue RGB vrednosti. Veba 23: (8) Na osnovu aplikacije Sinusoida. java napravite program koji na ekranu
prikazuje rotirajui kvadrat. Neka jedan kliza upravlja brzinom rotacije, a drugi veliin om prozora. Veba 24: (7) Seate li se igrake s dva dugm eta - jednim koje kontrolie vertikalno kretanje nacrtane take i drugim koje kontrolie horizontalno kretanje? Napravite takvu igraku koristei program Sinusoida. java. U m esto dugm adi upotrebite klizae. Dodajte dugm e kojim se brie cela skica.

Veba 25: (8) Na osnovu aplikacije Sinusoida. java napravite program (aplikaciju koja upotrebljava klasu SwingKonzoIa) koji na ekranu crta anim iranu sinusoidu to se p omera u prozoru za prikazivanje kao da je u pitanju osciloskop. Anim aciju neka pokree klasa java.util.Timer, a njenu brzinu neka odreuje kontrola javax.swing.JSlider. Veba 26: (3) Izm enite prethodnu vebu tako da aplikacija im a vie panela sa sinusoidama. Broj panela neka se zadaje parametrom na kom andnoj liniji.

Veba 27: (5) Izm enite vebu 25 tako da anim aciju pokree klasa javax.swing.Timer. Obratite panju na razliku izm eu ove anim acije i o n e u kojoj se koristi klasa java.util.Timer.
Veba 28: (7) Napravite klasu za kockanje (bez grafikog korisnikog okruenja). Neka se baca pet kocaka i to vie puta. Nacrtajte krivu koja prikazuje zbir palih brojeva i dinam iki se aurira nakon svakog bacanja.

Okviri za dijalog
Okvir za dijalog je prozor koji iskae iz drugog prozora. N jegova svrha je da se izbori sa odreenom tem om a da ne zatrpa detaljima poetni prozor. Dijalozi se esto koriste u grafikim program skim okruenjima.

1096

Misliti na Javi

Da biste napravili okvir za dijalog, treba da nasledite klasu JDialog, to je sam o jedna vrsta prozora, p op ut klase JFrame. Klasa JDialog im a svoj rasporeiva (podrazum evani je BorderLayout), a za obradu dogaaja treba da dodate prijemnike. Evo vrlo jednostavn o g primera:
//: gui/Dijalozi.java // Pravljenje i korienje okvira za dijalog. import javax.swing.*; import java.awt.*; import java.awt.event.*; import static net.mindview.util.SwingKonzola.*; class MojDijalog extends JDialog { public MojDijalog(JFrame roditelj) { super(rodit.elj, "Moj dijalog", true); setLayout(new FlowLayout()); add(new JLabel("Evo mog dijaloga")); JButton ok = new JButton("OK"); ok.addActionListener(new ActionListener()

public void actionPerformed(ActionEvent e) { disposeO; // Zatvara dijalog

1
});
add(ok); setSize(150,125);

} }
public class Dijalozi extends JFrame { private JButton bl = new JButton("Okvir za dijalog"); private MojDijalog dlg = new MojDi jalog(nul 1); public Dijalozi() { b l .addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e ) { dl g .setVi si ble();

} });
add(bl);

}
public static void main(String[] args) { run(new Dijalozi(), 125, 75);

III---

Kada se napravi objekat klase JD ialog, treba pozvati m etodu setV isib le(tru e) da bi se dijalog prikazao i aktivirao. Nakon zatvaranja prozora dijaloga, pozovite m etodu d isp o s e ( ) da biste oslobodili resurse koje prozor dijaloga (i dalje) koristi.

Poglavlje 22: Grafika korisnika okruenja

1097

Sledei prim er je sloeniji. Dijalog sadri tabelu dugm adi tipa Ik sO k sD u gm e (napravljena je p o m ou klase GridLayout). Ovo dugm e oko sebe iscrtava okvir i, u zavisnosti od svog stanja, u sredini ne prikazuje nita, prikazuje x ili o. D u gm e je u poetku prazno, a zatim se, u zavisnosti od toga ko je na redu da igra, pretvara u x ili o. M eutim , on o naizm en ino menja stanja izm eu x i o i kada ga pritiskate. (N a taj nain igra iks-oks se m alo kom plikuje.) Pored toga, okvir za dijalog m oe da sadri proizvoljan broj redova i kolona, to se zadaje prom enom brojeva u glavnom prozoru aplikacije.
//: gui/IksOks.java // Prikaz okvira za dijalog // i izrade komponenata. import javax.swing.*; import java.awt.*; import java.awt.event.*; import static net.mindview.util.SwingKonzola.*; public class IksOks extends JFrame { private JTextField redovi = new JTextField("3"), kolone = new JTextField("3"); private enum Stanje { PRAZAN, XX, 00 ) static class IksOksDijalog extends JDialog { private Stanje potez = Stanje.XX; // Poinje igra x // s = broj elija po irini // v = broj elija po visini IksOksDijalog(int s, int v) { setTitle("Igra"); setLayout(new GridLayout(s, v)); for(int i = 0; i < s * v; i++) add(new Iks0ksDugme()); setSize(s * 50, v * 50); setDefaultClose0peration(DISP0SE_0N_CL0SE);

}
class IksOksDugme extends JPanel { private Stanje stanje = Stanje.PRAZAN; public Iks0ksDugme() { addMouseListener(new ML()); } public void paintComponent(Graphics g) { super.paintComponent(g); int xl = 0, yl = 0; x2 = getSizeO .width - 1; y2 = getSizeO .height - 1; g.drawRect(xl, yl, x2, y2); x 1 = x2/4; yl = y2/4; int siroko = x2/2, visoko = y2/2; if (stanje == Stanje.XX) { g.drawLine(xl, yl, xl + siroko, yl + visoko);

1098

Misliti na Javi

g.drawLine(xl, yl + visoko, xl + siroko, yl);

}
if(stanje == Stanje.OO) { g.drawOval(xl, yl, xl + siroko/2, yl + visoko/2);

} }
class ML extends MouseAdapter { public void mousePressed(MouseEvent e) { if(stanje == Stanje.PRAZAN) { stanje = potez; potez = (potez == Stanje.XX ? Stanje.00 : Stanje.XX);

}
else stanje = (stanje == Stanje.XX ? Stanje.00 : Stanje.XX); repaint();

} } } }
class BL implements ActionListener { . public void actionPerformedfActionEvent e) { JDialog d = new IksOksDijalog( new Integer(redovi.getText()), new Integer(kolone.getText())); d.setVisible(true);

} }
public IksOks() { JPanel p = new JPanel(); p.setLayout(new GridLayout(2,2)); p.add(new JLabel(Redovi", JLabel.CENTER)); p.add(redovi); p.add(new JLabel("Kolone", JLabel.CENTER)); p.add(kolone); add(p, BorderLayout.NORTH); JButton b = new JButton("potez"); b.addActionListener(new BL()); add(b, BorderLayout.SOUTH);

}
public static void main(String[] args) { run(new IksOks(), 200, 200);

} } ///:Poto sam o spoljanji nivo klase m oe imati statike lanove, unutranje klase ne m ogu da sadre statike podatke ili ugneene klase. Metoda p a in tC o m p o n e n t() crta kvadrat oko panoa, kao i oznake x ili o. Taj postupak je prepun dosadnih izraunavanja, ali je jednostavan.

Poglavlje 22: Grafika korisnika okruenja

1099

Operaciju pritiskanja mia hvata objekat klase MouseListener koji prvo proverava da li je na p an ou neto ispisano. Ako nije, ispituje se roditeljski prozor da bi se ustanovilo ko je na potezu i to se koristi za zadavanje stanja objekta IksOksDugme. P om ou m ehani zma unutranje klase, IksOksDugm e zatim pristupa svom roditelju i menja igraa koji je na potezu. Ako dugm e ve prikazuje x ili o, stanje m u se menja. U izraunavanjima se vidi zgodno korienje ternarnog operatora uslovljavanja koji je opisan u poglavlju Operatori. Nakon p rom en e stanja, objektu Iks-OksDugme menja se boja. Konstruktor klase IksOksDijalog je prilino jednostavan: u tabelu tipa GridLayout dodaje dugm ad po potrebi, a zatim poveava stranu svakog dugm eta za 50 piksela. Objekat klase IksOks pokree celu aplikaciju tako to pravi tekstualna polja za unoenje broja redova i kolona tabele dugm adi, dugm e ,,potez i prijemnik tipa ActionListener. Kada se pritisne dugm e, uzimaju se podaci iz tekstualnih polja, a poto su to znakovni nizovi, m oraju se konvertovati u tip int p om ou Integer konstruktora koji prima String argument.

Dijalozi za rad s datotekama


Neki operativni sistem i im aju veliki broj posebnih ugraenih dijaloga koji slue za biranje fontova, boje, tampaa i sl. G otovo svi grafiki operativni sistem i imaju i dijaloge za otvaranje i snim anje datoteka, pa i Javina klasa JFileChooser kapsulira takve dijaloge radi lakeg korienja. U sledeoj aplikaciji prikazuju se dva oblika dijaloga tipa JFUeChooser, jedan za otvaranje i drugi za snim anje. Najvei deo koda do sada bi trebalo da vam bude poznat, a sve zanim ljive aktivnosti deavaju se u prijem nicim a dogaaja za dva razliita dugmeta:
//: gui/TestBiranjaDatoteke.java // Prikaz dijaloga za rad s datotekama. import javax.swing.*; import java.awt.*; import java.awt.event.*; import static net.mindview.uti1 .SwingKonzola.*; public class TestBiranjaDatoteke extends JFrame { private JTextField imeDatoteke = new JTextField(), dir = new JTextField(); private JButton otvori = new JButton("Otvori"), snimi = new JButton("Snimi"); public TestBiranjaDatoteke() { JPanel p = new JPanel(); otvori.addActionListener(new OtvoriL()); p.add(otvori); snimi.addActionListener(new SnimiL()); p.add(snimi) ; add(p, BorderLayout.SOUTH); dir.setEditable(false);

1100

Misliti na Javi

imeDatoteke.setEditable(fa1se); p = new JPanel(); p.setLayout(new GridLayout(2,l)); p.add(imeDatoteke); p.add(di r); add(p, BorderLayout.NORTH);

}
class OtvoriL implements ActionListener { public void actionPerformed(ActionEvent e) { JFileChooser c = new JFileChooser(); // Prikazuje dijalog "Otvori": int rVrednost = c.showOpenDialog(TestBiranjaDatoteke.this); if(rVrednost == JFi1eChooser.APPR0VE_0PTI0N) { imeDatoteke.setText(c.getSelectedFile(),getName()); dir.setText(c.getCurrentDirectory() .toSt r i n g O ) ;

}
if(rVrednost == JFi1eChooser.CANCEL_OPTION) { imeDatoteke.setText("Pritisnu!i ste dugme Cancel"); dir.setText("");

} } }
class SnimiL implements ActionListener { public void actionPerformed(ActionEvent e) { JFileChooser c = new JFileChooser(); // Prikazuje dijalog "Snimi": int rVrednost = c.showSaveDialog(TestBiranjaDatoteke.this); if(rVrednost == JFileChooser.APPROVE_OPTION) { imeDatoteke.setText(c.getSelectedFile().getName()); di r.setText(c.getCurrentDi rectory().toStri n g ()) ;

}
if(rVrednost == JFileChooser.CANCEL_OPTION) { imeDatoteke.setText("Pritisnuli ste dugme Cancel"); di r.setText("");

} } }
public static void main(String[] args) { run(new TestBiranjaDatoteke(), 250, 150);

1 ///:Zapam tite da postoje razne verzije dijaloga JFileChooser, ukljuujui i one koje koriste filtre za suavanje spiska dozvoljenih im ena datoteka. M etodom sh o w O p en D ia lo g ( ) poziva se dijalog za otvaranje datoteke, a m etodom show SaveD ialog( ) dijalog za snim anje datoteke. Iz tih m etoda se izlazi tek kada se dijalog zatvori. Objekat tipa JFileChooser posle toga i dalje postoji, pa m oete da itate podatke iz njega. M etode getSelectedF ile( ) i getC urrentD irectory( ) dva su naina za ispitivanje rezultata operacije. Ako te m etode vrate null, korisnik je u dijalogu pritisnuo dugm e za odustajanje (C ancel).

Poglavlje 22: Grafika korisnika okruenja

1101

Veba 29: (3) U HTML dokum entaciji za biblioteku java.sw in g potraite klasu JCoIorC hooser. N apiite program koji pravi dugm e to, kada se pritisne, prikazuje kom andu za biranje boja u obliku dijaloga.

HTML u komponentama biblioteke Swing


Svaka kom ponenta koja prikazuje tekst m oe da prikae i HTM L tekst koji e se form atirati u skladu s HTML pravilim a. To znai da Swing kom ponenti m oete vrlo lako dodati lepo form atiran tekst. Na primer:
//: gui/HTMLDugme.java // Stavljanje HTML teksta na Swing komponente. import javax.swing.*; import java.awt.*; import java.awt.event.*; import static net.mindview.util.SwingKonzola.*; public class HTMLDugme extends JFrame ( private JButton b = new JButton( "<html><b><font size=+2>" + "<center>Zdravo!<br><i>Pritisni me!"); public HTMLDugtneO { b.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e){ add(new JLabel("<html>"+ "<i><font size=+4>Tras!")); // Izaziva ponovno iscrtavanje da bi se prikazao natpis: val idate();

} });
setLayout(new FlowLayout()); add(b);

}
public static void main(String[] args) { run(new HTMLDugme(), 200, 500);

} } lll--~ Tekst mora da zapone oznakom < h tm l> , a potom m oete da upotrebljavate uobiajene HTML oznake. Obratite panju na to da nije obavezno korienje uobiajenih oznaka za zatvaranje. Prijemnik tipa A ction L isten er u obrazac dodaje nov natpis tipa JLabel koji sa A i i 1 rn vil. tekst. M eutim , taj nalpis se ne oaje tokom konstruisanja objekta, pa m orateda pozovete kontejnersku m etodu validate( ) da biste prouzrokovali p on ovn o iscrtavanje kom ponenata (od n osn o, prikazivanje novog natpisa). I ITML tekst m oete da koristite i u objektim a klasa JTabbedPane, JM enuItem , JTooITip, JR adioB utton i JCheckBox. Veba 30: (3) Napiite program u kojem se HTML tekst ispisuje na svim objektima nabrojanim u prethodnom pasusu.

1102

Misliti na Javi

Klizai i linije napredovanja


Kliza (engl. slider) koji je ve upotrebljen u prim eru sinusoide, om oguuje korisniku da unosi podatke tako to ga pom era napred-nazad, to je u nekim situacijama intuitivno (na primer, za kontrolu jaine zvuka). Traka napredovanja (engl. progress bar) prikazuje podatke u relativnom odnosu, od nim alo do potpuno. Moj om iljen prim er za ove grafike elem ente je da se kliza zakai za traku napredovanja tako da se saglasno pom eranju klizaa pom era i traka napredovanja. U narednom prim eru videete i P rogressM onitor, to je bogatiji iskaui dijalog :
//: gui/Napredak.java // Korienje traka napredovanja, klizaa i monitora napredovanja. import javax.swing.*; import javax.swing.border.*; import javax.swing.event.*; import java.awt.*; import static net.mindview.util.SwingKonzola.*; public class Napredak extends JFrame { private JProgressBar pb = new JProgressBar(); private ProgressMonitor pm = new ProgressMonitor( this, "Nadziranje napretka", "Test", 0, 100); private JSlider sb = new JSlider(JSlider.H0RIZ0NTAL, 0, 100, 60); public Napredak() { setLayout(new GridLayout(2,l)); add(pb); pm.setProgress(O); pm.setMi11i sToPopup(lOOO); sb.setValue(O); sb.setPaintTicks(true); sb.setMajorTickSpacing(20); sb.setMinorTickSpacing(5); sb.setBorder(new TitledBorder("Pomeri me")); pb.setModel(sb.getModel()); // Zajedniki model add(sb); sb.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent e) { pm.setProgress(sb.getValue());

} }); }
public static void main(String[] args) { run(new Napredak(), 300, 200);

} ///:-

Poglavlje 22. Grafika korlsnika okruenja

1103

Klju za povezivanje klizaa i trake napredovanja lei u zajednikom korienju m odela, u redu:
pb.setModel(sb.getModel());

Ove dve kom p onente m ogli biste da kontroliete i p om ou prijemnika, ali je ovo reenje prostije. ProgressM onitor nem a m odel, pa sam m orao da upotrebim prijemnik. Verovatno ste prim etili da se ovaj ProgressMonitor kree sam o unapred i da se zatvara kada d oe do kraja. Klasa JProgressBar je prilino oigledna, ali klasa JSlider im a m notvo opcija, npr. orijentisanje, vee i manje zareze na klizau itd. Obratite panju na to kako se lako dodaje ivica sa natpisom .

V eba31: (8) Napravite indikator asim ptotskog napredovanja koji se usporava s pribliavanjem zavrnoj taki. Dodajte mu nasum ino pogreno ponaanje da bi s vremena na vrem e izgledalo kao da poinje da se ubrzava. Veba 32: (6) Izm enite program Napredak.java tako da za povezivanje klizaa i trake napredovanja ne koristi zajednike m odele, ve prijemnik.

Biranje izgleda i ponaanja


Jedna od zanim ljivih m ogunosti grafike biblioteke Swing jeste postizanje promenijivog izgleda i ponaanja (engl. Plnggable L ookik Feel). To om oguuje programu da simulira izgled i ponaanje razliitih radnih okruenja. M oete da uradite zanimljive stvari, npr. da dinam iki menjate izgled i ponaanje programa. M edutim , obino ete koristiti sam o jednu ili dve m ogunosti: biraete izgled koji je isti na svim platformam a (to je u biblioteci Swing ,,m etal), ili izgled sistem a u kom e trenutno radite, da bi program napisan na Javi izgledao kao da je pravljen za taj sistem . Kod kojim se biraju izgled i ponaanje prilino je jenostavan, ali m orate da ga izvrite prc nego to ponete da pravite vizuelne kom ponente. K om ponente se prave na osnovu tekueg izgleda i ponaanja, tj. postojee k om p onen te nee biti prom enjene sam o zato to ste vi usred programa odluili da im prom enite izgle (takav postupak je sloeniji i neuobiajen, a obraen je u knjigama posveenim grafikoj biblioteci Swing). Zapravo, ako elite da program izglea isto na svim platform am a, ne m orate nita da radite, jer se metalni" izgled podrazum eva. Ali ako elite da koristite izgled i ponaanje tekueg radnog okruenja,Rtreba sam o da um etnete sledei kod, obino na poetak metode m a in ( ); obavite to pre pravljenja bilo kakvih grafikih elemenata:
try { UIManager.setLookAndFeel ( UIManaqer.getSystemLookAndFee1ClassNamef)); / cdtcn ( c x c e p i 1on ej \ throw new RuntimException(e);

P ita n je je k o lik o u s p e n o Svving o p o n a a ra z n a ra d n a o k ru e n ja .

1 104

Misliti na Javi

Blok catch m oe da bude prazan zato to e U IM anager uitati podrazum evan izgled i ponaanje ako pokuaj da zadate drugaiji izgled ne uspe. M eutim , inform acija o izuzetku biva veom a korisna pri traenju greaka, pa u bloku catch m oete staviti barem naredbu za ispisivanje. Evo programa koji na osnovu argum enta s kom andne linije zadaje izgled i ponaanje, i pokazuje kako neke kom ponente u tom sluaju izgledaju:
//: gui/IzgledlPonasanje.java // Biranje razliitih izgleda i ponaanja. // {Args: motif} import javax.swing.*; irrport java.awt.*; import static net.mindview.util.SwingKonzola.*; public class IzgledlPonasanje extends JFrame { private String[] izbor = "eci peci pec ti si mali zec".split(" "); private Component[] uzorci = { new JButton("JButton"), new JTextField("JTextField"), new JLabel("JLabel"), new JCheckBox("JCheckBox"), new JRadioButton("Radio"), new JComboBox(izbor), new JList(izbor),

};
public IzgledIPonasanje() { superC'Izgled i ponasanje"); setLayout(new FlowLayout()); for(Component komponenta : uzorci) add(komponenta);

'

}
private static void greskaKoriscenja() { System.out.println( "Korienje:IzgledIPonasanje [prenosiv|sistemski|motif]"); System.exit(l);

}
public static void main(String[] args) { if(args.length == 0) greskaKoriscenjaO,;' ' if(args[0].equals("prenosiv")) { try { UIManager.setLookAndFeel(UIManager. getCrossPl atformLookAndFeelC1assName()); ) catch(Exception e) { e.printStackTrace();

}
} else if(args[0].equals("sistemski)) { try {

Poglavlje 22: Grafika korisnika okruenja

1105

UIManager.setLookAndFeel(UIManager. getSystemLookAndFeelClassName()); } catch(Exception e) { e.printStackTrace();

}
} else if(args[0] .equals("motif")) { try { UIManager.setLookAndFeel("com.sun.java.+ "swing.plaf.motif.MotifLookAndFeel"); } catch(Exception e) { e.printStackTrace();

}
} else greskaKoriscenja(); // Obratite panju na to da se izgled i ponaanje moraju // zadati pre pravljenja bilo kakvih komponenata. run(new IzgledIPonasanje() 300, 300);

} } ///:Za eksplicitno zaavanje izgleda i ponaanja koristi se M otifL ookA ndF eel. Sam o se taj i podrazum evani ,,m etalni izgled sigurno m ogu koristiti na svim platformam a; iako postoje kom ponente koje oponaaju izgled i ponaanje W indow sa i M acOS-a, one se m ogu koristiti sam o na odgovarajuim platform am a (dobijaju se kada pozovete m etodu getSystem L ookA ndF eeIC lassN am e() dok radite na odreenoj platform i). M ogue je napraviti i sopstveni paket elem enata koji opisuju izgled i ponaanje, na prim er ako pravite strukturu aplikacije za kom paniju koja eli da ta aplikacija posebno izgleda. To je velik posao i uveliko premauje tem u ove knjige (zapravo, otkriete da se njom e ne bave ni m noge knjige iskljuivo posveene biblioteci Swing).

Stabla, tabele i clipboard


Kratak uvod i prim ere za ove tem e nai ete u m renim dodacim a ovog poglavlja na adresi w w w .M ind V iew .n et.

JN LP i Java Web Start


Aplet m oe biti potpisan da bi se ostvarila bezbednost. To je pokazano u m renom dodatku ovog poglavlja na adresi w w w .M indV iew .net. Potpisani apleti su m on i i m ogu da zam ene aplikacije, ali moraju da se izvravaju u itau Weba. To znai da ita koji se izvrava na korisnikovom raunaru prouzrokuje dodatne reijske trokove a korisniko okruenje apleta ogranieno je i esto zbunjuje. Cita Weba im a sopstvene m enije i palete sa alatkama, koji se prikazuju iznad apleta.'1 Javin protokol za m reno pokretanje (Java N etw ork Launch Protocol, JNLP) reava problem a da ne ugroava prednosti apleta. JNLP aplikacija m oe se preuzeti i instalirati kao sam ostalna Java aplikacija na raunaru korisnika. Ona se m oe pokretati s kom andne liniOvaj odeljak n apisao je Jeremy Meyer.

1106

Misliti na Javi

je, pom ou ikonice na radnoj povrini ili programa za upravljanje aplikacijama koji je deo realizacije JNLP-a. Aplikacija se m oe pokretati ak i s Web lokacije s koje je bila preuzeta. U vrem e izvravanja, JNLP aplikacija m oe dinam iki da preuzim a resurse sa Interneta i da autom atski proverava verziju ako je korisnik povezan sa Internetom . To znai da im a sve prednosti apleta, a i prednosti sam ostalnih aplikacija. Korisnikov raunar mora oprezno da tretira JNLP aplikacije, kao i aplete. Zato se JNLP aplikacije izvravaju sam o u bazenu s peskom , kao apleti. U koliko su u potpisanim JAR datotekama, korisnik m oe odluiti da im ukae poverenje i dopusti korienje resursa njegovog raunara. (Isto vai i za aplete.) Za razliku od apleta, p o m o u usluga JNLP interfejsa za programiranje JNLP aplikacije m ogu zatraiti pristup odreenim resursima korisnikovog raunara i kada su u nepotpisanim JAR datotekam a. Tokom izvravanja programa, od korisnika se trai da odobri te zahteve. JNLP je specifikacija protokola, a ne njegova realizacija. Zato je za korienje neoph odna i neka realizacija. Java Web Start (JAWS) Sunova je besplatna zvanina referentna realizacija koja se isporuuje u sklopu Jave SE5. U koliko elite da je koristite za razvoj, njena JAR datoteka (javaws.jar) mora biti u putanji klasa vaeg raunara; najjednostavnije reenje je dodati datoteku javaw s.jar putanji klasa prem estiti je iz njene uobiajene putanje Java instalacije na putanju jre/lib. Ako JNLP aplikaciju nameravate da instalirate s Web servera, on bi trebalo da prepoznaje MIME tip ap p lication /x-java-jn lp -file. To je unapred konfigurisano u novijim verzijama servera Tomcat {http://jakarta.apache.org/ tom cat ). Odgovarajue pojedinosti proitajte u korisnikim uputstvim a za svoj server. Nije teko napraviti JNLP aplikaciju. Napravite obinu aplikaciju, kom prim ujte je u JAR arhivu i zatim obezbedite datoteku za pokretanje - jednostavnu XML datoteku koja korisnikovom raunaru daje sve informacije potrebne za preuzim anje i instaliranje te aplikacije. U koliko ne potpiete JAR arhivu, morate da koristite usluge koje interfejs za programiranje JNLP aplikacija prua za sve tipove resursa kojima aplikacija treba da pristupi na raunaru korisnika. Sledi varijanta programa TestBiranjaD atoteka.java koja za otvaranje biraa datoteka upotrebljava JNI.P usluge, im e om oguuje da klasa bude distribuirana u obliku JNLP aplikacije u nepotpisanoj JAR arhivi.
//: gui/jnlp/JnlpBiranjeDatoteka.java // Otvaranje datoteka na lokalnom raunaru pomou JNLP-a. // {Requires: javax.jnlp.Fi1eOpenService; // javaws.jar mora biti u putanji klasa} // Ovako ete napraviti datoteku jnlpbiranjedatoteka.jar: // cd .. // cd .. // jar cvf gui/jnlp/jnlpbiranjedatoteka.jar gui/jnlp/*.class package g ui .jnlp; import javax.jnlp.*: import javax.swing.*; import java.awt.*; import java.awt.event.*; import java.io.*;

Poglavjje 22: Grafika korisnika okruenja

1107

public class JnlpBiranjeDatoteka extends JFrame { private JTextField imeDatoteke = new JTextField(); private JButton otvori = new JButton(Otvori), snimi = new JButton("Snimi"); private JEditorPane ep = new JEditorPane(); private JScrollPane jsp = new JScrollPane(); private FileContents sadrzajDatoteke; public JnlpBiranjeDatoteka() { JPanel p = new JPanel(); otvori.addActionListener(new Otvori L()); p.add(otvori); snimi.addActionListener(new SnimiL()); p.add(snimi); jsp.getViewport().add(ep); ad(jsp, BorderLayout.CENTER); add(p, BorderLayout.SOUTH); imeDatoteke.setEdi table(false); p = new JPanel(); p.setLayout(new GridLayout(2,l)); p.add(imeDatoteke); add(p, BorderLayout.NORTH); ep.setContentType("text"); snimi.setEnabled(false);

1
class OtvoriL implements ActionListener { public void actionPerformed(ActionEvent e) { Fi 1 eOpenService fs = null; try { fs = (Fi 1eOpenService)ServiceManager.1ookup( "javax.jnlp.Fi1eOpenServi ce"); } catch(UnavailableServiceException use) { throw new RuntimeException(use);

}
if(fs != null) { try { sadrzajDatoteke = fs.openFileDialogC1.", new String[]{"txt", "*"}); if(sadrzajDatoteke == null) return; imeDatoteke.setText(sadrzajDatoteke.getName()); ep.read(sadrzajDatoteke.getInputStream(), nul1); } catch(Exception exc) { throw new RuntimeException(exc); snimi.setEnabled(true);

1 108

Misliti na Javi

}
class SnimiL implements ActionListener { public void actionPerformed(ActionEvent e) { FileSaveService fs = null; try { fs = (FileSaveService)ServiceHanager.lookup( "javax.jnlp.FileSaveService"); } catch(Unavailab1eServiceException use) { throw new RuntimeException(use);

}
if(fs != null) { try { sadrzajDatoteke = fs.saveFileDialog(".", new String[]{"txt"}, new ByteArrayInputStream( ep.getText().getBytes()), sadrzajDatoteke.getName()); if(sadrzajDatoteke == null) return; imeDatoteke.setText(sadrzajDatoteke.getName()); } catch(Exception exc) { throw new RuntimeException(exc);

} } } }
public static void main(String[] args) { JnlpBiranjeDatoteka bd = new JnlpBiranjeDatoteka(); bd.setSize(400, 300); bd.setVisible(true);

} } ///Obratite panju na to da se klase FileOpenService i FileCloseService uvoze iz paketa javax.jnlp i da se dijalog JFileChooser nigde neposredno ne spom inje. O be usluge koje se ovde upotrebljavaju m oraju biti zatraene m etod om ServiceM anager.Iookup( ), a resursim a na raunaru korisnika m oe se pristupiti sam o preko objekata koje ta m etoda vrati. U ovom sluaju, u sistem u datoteka korisnikovog raunara datoteke se itaju i upisuju p o m ou interfejsa FileContent, koji obezbeuje JNI.P. Svaki pokuaj neposrednog pristupanja resursima preko, recim o, objekta tipa File ili FileReader, izazvao bi bacanje izuzetka SecurityException, jednako kao da ih pokuate upotrebiti iz nepotpisanog apieta. U koliko elite da koristite te klase i da ne budete ogranieni na interfejse JNLP usluga, m orate potpisati JAR arhivu. Potrebnu JAR arhivu pravi kom anda jar koja je na poetku programa JnlpBiranjeDatoteka.java pretvorena u komentar. Ovo je datoteka za pokretanje prethodnog primera.

Poglavlje 22: Grafika korisnika okruenja

1109

/ / :! gui/jnlp/biranjedatoteka.jnlp <?xml version="1.0" encoding="UTF-8"?> <jnlp spec = "1.0+" codebase="fi1e:C:/AAA-TIJ4/code/gui/jnlp" href="biranjedatoteka.jnlp"> <information> <title>Primer FileChooser aplikacije</title> <vendor>Mindview Inc.</vendor> <description> Jnlp aplikacija za biranje datoteka </description> <description kind="short"> Pokazuje otvaranje, itanje i upisivanje u tekstualnu datoteku </description> <icon href="inindview.gif"/> <off1ine-all owed/> </information> <resources> <j2se version="1.3+" href="http://java.sun.com/products/autodl/j2se"/> <jar href="jnlpbiranjedatoteka.jar" download="eager"/> </resources> <application-desc main-class = "gui.jnlp.JnlpBi ranjeDatoteka"/> </jnlp>

///:N avedenu datoteku za pokretanje nai ete u datoteci izvornog koda ove knjige (m oe se preuzeti na adresi w w w .M in d V iew .n et) pod im en om biranjedatoteka.jnlp (bez prvog i poslednjeg reda), u istom direktorijum u kao JAR arhiva. Kao to vidite, radi se o XML datoteci s jed n om oznakom <jnlp>. Ona im a nekoliko podelem enata, m anje-vie jasnih po sebi. Atribut spec elem enta jnlp saoptava klijentskom sistem u koja verzija JNLP-a m oe da pokrene aplikaciju. Atribut codebase pokazuje na URL adresu na kojoj su resursi i datoteka za pokretanje. Ovde pokazuje na direktorijum lokalnog raunara, to je dobar nain za testiranje ove aplikacije. N a d a m se da shvatate kako tu p u ta n ju n io ra tep ro m en iti tako da pokazuje na odgovarajui direktorijum vaeg rac'unara tek tada e seprogram uitati. Atribut href specificira im e datoteke koju treba uitati. Oznaka inform ation im a razne p od elem ente koji pruaju inform acije o aplikaciji. Njih ita adm inistrativna konzola Java Web Start ili druga njoj ekvivalentna realizacija JNLP-a koja instalira JNLP aplikaciju i om ogu u je korisniku da je pokrene s kom andne liniie, pravi preice itd. Svrha oznake resources slina je svrsi oznake <applet> u U'i iVft. datoteci. Podelem ent j2se specificira verziju J2SE potrebnu za pokretanje aplikacije, a podelem ent jar JAR datoteku u kojoj je klasa arhivirana. Elem ent jar im a atribut download ije vrednosti eager od n osn o lazv kazuju realizaciji JNLP-a da li pre pokretanja aplikacije m ora da uita celu arhivu ili ne mora.

1110

Misliti na Javi

Atribut application-desc saoptava realizaciji JNLP-a koja klasa je izvrna klasa (ulazna taka) JAR arhive. D rugi koristan podelem ent oznake jnlp jeste oznaka security koje u gornjoj datoteci nem a. Evo kako izgleda oznaka security: <security>
<a11-permissions/>

< secu rity/> O znaku security treba da im aju aplikacije u potpisanim JAR arhivama. U preth od n om prim eru nije potrebna zato to se svim lokalnim resursima pristupa pom ou JNLP usluga. Pojedinosti o ostalim oznakama potraite u specifikaciji na adresi h ttp ://ja va .su n .co m / products/javaw ebstart/dow nload-spec.htm l. Za pokretanje programa potrebna je stranica za preuzim anje koja sadri hipertekstualnu vezu sa .jnlp datotekom . Evo kako ona izgleda (bez prvog i poslednjeg reda):
//:! gui/jnlp/biranjedatoteka.html <html> Sledite uputstva iz JnlpBiranjeDatoteka.java za pravljenje jnlpfi1echooser.jar, a zatim: <a href="biranjedatoteka.jnlp">pritisnite ovde</a> </html>

///: Poto preuzm ete aplikaciju, m oete da je konfiguriete p o m o u adm inistrativne konzole. Ako Java Web Start koristite u W indow su, prilikom druge upotrebe m oi ete da napravite preicu do aplikacije. To ponaanje se m oe konfigurisati. O vde sam opisao sam o dve JNLP usluge, a u tekuem izanju ih im a sedam . Svakom od njih obavlja se odreeni zadatak, kao to je tam panje ili isecanje i prenoenje na clipboard. Vie informacija o tom e potraite na adresi http://ja va .su n .co m .

Paralelno izvravanje i Svving


Kada programirate u Swingu, radite s nitim a. To ste videli na poetku ovog poglavlja kada ste saznali da sve zadatke treba pokretati m etod om S w in g U tilities.in v o k eL a ter() Swingovog rasporeivaa. M eutim , injenica da ne m orate eksplicitno praviti niti (objekte klase T hread) znai da bi problem i s nitim a m ogli da vas snau kada se ne nadate. Vodite rauna o tom e da nit Sw ingovog rasporeivaa uvek postoji i da sve Swing dogaaje obraduje vadei ih iz reda za ekanje i izvravajui ih jedan za drugim . Ukoliko ne zaboravite na nit rasporeivaa, manje su anse da e vaa aplikacija upasti u uzajamnu blokadu ili uslov za trku. U narednom odeljku razm otriem o oblasti vien itn og izvravanja na koje treba obratiti panju prilikom rada u Swingu.

Poglavlje 22: Grafika korisnika okruenja

1111

Dugotrajni zadaci
Jedna od osnovnih greaka pri programiranju grafikih korisnikih okruenja jeste upotreba rasporedivaa za izvravanje dugotrajnog zadatka. Sledi jednostavan primer:
//: gui/DugotrajanZadatak.java // Loe projektovan program. import javax.swing.*; import java.awt.*; import java.awt.event.*; import java.util.concurrent.*; import static net.mindview.util.SwingKonzoia.*; public class DugotrajanZadatak extends JFrame { private JButton dl = new JButton("Pokreni dugotrajan zadatak"), d2 = new JButton("Ugasi augotrajan zadatak"); public DugotrajanZadatakf) { dl.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { try { TimeUnit.SEC0NDS.sleep(3); } catch(InterruptedException e) { System.out.pri ntln(MZadatak preki nut"); return;

}
System.out.println("Zadatak zavren");

} });
d 2 .addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { // Da se prekinem? Thread.currentThread().interrupt();

} });
setLayout(new FlowLayout()); add(dl); add(d2);

}
public static void main(String[] args) { run(new DugotrajanZadatak(), 200, 150);

} } ///: Kada pritisnete d I, nit rasporeivaa iznenada poinje da izvrava dugotrajan zadatak. Videcete da ni to dugm e nece stii da se vrati iz utisnutog poloaja, poto je mt rasporeivaa, koja bi inae osveila sliku na ekranu, zauzeta tokoin cele 3 sekunde. A i nita drugo ne m oete da uradite, npr. da pritisnete d2, poto program ne odgovara dok se zadatak dugm eta d l ne zavri i nit rasporeivaa opet ne postane dostupna. Kod u d2 je pogrean pokuaj da se problem rei prekidanjem niti rasporeivaa.

1112

Misliti na Javi

Naravno, reenje je da se dugotrajni procesi izvravaju u zasebnim nitim a. Sada em o upotrebiti jed n on itn i izvrilac (Executor, koji pozivajue zadatke autom atski ubacuje u red za ekanje. Odatle ih vadi i izvrava jedan po jedan:
/ / : gui/DugotrZadKojiSeMozePrekinuti.java // Dugotrajni zadaci u nitima. import javax.swing.*; import java.awt.*; import java.awt.event.*; import java.util.concurrent.*; import static net.mindview.util.SwingKonzola.*; class Zadatak implements Runnable { private static int brojac = 0; private final int id = brojac++; public void run() { System.out.println(this + " pokrenut"); try { TimeUnit.SEC0NDS.sleep(3); } catcn(InterruptedException e) { System.out.println(this + " prekinut"); return;

}
System.out.println(this + " zavren1 ');

}
public String toString() { return "Zadatak " + id; } public long id() { return id; }

public class DugotrZadKojiSeMozePrekinuti extends JFrame { private JButton dl = new JButton("Pokreni dugotrajan zadatak"), d2 = new JButton("Ugasi dugotrajan zadatak"); ExecutorService izvrsilac = Executors.newSingleThreadExecutor(); public DugotrZadKojiSeMozePrekinuti () { dl.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { Zadatak zadatak = new Zadatak(); izvrsilac.execute(zadatak); System.out.println(zadatak + " dodat u red za ekanje");

} });
d 2 .a d dA ct io nL istener(new ActionListener() {

public void actionPerformed(ActionEvent e) { izvrsilac.shutdownNow(); // Prejako

} });

Poglavlje 22: Grafika korisnika okruenja

1113

setLayout(new FlowLayout()); add(dl); add(d2);

}
public static void main(String[] args) { run(new DugotrZadKojiSeMozePrekinuti(), 200, 150);

} } ///:O vo je bolje, ali kada pritisnete dugm e d2, on o e pozvati m etodu sh utdow n N ow ( ) za ExecutorService i tako ugasiti taj zadatak. U koliko pokuate da dodate jo zadataka, dobiete izuzetak. Dakle, pritisak na d2 ini da program umre. eleli sm o da ugasim o tekui zadatak (i otkaem o zadatke u redu za ekanje), a ne da gasim o ceo program . Treba nam ba m ehanizam Jave SE5 Callable/Future opisan u poglavlju Paralelno izvravanje. D efinisaem o novu klasu MenadzerZadataka; ona sadri torku koja obuhvata objekat tipa Callable. Callable je zadatak i objekat tipa Future koji je rezultat tog zadatka. Torka je potrebna zato to nam om oguuje da pratim o prvobitni zadatak, kako bism o im ali i d odatne inform acije kojima Future ne raspolae. Evo kako to izgieda:
//: net/mindview/util/StavkaZadatka.java // Objekat tipa Future i Callable koji ga je napravio. package net.mindview.util; import j a v a . u t i l .concurrent.*; public class StavkaZadatka<R,C extends C a l l a b l e < R { public final Future<R> b u d u c i ; { public final C zadatak; public StavkaZadatka(Future<R> b u d u c i , C zadatak) thi s.buduci = buduci; this.zadatak = zadatak;

} ///: -

U biblioteci java.util.concurrent zadatak nije podrazum evano dostupan preko klase Future, poto on ne mora ni postojati kada od kiase Future dobijete rezultat. Ovde zadatak postoji zato to sm o ga snimili. MenadzerZadataka je stavljen u paket net.m indview.util da bi bio dostupan kao usluna klasa opte namene:
//: net/mindview/util/MenadzerZadataka.java // Upravljanje redom zadataka i njegovo izvravanje. package net.mindview.util; import j a v a .ut i1 .co nc u r r e n t .41; import j a v a . u t i1.*; public class MenadzerZadataka<R,C extends C a l l a b l e < R extends ArrayLi s t < S t a v k a Z a d a t k a < R , C {

1114

Misliti na Javi

private ExecutorService exec = Executors.newSi ngleThreadExecutor(); public void add(C zadatak) { add(new StavkaZadatka<R,C>(exec.submit(zadatak).zadatak));

}
public List<R> getResults() { Iterator<StavkaZadatka<R,C stavke = iterator(); List<R> rezultati = new ArrayList<R>(); while(stavke.hasNext()) { StavkaZadatka<R,C> stavka = stavke.next(); if (stavka.buduci .isDoneO) { try { rezultati.add(stavka.buduci.get()); } catch(Exception e) { throw new RuntimeException(e);

}
s t av k e.r em ov eO ;

} }
return rezultati;

}
public List<String> purge() { Iterator<StavkaZadatka<R,C stavke = iterator(); List<String> rezultati = new ArrayList<String>(); whi1e(stavke.hasNext()) { StavkaZadatka<R,C> stavka = stavke.next(); // Ostavi zavrene zadatke radi izvetavanja o rezultatima: if (Istavka.buduci .isDoneO) { rezultati,add("Otkazujem " + stavka.zadatak); stavka.buduci.cancel(true); // Moe da prekine stavke.remove();

} }
return rezultati;

} III-MenadzerZadataka je lista tipa ArrayList ije su stavke tipa StavkaZadatka. On sadri i jednonitni Executor, pa kada pozovete m etodu a d d ( ) i prosledite joj objekat tipa Callable, ona alje taj Callable, a rezultujui objekat tipa Future skladiti zajedno s prvobitnim zadatkom. Na taj nain, ako ita bude trebalo da se radi s tim zadatkom, imaete referencu na njega. Kao jednostavan primer, u metodi p u rg e( ) koristi se m etoda toString( ) tog zadatka. To em o sada upotrebiti za upravljanje dugotrajnim zadacima u naem primeru:
// : gui/DugotrCallableKoj1SeMozePrekinuti.java // Korienje Callable objekata za dugotrajne zadatke. import javax.swing.*; import java.awt.*;

Poglav[je 22: Grafika korisnika okruenja

1115

import java.awt.event.*; import java.uti1.concurrent.*; import net.mindview.util.*; import static net.mindview.util.SwingKonzola.*; class CallableZadatak extends Zadatak implements Callable<String> ( publ ic String call () { run(); return "Povratna vrednost od " + this;

} }
public class DugotrCallableKojiSeMozePrekinuti extends JFrame { private JButton dl = new JButtonC'Pokreni dugotrajan zadatak"), d2 = new JButton("Ugasi dugotrajan zadatak"), d3 = new JButton("Daj rezultate"); private MenadzerZadataka<String,CallableZadatak> menadzer = new MenadzerZadataka<String,CallableZadatak>(); public DugotrCallableKojiSeMozePrekinuti() { dl.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { Cal 1ableZadatak zadatak = new CallableZadatak(); menadzer.add(zadatak); System.out.println(zadatak + " dodat u red za ekanje");

} });
d2.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { for(String rezultat : menadzer.purge()) System.out.pri ntln (rezul tat);

} });
d 3 .addActionListener(new ActionListener() { public void actionPerformed(A*tionEvent e) { // Primer poziva metode klase Zadatak: for(StavkaZadatka<String,CallableZadatak> tt : menadzer) tt.zadatak.id(); // Nije potrebna eksplicitna konverzija tipa for(String rezultat : menadzer.getResults()) System.out.println(rezultat);

} /);
setLayout(new FlowLayout()); add(dl); add (d2); add (d3);

1116

Misliti na Javi

}
public static void main(String[] args) { run(new DugotrCallableKojiSeMozePrekinuti(), 200, 150);

} } ///: Kao to vidite, CallableZadatak radi isto to i Zadatak, sem to vraa i rezultat - u ovom sluaju String identifikator zadatka. Za reavanje istog problem a napisane su i n e-Sw ing uslune klase (koje se ne isporuuju u standardnoj distribuciji Jave). To su SwingWorker (nai ete je na Sunovoj Web lokaciji) i F oxtrot (na adresi http://foxtrot.sourceforge.net). D ok sam pisao knjigu, te klase nisu bile m odifikovane tako da iskoriste m ehanizam Jave SE5 Callable/Future. esto krajnjem korisniku valja dati nekakav vizuelni znak da se zadatak izvrava i obavestiti ga dokle je odm aklo izvravanje. To se ob in o radi p om o u klasa JProgressBar ili ProgressMonitor. U narednom prim eru upotrebiu ProgressMonitor:
//: gui/NadziranDugotrCal 1 able.java // Prikazivanje napredovanja zadatka pomou klase ProgressMonitors. import javax.swing.*; import java.awt.*; import java.awt.event.*; import java.util.concurrent.*; import net.mindview.util.*; import static net.mindview.util.SwingKonzola.*; class NadziranCallable implements Callable<String> { private static int brojac = 0; private final int id = brojac++; private final ProgressMonitor monitor; private final static int MAX = 8; public NadziranCallable(ProgressMonitor monitor) { this.monitor = monitor; monitor.setNote(toStringO); monitor.setMaximum(MAX - 1); monitor.setMil1isToPopup(500);

}
public String cal 1 () { System.out.println (this + " pokrenut"); try { for(int i = 0; i < MAX; i++) { TimeUnit.MILLISECONDS.sleep(500); if(moni tor.i sCanceled()) Thread.currentThreadf).interrupt();
final int napredovanje = i;

Swinglltilities.invokeLater( new Runnable() { public void run() { monitor.setProgress(napredovanje);

Poglavlje 22: Grafika korisnika okruenja

11 17

} } ); }
} catch(InterruptedException e) { monitor.close(); System.out.println(this + " prekinut"); return "Rezultat: " + this + " prekinut";

}
System.out.println(this + " zavren"); return "Rezultat: " + this + " zavren";

}
public String toString() { return "Zadatak " + id; }

};
public class NadziranDugotrCallable extends JFrame { private JButton dl = new JButton("Pokreni dugotrajan zadatak"), d2 = new JButton("Ugasi dugotrajan zadatak"), d3 = new JButton("Daj rezultate1 '); private MenadzerZadataka<String,NadziranCallable> menadzer = new MenadzerZadataka<String,NadziranCallable>(); public NadziranDugotrCallableO { dl.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { NadziranCal1able zadatak = new NadziranCallable( new ProgressMonitor( Nadzi ranDugotrCal1able.thi s , "Dugotrajan Zadatak", 0, 0)

);
menadzer.add(zadatak); System.out.println(zadatak + " dodat u red za ekanje");

} });
d2.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { for(String rezultat : menadzer.purge()) System.out.println(rezultat);

} });
d3.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { for(String rezultat : menadzer.getResults()) System.out.println(rezultat);

) });
setLayout(new F1owLayout()); add(dl); add(d2);

1118

Misliti na Javi

add(d3);

}
public static void main(String[] args) { run(rew NadziranDugotrCallable(), 200, 500);

} } ///:Konstruktor klase NadziranCallable prim a objekat tipa ProgressM onitor kao argum ent, a njegova m etoda c a ll( ) aurira ProgressM onitor svako pola sekunde. Imajte u vidu da je NadziranCallable zaseban zadatak i da ne bi trebalo da neposredno kontrolie ulazno/izlazne operacije, pa sam m onitoru m etod om SwingUtilities.invokeLater( ) podneo informacije o prom eni stepena napredovanja. U Sunovom udbeniku za Swing (Swing Tutorial" na adresi http://java.sun.com ) prikazan je drugaiji pristup; tam o se upotrebljava Swing Timer, koji proverava status zadatka i aurira m onitor. Ako se na m onitoru pritisne dugm e cancel, m etoda m onitor.isC anceled( ) vraa true. Ovde zadatak sam o poziva m etodu in terru p t( ) za sopstvenu nit, to e ga dovesti u odredbu catch gde se m onitor gasi m etod om cIo se( ). Ostatak koda je uglavnom isti kao pre, sem to se pravljenje objekta tipa ProgressMonitor odvija unutar konstruktora zadatka NadziranDugotrCallable.

Veba 33; (6) Izmenite DugotrCallableKojiSeMozePrekinuti.java tako da sve zadatke


izvrava paralelno, a ne sekvencijalno.

Vizuelno vienitno programiranje


U narednom primeru napraviem o Runnable objekat tipa IPanel (pano) koji sebe boji raznim bojama. Aplikacija je podeena tako da s kom andne linije prim a parametre koji odreuju veliinu tabele boja i - m etod om sle e p ( ) trajanje spavanja izm eu prom ena boja. Poigrajte se s tim parametrima i m ogli biste otkriti zanim ljiva i m oda neobjanjiva obeleja vienitnog izvravanja na vaoj platformi:
//: gui/ObojeneKutije.java // Vizuelni prikaz vienitnog izvravanja. // {Args: 12 50} import javax.swing.*; import java.awt.*; import java.util.concurrent.*; import java.util.*; import static net.mindview.uti 1 .SwingKonzola.*; class ObKut extends JPanel implements Runnable { private int pauza; private static Random rand = new RandomO; private Color boja = new Color(O); public void paintComponent(Graphics g) { g.setColor(boja); Dimension velicina = getSize();

Poglavlje 22: Grafika korisnika okruenja

1119

g.fillRect(0, 0, velicina.sirina, velicina.visina);

}
public 0bKut(int pauza) { this.pauza = pauza; } public void run() { try { while(!Thread.interrupted()) { boja = new Color(rand.nextInt(OxFFFFFF)); // Asinhron poziv metode paint(), tj. zahtev da se slika nanovo iscrta: repaint(); TimeUnit.MILLISECONDS.sleep(pauza);

}
} catch(InterruptedException e) { // Prihvatljiv nain izlaska

} }

public class ObojeneKutije extends JFrame { private int brojcelija = 12; private int pauza = 50; private static ExecutorService exec = Executors.newCachedThreadPool(); public ObojeneKutije() { setLayout(new GridLayout(brojcelija, brojcelija)) ; for(int i = 0; i < brojcelija * brojcelija; i++) { ObKut ok = new ObKut(pauza); add(ok); exec.execute(ok);

} }
public static void main(String[] args) { ObojeneKutije kutije = new ObojeneKutije(); if(args.1ength > 0) kutije.brojcelija = new lnteger(args[0]); if(args.length > 1) kutije.pauza = new Integer(args[1]); run(kutije, 500, 400);

} } ///: -

ObojeneKutije konfigurie objekat tipa GridLayout tako da u svakoj dim enziji ima brojcelija. Zatim dodaje odgovarajui broj ObKut objekata da popuni taj broj elija i svakom prosleuje broj pauza. U m etodi m a in ( ) vidite da pauza i brojcelija imaju podrazum evane vrednosti koje se m ogu menjati prosleivanjem argumenata na komandnoj liniji. bve se radi u kiasi ObKut. Ona je izvedena iz klase JPanel i realizuje interfejs Runnable, tako da svaki objekat tipa JPanel m oe biti nezavisan zadatak. Tim zadacim a upravlja grupa niti ExecutorService.

1120

Misliti na Javi

Tekua boja elije je boja. Boje pravi konstruktor klase Color iji ulazni argum ent treba da bude 24-bitni broj koji u ovom sluaju pravim o nasum ino. M etoda paintC om ponent( ) sasvim je jednostavna; ona sam o menja boju kako joj kae boja i ceo pano (objekat tipa JPanel) popunjava njom . U m etodi r u n ( ) postoji beskonana petlja koja objektu boja zadaje novu nasum ino napravljenu boju i zatim poziva rep a in t( ) da bi je pokazala. Potom ta nit - prim enom m etode sle e p () - odlazi na spavanje, za vrem e zadato na kom andnoj liniji. Poziv m etode repaint( ) unutar m etod e r u n ( ) zasluuje panju. Na prvi pogled izgleda da pravim o m nogo niti i da se sve m oraju prebrojiti. M oda vam izgleda da tim e krim o princip na osnovu koga sve zadatke treba poslati rasporeivau, od n osn o redu za ekanje. M eutim , te niti zapravo ne m odifikuju deljeni resurs. Kada one p ozovu m etodu rep ain t( ), ona ne preduzim a prebojavanje nego sam o postavlja odreeni indikator ,,zamrljano, kojim pokazuje da je ta oblast kandidat za prebojavanje kada rasporeiva sledei put bude sprem an za tu akciju. Zato program ne prouzrokuje problem e sa Swingovim nitima. Kada nit rasporeivaa dogaaja pozove m etodu p a in t( ), ona prvo pozove p aintC om ponent( ), zatim paintB order( ) i onda paintC hildren( ). Ako u izvedenoj kom ponenti elite da redefiniete m etodu p a in t( ), ne zaboravite da pozovete verziju p a in t( ) iz osnovn e klase, tako da se uradi ta treba. Upravo zato to je ovaj dizajn prilagodljiv i niti su vezane za svaki elem ent panoa, m oete da eksperim entiete i pravite proizvoljan broj niti. (U praksi, ogranienje nam ee broj niti s kojima vaa JVM m oe da izae na kraj.) Ovaj program om oguuje zanim ljivo poreenje perform ansi, poto m oe da pokae velike razlike u ponaanju i perform ansam a raznih realizacija JVM niti i raznih platformi.

Veba 34: (4) Izm enite program ObojeneKutije.java tako da prvo nasum ino prska take (,,zvezde) po platnu, a zatim nasum ino menja boje tih ,,zvezda.

Vizuelno programiranje i zrna Jave


D osad ste u ovoj knjizi videli koliko je Java korisna za pisanje viekratno upotrebljivog koda. Jedinica koda koja se najee m oe p on ovo iskoristiti jeste klasa jer se sastoji od karakteristika (polja) i ponaanja (m etoda) koje se niogu pon ovo koristiti - kom pozicijom ili nasleivanjem. Nasledivanje i polim orfizam su osn ovn i principi objektno orijentisanog programiranja, ali kada piete aplikaciju, najee se trudite da kom ponente rade tano on o to vi hoete. Zeleli biste da ubacite te delove u svoj program kao to elektroinenjer postavlja ipove na plou. Takoe, izgleda da bi trebalo da postoji neki nain za ubrzavanje ovakvog stila programiranja koji bi se m ogao nazvati sklapanje od deIova.
V i/.ueln o p r o g r a m i r a n j e " je d o i \ elo svoj p r \ i u s p e h , i to i 'cliki, sa M i c r o s o i t o v i m je-

zikom Visual BASIC (VB). Nakon njega se pojavila druga generacija iji je najistaknutiji predstavnik B orlandov Delphi; on je bio i najvea inspiracija za zrna Jave. U tim program erskim alatkama kom ponente su predstavljene vizuelno, to ima sm isla poto one obino prikazuju neki grafiki elem ent, kao to su dugm e ili polje za tekst. Zapravo, takva

Poglav[je 22: Grafika korisnika okruenja

1121

vizuelna predstava je esto izgled kom ponente u program u koji se izvrava. Zbog toga deo postupka vizuelnog programiranja podrazum eva prevlaenje kom ponente s palete i sputanje na obrazac. Alatka za pravljenje aplikacija pie kod dok vi prevlaite kom p onente, a taj kod prouzrokuje pravljenje kom ponente u izvrnom programu. Sam o prevlaenje kom ponente u obrazac obino nije dovoljno za kom pletiranje programa. esto m orate da prom enite neka njena obeleja, npr. boju, tekst koji sadri, bazu podataka s kojom je povezana itd. Obeleja koja se m ogu prom eniti tokom projektovanja oznaavaju se kao svojstva (engl. properties). Sa svojstvim a kom ponente m oete da radite p om o u alatke za pravljenje aplikacija, a kada napravite program , svojstva kom p on en te e biti snim ljena da bi se m ogla rekonstruisati kada se program pokrene. D osad ste se sigurno navikli na ideju da objekat nem a sam o svojstva, ve i ponaanje. Tokom projektovanja, ponaanje vizuelne kom ponente je delim ino predstavljeno dogaajim a. Dogaaj znai: Evo neega to bi se m oglo dogod iti kom p on en ti. O bino od lu ujete ta treba da se desi nakon dogaaja tako to povezujete kod sa odreenim dogaajem . Evo izuzetno vanog dela: alatka za pravljenje aplikacija koristi refleksiju za dinam iko ispitivanje k om p on ente i otkrivanje svojstava i dogaaja koja kom ponenta podrava. Kada to sazna, m oe da prikae svojstva i om ogu i a ih m enjate (uz snim anje stanja), ali m oe i da prikae dogaaje. Po pravilu, vi dvaput pritiskate m iem , a alatka za pravljenje aplikacija pravi kod i povezuje ga s tim dogaajem . U tom trenutku treba sam o da napiete kod koji se izvrava kada se desi dogaaj. Dakle, alatka za pravljenje aplikacija obavlja najvei deo posla um esto vas. Zbog toga se m oete usredsrediti na izgled programa i na to ta on treba da radi, a da se oslonite da e ta alatka obaviti kom pletno povezivanje. Alatke za vizuelno programiranje izuzetno su popularne zato to prim etno ubrzavaju pravljenje aplikacije. To posebno vai za korisnika okruenja, ali esto i za druge delove aplikacije.

ta je zrno?
Kada se praina slegne, vidi se da je kom ponenta sam o blok koda koji je ob in o predstavljen u obliku klase. Kljuno pitanjeje m ogunost alatke za pravljenje aplikacija da otkriva svojstva i dogaaje te kom ponente. Da bi napravio VB kom ponentu, programer je m orao da pie prilino sloen kod u kom e je potovao odreena pravila za otkrivanje svojstava i dogaaja. Delphi je bila alatka za vizuelno programiranje druge generacije. Taj jezik je aktivno koristio vizuelno programiranje pa se vizuelna kom ponenta m n ogo lake pravila. M edutim , izradu vizuelnih kom ponenata na najvii nivo dovela je Java, i to p om ou zrna (engl. Java Bcans J, poto je zrno sam o klasa. Ne m orate pisati nikakav dodatni kod niti koristiti specijalna proirenja jezika da bi neto postalo zrno. Zapravo, treba sam o m alo dn prom enite nain im enovanja metoda. Na osn ovu imena m etode, alatka za pravljenje aplikacija zna da li se radi o svojstvu, dogadaju ili obinoj m etodi. U dokum entaciji za Javu ova pravila im enovanja pogreno su oznaena kao projektni obrazac. To je nesporazum , poto su projektni obrasci dovoljan izazov i bez ove zbrke (nai ete ih u knjizi T h in kin g in Patterns w ith Java koju m oete preuzeti s lokacije

1 122

Misliti na Javi

w w w .M ind V iew .n et). Zrna Jave nisu opisana projektnim obrascem , ve sam o pravilim a za im enovanje, i to prilino jednostavnim :

1 . Da bi se neko svojstvo nazvalo xxx, ob in o treba napraviti dve metode: getX xx() i setX x x ( ). Obratite panju na to da se prvo slovo iza get ili set autom atski pretvara
u m alo da bi se dobilo im e svojstva. Tip koji vraa m etoda get isti je kao tip argum enta m etode set. Im e svojstva i tip ovi koje vraaju m etode get i set ne moraju da b udu povezani.

2. Za svojstvo tipa boolean m oete da koristite prethodni pristup s m etodam a get i


set, ali um esto get m oete da koristite is.

3. O bine m etode zrna ne potuju prethodna pravila za dodeljivanje im ena, ali su javne.

4. Za dogaaje se koristi pristup ,,prijemnika iz biblioteke Swing. To je isto on o to


ste ve videli: m etodam a addBounceListener(BounceListener) i removeBounceListener(BounceListener) obrauje se dogaaj BounceEvent. Najee e ugraeni dogaaji i prijem nici zadovoljavati vae potrebe, ali m oete da napravite i sopstvene dogaaje i prijem nike interfejse. Ova saznanja m oem o da iskoristim o za pravljenje jednostavnog zrna:
//: frogbean:Frog.java // Obino zrno Jave. package frogbean; import java.awt.*; import java.awt.event.*; class Spots {} public class Frog { private int jumps; private Color color; private Spots spots; private boolean jmpr; public int getJumps() { return jumps; ) public void setJumps(int newjumps) { jumps = newJumps;

}
public Color getColor() { return color; } public void setColor(Color newColor) { color = newColor;

}
public Spots getSpots() { return spots; } public void setSpots(Spots newSpots) { spots = newSpots;

}
public boolean isJumper() { return jmpr; } public void setJumper(boolean j) { jmpr = j; }

Poglavlje 22: Grafika korisnika okruenja

1123

public void addActionListener(ActionListener 1) {

II...
}
public void removeActionListener(ActionListener 1) {

// }
public void addKeyListener(KeyListener 1) {

/ / }
public void removeKeyListener(KeyListener 1) {

// }
// "Obina" javna metoda: public void croak() { System.out.println("Ribbet!");

} } ///:Prvo uoavate da je ovo obina klasa. Sva njena polja ob in o e biti privatna i m oi e da im se pristupa sam o preko m etoda. U skladu s pravilom za im enovanje, svojstva su jum ps, color, spots i jum per (obratite panju na prom enu veliine prvog slova im ena svojstva). Iako je im e lokalnog identifikatora isto kao im e svojstva u prva tri sluaja, svojstvo jum per pokazuje da im e svojstva nije uslov za korienje tano odreenog identifikatora za lokalne prom enljive. (Lokalne prom enljive za to svojstvo ak ne moraju ni da postoje.) Dogaaji koje generie ovo zrno su ActionEvent i KeyEvent, to zakljuujemo po im enim a m etoda add i rem ove za rad s prijem nicim a. Konano, vidite da je obina m etoda cro a k ( ) i dalje deo zrna sam o zato to je javna, a ne zato to potuje pravilo dodeljivanja im ena.

Ispitivanje zrna klasom Introspector


Jedna od najosetljivijih strana zrna uoava se kada zrno prevlaite s palete i sputate na obrazac. Alatka za pravljenje aplikacije mora biti u stanju da napravi zrno (to m oe da uradi ako postoji podrazum evani konstruktor) i da zatim , bez pristupanja njegovom izvorn om kodu, izdvoji sve neop h od n e inform acije kako bi napravila spisak svojstava i blokove za obradu dogaaja. D eo reenja ete nai na kraju poglavlja Podaci o tipu: favina refleksija om oguuje da se otkriju sve m etode bilo koje klase. To je savreno za reavanje problem a sa zrnim a bez korienja dodatnih rezervisanih rei jezika, to je potrebno u drugim jezicim a za vizuelno programiranje. Jedan od najvanijih razloga za dodavanje refleksije u Javu i jeste bilo podravanje zrna (mada refleksija om oguuje i seriializaciju objekata i daljinsko pozivanje m etod a). Dakle, m oe se oekivati da autor alatke za pravljenje aplikacija mora ispitivati svako zrno i pregledati njegove m etode, da bi saznao koja su njegova svojstva i m etode.

1124

Misliti na Javi

To je izvesno m ogue, ali su projektanti Jave eleli da obezbede standardnu alatku, ne sam o da bi se zrna lake koristila, ve i da bi se obezbedio standardizovan nain za pravljenje sloenijih zrna. Ta alatka je Idasa Introspector, a njena najvanija m etoda je static getB eanInfo( ). Ovoj m etodi se prosleuje referenca na objekat ldase Class, a ona iscrpno ispituje klasu i vraa objekat tipa Beanlnfo koji p otom m oete analizirati da biste saznali svojstva, m etode i dogaaje zrna. O b ino vam nita od toga nee biti vano; najvei broj zrna koristiete gotova i nema potrebe da znate detalje koji se deavaju unutar njih. Sam o ete prevlaiti zrna na obrazac, zatim podeavati njihova svojstva i pisati procedure za dogaaje koji vas zanim aju. M eutim , veba korienja klase Introspector za prikazivanje inform acija o zrnu zanimljiva je i pouna, pa evo alatke koja to radi:
//; gui/IspitivanjeZrna.java // Ispitivanje Zrna klasom Introspector. import javax.swing.*; import java.awt.*; import java.awt.event.*; import java.beans.*; import java.lang.reflect.*; import static net.mindview.util.SwingKonzola.*; public class IspitivanjeZrna extends JFrame { JTextField upit = new JTextField(20); JTextArea rezultati = new JTextArea(); public void print(String s) { rezultati.append(s + "\n"); ) public void ispitaj(C1ass<?>zrno){ rezultati.setText(""); Beanlnfo bi = null; try { bi = Introspector.getBeanInfo(zrno, Object.class); ) catch(IntrospectionException e) { print("Ne mogu da ispitam " + zrno.getName()); return;

(
for(PropertyDescriptor d: bi,getPropertyDescriptors()){ Class<?> p = d.getPropertyType(); if(p == null) continue; print("Tip svojstva:\n " + p.getName() + "Ime svojstva:\n " + d .getName()); Method metodaCitanja = d.getReadMethod(); if(metodaCitanja != null) print("Metoda za itanje:\n " + metodaCitanja);
Method metodaUpisivanja = d . g e t W r i t e M e t h o d ();

if(metodaUpisivanja != null) print("Metoda za upisivanje:\n print("====================");

" + metodaUpisivanja);

Poglavlje 22: Grafika korisnika okruenja

1125

print("Javne metode:"); for(MethodDescriptor m : bi .getMethodDescriptorsO) print(m.getMethod() .toSt r i n g O ) ; print("======================"); print("Podrka za dogaaje:"); for(EventSetDescriptor e : bi .getEventSetDescriptorsO) { print("Tip pri jemnika:\n 1 1 + e.getListenerType() .getNameO); for(Method lm : e.getListenerMethodsO) print.("Metoda pri jemnika:\n " + lm.getName()); for(MethodDescriptor lmd : e.getListenerMethodDescriptorsO) print("Opis metode:\n " + lmd.getMethod()); Method dodavanje = e.getAddListenerMethod(); print("Metoda za dodavanje:\n " + dodavanje); Method uklanjanje = e.getRemoveListenerMethod(); printC'Metoda za uklanjanje:\n " + uklanjanje); print("====================");

} }
class Ispitivac implements ActionListener { public void actionPerformed(ActionEvent e) { String ime = upit.getText(); C1ass<?>c = nul1; try { c = Class.forName(ime); } catch(ClassNotEoundException ex) { rezultati.setText("Ne mogu da pronaem" + ime); return;

}
ispitaj(c);

public IspitivanjeZrnaO { JPanel p = new JPanel(); p.setLayout(new FlowLayout()); p.add(new JLabel("Puno ime zrna:")); p.add(upit); cp.add(BorderLayout.NORTH, p ) ; cp.add(new JScrollPane(rezultati)); Ispitivac isp = new Ispitivac(); upit.addActionListener(isp); upi t .setText("frogbean.Frog"); // Prinudna provera isp.actionPerformed( new ActionEvent(isp, 0, ""));

}
public static void main(String[] args) { run(new IspitivanjeZrnaO, 600, 500);

} ///:-

1126

Misliti na Javi

Sav p osao obavlja m etoda IspitivanjeZrna.ispitaj( ). Ona prvo pokuava da napravi objekat interfejsa Beanlnfo, a ako uspe, poziva m etod e koje vraaju inform acije o svojstvim a zrna, m etodam a i dogaajima. U m etodi Introspector.getBeanInfo( ) prim etiete drugi argum ent. O n kae objektu klase gde da se zaustavi u hijerarhiji nasleivanja. Ovde se zaustavlja pre nego to analizira m etode klase Object, poto nas on e ne interesuju. M etoda getPropertyD escriptors( ) vraa niz objekata tipa PropertyDescriptor koji opisuju svojstva. Za svaki deskriptor svojstava (objekat tipa PropertyDescriptor) m oete da pozovete m etodu getPropertyType( ) da biste otkrili klasu objekta koji se prosleuje unutra i napolje p om ou m etoda svojstava. Zatim, za svako svojstvo m oete da dobijete pseud onim (generisan na osn ovu im ena m etoda) p o m o u m etode g etN a m e( ), m etodu za itanje p o m ou getR eadM ethod( ) i m etodu za upisivanje p om ou getW riteMethod( ). D ve poslednje m etode vraaju objekat tipa M ethod koji se m oe koristiti za pozivanje odgovarajue m etode objekta (to je deo refleksije). to se tie javnih m etoda (ukljuujui i m etode svojstava), getM ethodD escriptors( ) vraa niz objekata klase M ethodDescriptor. M oete da dobijete objekat tipa M ethod p ovezan sa svakim od tih objekata i da ispiete njegovo ime. to se tie dogaaja, m etoda getEventSetDescriptors( ) vraa niz (ega drugog nego) objekata tipa EventSetDescriptor. Svaki od njih se m oe ispitati da bi se otkrila klasa prijem nika, m etode prijem nike klase, kao i m etode za dodavanje i uklanjanje prijemnika. Program IspitivanjeZrna ispisuje sve ove informacije. Pre izvravanja, program proverava zrno frogbean.Frog. Nakon uklanjanja dodatnih, nepotrebnih detalja, rezultat izgleda ovako:
Tip svojstva: Color Ime svojstva: color Metoda za itanje: public Color getColor() Metoda za upisivanje: public void setColor(Color) Tip svojstva: boolean Ime svojstva: jumper Metoda za itanje: public boolean isJumper() Metoda za upisivanje: public void setJumper(boolean) Tip svojstva: int Ime svojstva: jumps Metoda za itanje: public int getJumps()

Poglavlje 22: Grafika korisnika okruenja

1127

Metoda za upisivanje: public void setJumps(int) Tip svojstva: frogbean.Spots Ime svojstva: spots Metoda za itanje: public Spots getSpots() Metoda za upisivanje: public void setSpots(Spots) Javne metode: public void setSpots(frogbean.Spots) public void setColor(Color) public void setJumps(int) public boolean isJumper() public frogbean.Spots getSpotsO public void croak() public void addActionListener(ActionListener) public void addKeyListener(KeyListener) public Color getColor() public void setJumper(boolean) public int getJumps() public void removeActionListener(ActionListener) public void removeKeyListener(KeyListener) Podrka za dogaaje: Tip prijemnika: KeyLi stener Metoda prijemnika: keyTyped Metoda prijemnika: keyPressed Metoda prijemnika: keyReleased Opis metode: public void keyTyped(KeyEvent) Opis metode: public void keyPressed(KeyEvent) Opis metode: public void keyReleased(KeyEvent) Metoda za dodavanje: public void addKeyListener(KeyListener) Metoda za uklanjanje: public void removeKeyListener(KeyListener) Tip prijemnika: Acti onLi stener

1128

Misliti na Javi

Metoda prijemnika: actionPerformed Opis metode: public void actionPerformed(ActionEvent) Metoda za dodavanje: public void addActionListener(ActionListener) Metoda za uklanjanje: public void removeActionListener(ActionListener)

Ovaj program otkriva najvei d eo svojstava koje vidi klasa Introspector dok na osno vu zrna pravi objekat tipa Beanlnfo. Prim etiete da su tip svojstva i njegovo im e nezavisni. Obratite panju na to da su sva slova u im enu svojstva mala. (Jedino se odstupa kada im e svojstva poinje s nekoliko uzastopnih velikih slova.) Zapamtite i to da se im ena m etoda kakva ovde vidite (npr. m etod e za itanje i upisivanje) zapravo dobijaju iz objekta tipa M ethod koji se m oe koristiti za pozivanje odgovarajue m etode. Spisak javnih m etoda sadri m etod e koje nisu povezane s nekim svojstvom ili dogaajem , kakva je m etoda croak ( ), ali i o n e koje jesu povezane sa svojstvima i dogaajima. To su sve m etode zrna koje m oete da pozovete iz programa, a alatka za pravljenje aplikacija m oe ih sve prikazati dok piete program da bi vam olakala posao. Konano, prim euje se da su dogaaji p otp u n o analizirani i razdvojeni na interfejs prijemnika, njegove m etod e i m etod e za dodavanje i uklanjanje prijemnika. U osnovi, kada im ate objekat tipa Beanlnfo, m oete da saznate sve to je vano o zrnu. M oete i da pozivate m etode zrna, ak i ako nem ate nikakve druge inform acije osim ovog objekta (to je jo jedna osobina refleksije).

Naprednije zrno
Sledei prim er je sam o neto m alo napredniji, mada je frivolan. To je objekat klase JPanel koji iscrtava mali krug oko mia kad god se on pom eri. Kada pritisnete taster mia, re Bang! se pojavljuje u sredini ekrana i pokree se prijem nik dogaaja. M oete da prom enite sledea svojstva: veliinu kruga, boju, veliinu i re koja se prikazuje kada se pritisne taster mia. Objekat BangBean takode ima sopstvene m etode addActionListener( ) i rem oveA ctionListener( ) p om ou kojih m oete da poveete zrno sa svojim prijem nikom koji se pokree kada korisnik pritisne m iem . Sada bi ve trebalo da um ete prepoznati podrku za svojstva i dogaaje:
//: bangbean:BangBean.java // Grafiko zrno. package bangbean; import javax. swi ng import java.awt.*; import java.awt.event.*; import java.io.*; import java.uti1.*;

Poglavlje 22: Grafika korisnika okruenja

1129

public class BangBean extends JPanel implements Serializable { protected int xm, ym; protected int cSize = 20; // Velicina kruga protected String text = "Bang!"; protected int fontSize = 48; protected Color tColor = Color.RED; protected ActionListener actionListener; public BangBean() { addMouseListener(new M L ()); addMouseMotionListener(new MML());

1
public int getCircleSize() { return cSize; } public void setCircleSize(int newSize) { cSize = newSize;

}
public String getBangText() { return text; } public void setBangText(String newText) { text = newText;

}
public int getFontSize() fontSize = newSize; { return fontSize; { } public void setFontSize(int newSize)

}
public Color getTextColor() tColor = newColor; { return tColor; { } public void setTextColor(Color newColor)

}
public void paintComponent(Graphics g) { s u pe r. pa in tC om po ne nt( g); g .s e t C o l o r ( C o l o r .B L A C K ) ; g.draw 0v al (x m - cSize/2, ym - cSize/2, cSize, cSize);

}
// Zrno dozvoljava samo jedan prijemnik, a to je // na jj ed no st av niji oblik upravljanja prijemnicima: public void addActionListener (ActionListener 1) throws To oManyListenersException { if (a ct ionListener != null) throw new TooManyListenersException(); a c ti on Li st en er = 1 ;

}
public void removeActionListener(ActionListener 1) { ac ti on Li st en er = null;

}
class ML extends MouseAdapter {
public void mousePressed(MouseEvent e) { Graphics g = ge tG r a p h i c s (); g .s e tC ol or (t Co lo r); g.setFont( new Font("TimesRoman", Font.BOLD, fontSize)); int width = g .getFontMetrics().stringWidth(text);

1130

Misliti na Javi

g.drawString(text, (getSize().width - width) /2, getSizeO .height/2); g.disposeO; // Poziva prijemniku metodu: if(actionListener != n u l O actionListener.actionPerformed( new ActionEvent(BangBean.this, ActionEvent.ACTION_PERFORMED, n u l O ) ;

1 }
class MML extends MouseMotionAdapter { public void mouseMoved(MouseEvent e) { xm = e.getX(); ym = e.getY(); repaint();

} }
public Dimension getPreferredSize() return new Dimension(200, 200); {

} } ///= Prvo ete prim etiti da klasa BangBean realizuje interfejs Serializable. To znai da alatka za pravljenje aplikacija m oe da uita sve inform acije o zrnu p om ou serijalizacije, nakon to projektant programa podesi vrednosti svojstava. Kaa se zrno napravi kao deo izvrne aplikacije, snim ljena svojstva se rekonstruiu pa se dobija tano on o to ste projektovali. U potpisu m etode addA ctionListener( ) vidite da ona m oe da generie izuzetak TooManyListenersException. To znai da je dogaaj jcdnosm eran, tj. da se sam o jedan prijem nik obavetava o tom e da se desio. O b in o ete koristiti viesm erne dogaaje tako da o dogaaju bude obaveteno vie prijem nika. M eutim , ovde ve uiazim o u tem e koje e biti razm otrene u odeljku Zrna Jave i sinhronizacija. Jednosm ernim dogaajem zasad em o zaobii ovaj problem . Kada pritisnete taster mia, u zrnu se ispisuje tekst, a ako polje actionListener nije null, pravi se nov objekat tipa ActionEvent i alje se prijem niku. Kad god se pom eri mi, pam te se njegove nove koordinate i pozadina menja boju (uz brisanje teksta koji se nalazi na pozadini, kao to ete videti). Evo klase BangBeanTest koja om ogu u je testiranje zrna kao apleta ili aplikacije:
//: gui/BangBeanTest.java // {Timeout: 5} U testiranju, ugasi nakon 5 sekundi package bangbean.*; import javax.swing.*; import import import import java.awt.*; java.awt.event.*; java.uti1 .*; static net.mindview.util.SwingKonzola.*;

Poglavlje 22: Grafika korisnika okruenja

1131

public class BangBeanTest extends JFrame { private JTextFie1d txt = new JTextField(20); // Tokom testiranja prijavljuju se akcije: class BBL implements ActionListener { private int count = 0; public void actionPerformed(ActionEvent e) { txt.setText("Akcija zrna"+ count++);

} }
public BangBeanTest() { BangBean bb = new BangBean(); try { bb.addActionListener(new BBL()); ) catch(TooManyListenersException e) { txt.setText("Previe prijemnika");

}
add(bb); add(BorderLayout.SOUTH, txt);

}
public static void main(String[] args) { run(new BangBeanTest(), 400, 500);

} 1 ///:Ova klasa se nee koristiti kada se zrno nalazi u razvojnom okruenju, ali je korisna kao nain za brzo testiranje svih zrna. BangBeanTest stavlja zrno BangBean u aplet, povezujui s njini jednostavan prijem nik tipa ActionListener koji ispisuje broj ogaaja u tekstualnom polju kad god se desi dogaaj. Alatka za izradu aplikacija obino pravi najvei deo koda koji koristi zrno. Kada ispitate ovo zrno Idasom IspitivanjeZrna ili ga stavite u razvojno okruenje koje podrava zrna, prim etiete da ima m n ogo vie svojstava i akcija nego to se vidi u gornjem kodu. Razlog je to to je klasa BangBean izvedena iz klase JPanel, a JPanel je takoe zrno, pa se prikazuju i njegova svojstva i dogadaji.

Veba 35: (6) Pronadite na Internetu i preuzm ite jednu ili vie besplatnih vizuelnih alatki
za pravljenje grafikih korisnikih okruenja, ili kupite neku. Saznajte ta je neophodno da bi se zrno BangBean unelo i koristilo u tom okruenju.

Zrna Jave i sinhronizacija


Kad god napravite zrno, treba da oekujete da e o n o raditi u vienitnom okruenju. To znai sledee: 1. Kad god je m ogue, sve javnc m etode jednog zrna treba sinhronizovati. Naravno, tim e izazivate poveane procesorske trokove koje povlai atribut synchronized. Ako su oni neprihvatljivi, m etode koje nee izazvati problem e u kritinim delovim a programa m ogu da ostanu nesinhronizovane, ali imajte u vidu da to nije uvek lako proceniti. O b in o su kandidati za to male m etode (p opu t m etode getCircleSize() u sledeem prim eru) i (ili) ,,atom ine , pa se m etoda izvrava u tako kratkom vrem enu da objekat ne m oe da bude izm enjen tokom izvravanja. Ako takve m etode

1 132

Misliti na Javi

ostanu nesinhronizovane, izvravanje celog programa m oda se nee znaajno ubrzati. Stoga biste sve javne m etode zrna m ogli da sinhronizujete, a rezervisanu re syn ch ron ized da uklonite sam o ako znate da je to potrebno i da ete tim e znaajno ubrzati izvravanje programa. 2 . Prilikom obavetavanja vie prijemnika (engl. m ultica st ) o nekom dogaaju, m orate pretpostaviti da tokom kretanja kroz taj spisak neki prijem nici m ogu biti dodati, a drugi uklonjeni. Prvu taku je lako uraditi, ali pri izvoenju druge treba m alo razmisliti. U zm im o prim er zrna BangBean.java predstavljenog u prolom poglavlju. Tamo je izbegnuto pitanje korienja vie niti tako to je zanemarena rezervisana re sy n ch ro n ized i obaveten sam o jedan prijem nik (engl. unicast). Evo tog prim era, izm enjenog tako da koristi vie niti i da o dogaajima obavetava vie prijemnika:
//: gui/BangBean2.java // Trebalo bi da svoja zrna ovako piete, // kako bi mogla da se izvravaju u okruenju s vie niti. import javax.swing.*; import import import import import java.awt.*; java.awt.event.*; java.util.*; java.io.*; net.mindview.uti1 .SwingKonzola.*;

public class BangBean2 extends JPanel implements Serializable { private int xm, ym; private int cSize = 20; // veliina krunice private String text = "Bang!"; private int fontSize = 48; private Color tColor = Color.RED; private ArrayList actionListeners = new ArrayList<ActionListener>(); public BangBean2() { addMouseListener(new M L ()); addMouseMotionListener(new M M ());

}
public synchronized int getCircleSiz e () { return cSize; } public synchronized void setCircleSize(int newSize) { cSize = newSize;

}
public synchronized String getBangText() { return text; } public synchronized void setBangText(Strinq n e w T e x t ) ( text = newText;

}
public synchronized int getFontSize() { return fontSize; } public synchronized void setFontSize(int newSize) { fontSize = newSize;

Poglavlje 22: Grafika korisnika okruenja

1133

}
public synchronized Color getTextColor() { return tColor;} public synchronized void setTextColor(Color newColor) { tColor = newColor;

}
public void paintComponent(Graphics g) { super.paintComponent(g); g.setColor(Color.BLACK); g.drawOval(xm - cSize/2, ym - cSize/2, cSize, cSize);

}
// Ovo zrno ima vie prijemnika to se ee koristi // od obavetavanja samo jednog prijemnika, // kako je bilo uraeno u programu BangBean.java: public synchronized void addActionListener(ActionListener 1) { actionListeners.add(l);

}
public synchronized void removeActionListener(ActionListener 1) { actionLi steners.remove(l);

}
// Pazite, ovo nije sinhronizovano: public void notifyListeners() { ActionEvent a = new ActionEvent(BangBean2.this, ActionEvent.ACTION_PERFORMED, null); ArrayLi st 1v = nul 1 ; // Napravi povrnu kopiju spiska za sluaj // da neko doda jedan prijemnik tokom // pozivanja drugih prijemnika: synchronized(this) { lv = new ArrayList<ActionListener>(actionListeners);

}
// Obavesti sve metode prijemnike: for(ActionListener al : 1v) a l .actionPerformed(a);

}
class ML extends MouseAdapter { public void mousePressed(MouseEvent e) { Graphics g = getGraphics(); g.setColor(tColor); g.setFont( new FontC'TimesRoman", Font.BOLD, fontSize)); int width = g.getFontMetricsO .stringWidth(text);
g.drwString(text, (getSize().width - width) / 2,

getSizeO .height/2); g.disposeO; noti fyListeners();

1134

Misliti na Javi

}
class MM extends MouseMotionAdapter { public void mouseMoved(MouseEvent e) { xm = e.getX(); ym = e.getY(); repaint();

} }
public static void main(String[] args) { BangBean2 bb2 = new BangBean2(); bb2.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e){ System.out.println("ActionEvent'' + e ) ;

} } );
bb2.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e){ System.out.println("BangBean2 action");

} });
bb2.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e){ System.out.println("More action");

} });
JFrame frame = new JFrameO; frame.add(bb2); run(bb, 300, 300);

} } ///:Dodavanje rezervisane rei synchronized m etodam a jeste mala i laka izm ena. M eutim , uoite da se u m etodam a addActionListener( ) i rem oveActionListener( ) prijemnici sada dodaju na listu i uklanjaju s nje kako bi zrno m oglo da ima vie prijemnika. Vidi se da m etoda notifyL isteners( ) nije sinhronizovana. Nju m oe istovrem eno da pozove vie niti. M ogue je i da m etode addA ctionListener( ) i rem oveActionListener( ) budu pozvane usred poziva m etode notifyListeners( ), to predstavlja problem jer ta m etoda prolazi kroz listu prijemnika. Da bi se taj problem ublaio, spisak je unutar sinhronizovanog dela koda kloniran p om ou konstruktora spiska prijem nika ArrayList koji kopira elem ente svog argumenta, pa se prolazi kroz klon. Tako se m oe raditi sa originalnim spiskom prijemnika, a da se pri tom ne utie na tnetodu notifyListeners( ). N i m etoda paintComponent( ) nije sinhronizovana. Odiuiti da li treba sinhronizovati redefm isane m etode nije tako lako kao kad se sam o dodaju sopstvene m etode. o\'oni prim eru ispostavlja se da m etoda p ain tC o m p o n en t( ) naizgled dobro radi, bila sinhronizovana ili ne. Ali m orate uzeti u obzir i sledea pitanja:

Poglavlje 22: Grafika korisnika okruenja

1135

T. D a li ta m etoda menja stanje kritinih prom enljivih unutar objekta? D a biste otkrili koje su prom enljive kritine, m orate utvrditi da li e ih druge niti u program u itati ili im zadavati vrednosti. (U ovom sluaju, itanje i zadavanje gotovo se uvek obavlja unutar sinhronizovanih m etoda, pa m oete da ispitate sam o njih.) M etoda paintCom ponentO nem a takvih izm ena stanja. 2 . Da li m etoda zavisi od stanja tih kritinih promenljivih? Ako neka sinhronizovana m etoda m odifikuje prom enljivu koju vaa m etoda koristi, trebalo bi da i nju sinhronizujete. Na osnovu toga m ogli biste uoiti da prom enljivu cSize m odifikuju sinhronizovane m etode, pa bi stoga m etodu paintC om ponent( ) trebalo sinhronizovati. U ovom sluaju, m eutim , m oete da se zapitate: ta se najgore m oe d ogoditi ako se sadraj prom enljive cSize prom eni tokom izvravanja m etode p aintC om p onent( )? Ako zakljuite da to to se m oe desiti nije tako strano, a uz to je i prolazno, m oete da ostavite m etodu p aintC om ponent( ) nesinhronizovanu kako biste izbegli dodatne reijske trokove koje izaziva pozivanje sinhronizovane m etode.

3. Da li je verzija m etode u osnovnoj klasi sinhronizovana? M etoda paintC om ponent( ) nije sinhronizovana u osnovnoj klasi. To nije ba besprekoran argum ent nego sam o povod za razmiljanje. U ovom sluaju, recim o, postoji jedno polje (cSize) koje m odifikuje sinhronizovana m etoda, a njem u pristupa m etoda paintComp o n e n t( ), pa je to m oglo a izm eni situaciju. Imajte u vidu, m eutim , da se atribut synchronized ne nasleuje - znai, ukoliko je neka m etoda sinhronizovana u osnovnoj klasi, ona nepostajc autom atski sinhronizovana u redefinisanoj verziji izvedene klase.

4. M etode paint( ) i paintComponent( ) moraju biti najbre m ogue. Izuzetno se


preporuuje korienje svega to iz njih uklanja reijske trokove, pa ako smatrate da te m etode treba da sinhronizujete, to m oe biti znak loeg projekta. Kod za testiranje unutar m etode m a in ( ) drugaiji je nego onaj u program u BangBeanTest, kako bi se pokazala sposobnost klase BangBean2 da o dogadajim a obavetava vie prijemnika.

Pakovanje zrna
Pre nego to se zrno uveze u vizuelnu alatku, mora se staviti u standardan paket za zrna, a to je JAR arhiva koja sadri sve klase zrna i m anifest datoteku. M anifest datoteka je tekstualna datoteka odreenog oblika. Za zrno BangBean, ta datoteka izgleda ovako:
Manifest-Version: 1.0 Name: bangbean/BangBean.class Java-Bean: True

Prvi red prikazuje broj verzije manifest datoteke koji, do daljeg obavetenja iz kom panije Sun, iznosi 1.0. Drugi red (prazni redovi se zanemaruju) navodi datoteku BangBean.class, a trei kae ,,To je zrno. Bez treeg reda, alatka za pravljenje programa ne bi prepoznavala klasu kao zrno.

1136

Misliti na Javi

Jedina nepovoljnost je to to m orate da obezbedite pravilnu putanju u polju Name:. Ako pon ovo pogledate program BangBean.java, videete da se nalazi u paketu bangbean (pa stoga u poddirektorijum u bangbean koji nije u putanji klase), a im e manifest datoteke m ora da sadri informacije o paketu. O sim toga, m anifest datoteku m orate da sm estite u direktorijum iznad korena putanje paketa, to u ovom sluaju znai da tu datoteku treba sm estiti u direktorijum iznad poddirektorijum a bangbean. Zatim treba da pozovete program jar u istom direktorijumu u k om e se nalazi m anifest datoteka, kao ovde:
jar cmf BangBean.jar BangBean.mf bangbean

O vo podrazum eva da elite da se dobijena JAR datoteka zove BangBean.jar i da se manifest datoteka zove BangBean.mf. M oda se pitate ta se deava sa svim drugim klasama koje se generiu prilikom prevoenja programa BangBean.java. O n e zavravaju u poddirektorijum u bangbean, a prim etiete da je poslednji argum ent na prethodnoj kom andnoj liniji upravo taj poddirektorijum . Kada programu jar date im e nekog poddirektorijum a, on pakuje ceo taj poddirektorijum (ukljuujui, u ovom sluaju, i originalnu datoteku sa izvornim kodom programa BangBean.java; vi ete m oda odluiti da izvorni koti ne pakujete zajedno sa zrnim a). Pored toga, ako otpakujete JAR arhivu koja je upravo napravljena, otkriete da se m anifest datoteka ne nalazi u paketu, ve da je program jar napravio sopstvenu datoteku koja se delim ino zasniva na vaoj, a zove se MANIFEST.MF i nalazi se u poddirektorijum u META-INF (skraenica za m etainform acije). Kad otvorite tu datoteku, prim etiete i da je alatka jar dodala inform acije o digitalnom potpisu svake datoteke u sledeem obliku:
Digest-Algorithms: SHA MD5 SHA-Digest: pDpEAG9Naecx8aFtqPI4udsx/00= MD5-Digest: 04NcSlhE3Smnzlp2Hhj6qeg==

O ovom e uglavnom ne m orate da vodite rauna, a ako neto menjate, m oete sam o da prom enite prvobitnu manifest datoteku i da p on ovo pozovete alatku jar da biste napravili novu arhivu. U JAR arhivu m oete da dodate i nova zrna tako to ete informacije o njima staviti u m anifest datoteku. D obro je da se svako zrno stavi u sopstveni poddirektorijum , jer kada pravite arhivu m oete da arhivirate ceo direktorijum. Vidi se da i Frog i BangBean imaju sopstvene poddirektorijume. Kada pravilno sm estite zrno u arhivu, m oete da ga unesete u alatku za izradu programa koja podrava zrna. Nain na koji se to radi razlikuje se od alatke do alatke, ali kompanija Sun obezbeuje besplatno okruenje za testiranje Javinih zrna koje se zove Bean Builder. (M oete ga preuzeti s lokacije h ttp ://ia va .iu n .co m /b ea n s.) Da biste sm estili zrno u Bean Builder, kopirajte njegovu JAR datoteku u odgovarajui poddirektorijum .

Veba 36: (4) D odajte datoteku Frog.class u manifest datoteku kao to je prikazano u ovom poglavlju i pokrenite program jar da biste napravili JAR arhivu koja sadri zrna Frog i BangBean. Zatim sa Interneta preuzm ite i instalirajte Bean Builder ili upotrebite svoju alatku za pravljenje programa koja podrava zrna. Dodajte novu arhivu u to okruenje kako biste m ogli da testirate ta dva zrna.

Poglav[je 22: Grafika korisnika okruenja

1137

Sloenija podrka za zrna


Vidite kako je jednostavno napraviti zrno. M eutim , niste ogranieni sam o na o n o to je ovde prikazano. Arhitektura Javinih zrna obezbeuje lak poetak, ali m oe da poslui i u sloenijim situacijama koje prevazilaze okvire ove knjige i bie ukratko izloene. Vie detalja saznaete na lokaciji java.sun.com . Svojstva zrna m ogu da se poboljaju. U prethodnim prim erim a prikazana su sam o p ojedinana svojstva, ali je m ogue predstaviti i vie svojstava u obliku niza. To se zove indeksirano svojstvo. Vi obezbeujete sam o odgovarajue m etode (naravno, potujui pravila za dodeljivanje im ena m etodam a), a objekat klase Introspector prepoznaje indeksirano svojstvo tako da alatka za pravljenje aplikacija m oe da reaguje na odgovarajui nain. Svojstva m ogu da budu povezana (engl. bound), to znai da obavetavaju druge objekte o prom enam a p o m o u dogaaja tipa PropertyChangeEvent. D rugi objekti potom m ogu da odlue da li e se prom eniti na osn ovu prom ene zrna. Svojstva m ogu da budu ograniena (engl. constrained), to znai da drugi objekti m ogu da zabrane prom enu nekog svojstva ako je neprihvatljiva. D rugi objekti se obavetavaju po m ou dogaaja tipa PropertyChangeEvent, a m ogu da generiu izuzetak tipa PropertyVetoException kako bi spreili prom enu i vratili stare vrednosti. M ogue je menjati i nain predstavljanja zrna tokom projektovanja: 1. M oete da obezbedite nam enski spisak svojstava za odreeno zrno. O bian spisak svojstava koristie se za sva ostala zrna, ali e se va spisak autom atski pozivati kada se izabere vae zrno. 2 . M oete da napravite nam ensku klasu za prilagoavanje odredenog svojstva (tako da se koristi obian spisak svojstava, ali kada se menja specijalno svojstvo, autom atski e se pozivati vaa klasa).

3. Za zrno m oete da obezbedite nam ensku klasu tipa Beanlnfo koja daje informacije razliite od onih koje pravi Introspector. 4. M oete ukljuivati i iskljuivati napredan reim u svim objektima klase FeatureD escriptor da bi s^razlikovale osn ovn e i sloenije m ogunosti.

Vie o zrnima
M nogi su pisali o zrnim a Jave; na primer, Flliote Rusty Harold autor je knjige JavaBeans (IDG , 1998).

Alternative za Svving
Iako je biblioteka Swing GKO alatka koju preporuuje Sun, to nikako ne znai da ne postoie i d ru g e alatke za pravljenje gralikih korisnikih okruenja. Dve vane alternative su Flash kom panije A dobe , u kojem se za pravljenje klijentskih GKO preko Weba koristi Flashov sistem za programiranje Flex , i biblioteka otvorenog izvornog koda Eclipse Standard W idget Toolkit (SW T) za aplikacije nam enjene stonim raunarima.

1 138

Misliti na Javi

Zato biste koristili alternative? Za Web klijente se prilino uverljivo m oe tvrditi da apleti nisu uspeli. Iako su postojali od poetka i m ada se o njim a toliko prialo i obeavalo, danas je iznenaenje kada ovek naie na Web aplikaciju koja koristi aplete. ak ni Sun ne koristi aplete svugde. Evo primera: http://java.sun.com /developer/onlineT raining/new 2java/javam ap/intro.htm l Iako je interaktivna mapa Javinih m ogu n osti na Sunovoj lokaciji veom a dobar kandidat za Java aplet, oni su je napravili u Flashu. Izgleda da je to preutno priznanje da apleti nisu uspeli. Jo je vanije to to je Flash Player instaliran na preko 98 procenata raunarskih platformi, pa se m oe sm atrati prihvaenim standardom . Kao to ete videti, sistem Flex obezbeuje veom a m o n o okruenje za programiranje klijentskih aplikacija, svakako m nogo jae od JavaScripta, a njegov izgled i ponaanje esto su bolji od apleta. U koliko ipak elite da koristite aplete, ostaje vam da ubedite klijenta da s Weba preuzme JRE koji je m nogo vei i due se preuzim a od Flash Playera. Za stone aplikacije, jedan od problem a sa Sw ingom je to to korisnici opaze da koriste drugu vrstu aplikacije, poto se izgled i ponaanje Sw ing aplikacija razlikuje od onih uobiajenih na stonim raunarima. Korisnici po pravilu ne ele da svaka aplikacija drugaije izgleda i ponaa se; oni se trude da to bre zavre svoj posao i trude se da sve aplikacije izgledaju i ponaaju se jednako. SWT pravi aplikacije koje izgledaju kao m atine, a poto ta biblioteka koristi m atine kom ponente koliko god je m ogue, te aplikacije se obino izvravaju bre od ekvivalentnih Swing aplikacija.

Pravljenje Flash Web klijenata pomou Flexa


Poto je laka virtuelna maina Flash kom panije A dobe iroko rasprostranjena, veina ljudi m oe da bez ikakve instalacije koristi okruenje zasnovano na Flashu, a on o e izgledati i ponaati se jednako na svim sistem im a i p latform am a.1 0 Za razvoj Flash korisnikih okruenja za Java aplikacije, m oete da upotrebite sistem F/exkom panije Adobe. Flex se sastoji od program skog m odela iju o sn ovu ine XML i jezik za pisanje skriptova, slini program skim m odelim a HTML i JavaScript, i robusne biblioteke kom ponenata. Za deklarisanje kontrolnih objekata i upravljanja rasporedom kom ponenata koristi se sintaksa MXML, a dinam iki skriptovi se upotrebljavaju za dodavanje koda za obradu dogaaja i pozivanje usluga, koji korisniko okruenje povezuje s Java klasama, m odelim a podataka, Web uslugam a itd. Flexov prevodilac prevodi MXML i skript datoteke u bajtkod. Flashova virtuelna m aina na klijentskom raunaru radi kao Javina virtuelna maina (JVM) utoliko to interpretira prevedeni bajtkod. Format Flashovog bajtkoda nazvan je SWF; SWF datoteke proizvodi Flexov prevodilac. Vodite rauna o tom e da postoji alternativa Flexu na adresi http://openlaszlo.org; njen izvorni kod je otvoren i strukture sline Flexu, te nekom e m oe bolje posluiti. Postoje i druge alatke za pravlienje Flash aplikaciia na razliite naine.

1 ,1

Jezgro m aterijala u ovom odeljku napravio je Sean Neville.

Poglavlje 22: Grafika korisnika okruenja

1139

Zdravo, Flex
Proitajte ovaj MXML kod, koji definie korisniko okruenje (imajte u vidu da prvog i poslednjeg reda nema u kodu koji preuzim ate u paketu izvornog koda ove knjige):
giii/flex/zdravoflexl.mxml <?xml version="1.0" encoding="utf-8"?> <mx:Application xml n s :mx="http://www.mac romedi a .com/2003/mxml1 1 1 1 backgroundCol or="#ffffff"> <mx:Natpis id="output" tekst="Zdravo, Flex!" /> </mx:Applicati on>

III-M XML datoteke su XML dokum enti, te stoga poinju op isom XML verzije i kodiranja; to su atributi version i encoding. Krajnji spoljanji MXML elem ent je Application koji se vizuelno nalazi na vrhu i logiki je kontejner za Flex korisniko okruenje. Unutar elem enta Application rnoete eklarisati oznake koje predstavljaju vizuelne kontrole kao to je gornji elem ent Natpis. Kontrole uvek sm etam o u kontejnere, a kontejner pored ostalih m ehanizam a kapsulira i rasporediva (engl. layout tnanager ) koji upravlja rasporedom kontrola u kontejneru. U najjednostavnijem sluaju, kao u gornjem prim eru, Application deluje kao kontejner. Podrazum evani rasporediva elem enta Application smeta kontrole vertikalno niz prozor, redosledom kojim su bile deklarisane. ActionScript je verzija ECMAScripta, ili JavaScripta, koja p otp u n o lii na Javu i p odrava klase i strogu proveru tipova, kao i dinam iko pravljenje skriptova. Kada prim eru dodam o skript, uveem o ponaanje. Ovde MXML kontrola Script sm eta ActionScript neposredno u MXML datoteku:
//:! gui/flex/zdravoflex2.mxml <?xml version="1.0" encoding="utf-8"?> <mx:Application xmlns:mx="http://www.macromedia.com/2003/mxml" backgroundColor="#ffffff"> < m x :Script> < ! [CDATA[ function update0utput() { output.tekst = "Zdravo! " + input.tekst;

} ]]>
</mx:Script> < m x:TextInput id="input" width="200" change="updateOutput()" /> <mx:Natpis id="output" tekst="Zdravo!" /> </mx:Appli cati on>

///: 1 1 N aveena adresa vie nije do stu p n a . Vie inform acija o M X M L-u nai ete na adresi w\vw\adobe.cotn/ licvnc t/flex/n ixrnl_as.htm l

1140

Misliti na Javi

Textlnput kontrola prima korisnikov unos, a Natpis (ve tokom unosa) prikazuje unesene podatke. Vodite rauna o tom e da je atribut id svake kontrole u skriptu dostupan kao im e promenljive, pa skript m oe da referencira instance M XM L oznaka. U polju TextInput vidite da je atribut change povezan s funkcijom updateO utput( ) - dakle, ta funkcija se poziva kad god se desi bilo kakva prom ena (engl. change).

Prevoenje MXML-a
Najbolje je da sa adrese w w w .adobe.com /products/flex/trial preuzm ete besplatnu probnu verziju Flexa.12 Taj proizvod postoji u vie izdanja - od besplatnih probnih do serverskih za velika preduzea a za razvoj Flex aplikacija A dobe nudi jo neke alatke. Poto se izdanja stalno menjaju, proitajte pojedinosti na Web lokaciji proizvoaa Adobe. Takoe, imajte u vidu da ete m oda m orati da m odifikujete datoteku jvm.config u direktorijum u bin Flexove instalacije. Za prevoenje MXML koda u Flashov bajtkod im ate dve opcije: 1 . M XML datoteku m oete sm estiti u neku Java Web aplikaciju, pored JSP i HTML stranica u WAR datoteci, i dati da se u vrem e izvravanja zahtevi za .m x m l datotekom prevode kad god bilo koji ita zatrai URL adresu tog MXML dokum enta. 2 . MXML datoteku m oete prevesti p om o u Flexovog prevodioca m xm lc koji se pokree s kom andne linije. Prva opcija, prevoenje na W ebu u vrem e izvravanja, sem Flexa zahteva i neki kontejner servleta (kao to je A pacheovT om cat). WAR datotekekontejnera servleta moraju biti aurirane p o m o u informacija iz Flexove konfiguracije, kao to su mapiranja servleta dodata deskriptoru w eb.xm l, i m oraju obuhvatati Flexove JAR datoteke - sve se to obavlja autom atski kada instalirate Flex. Nakon konfigurisanja WAR datoteke, MXML datoteke m oete sm estiti u Web aplikaciju i zatraiti URL adresu tog dokum enta bilo kojim itaem . Flex e prevesti aplikaciju nakon prvog takvog zahteva, slino kao u m odelu Javinih serverskih stranica (JSP), i p otom e isporuivati prevedeni i keirani SWF unutar HTM L ljuske. U drugoj opciji server nije potreban. SWF datoteke pravite pozivom Flexovog prevodioca m x m lc na kom andnoj liniji. Njih m oete upotrebiti kako god elite. Izvrna datoteka m xm lc je u direktorijum u b in FIexove instalacije, i kada je pozovete bez argumenata, ispisae spisak validnih opcija u kom andnoj liniji. O bino se lokacija Flexove biblioteke klijentskih kom ponenata zadaje kao vrednost opcije -flexlib, ali u veom a jednostavnim sluajevim a kao to su dva prethodna, Flexov prevodilac e pretpostaviti gde je biblioteka kom ponenata. Zato prva dva prim era m oete prevesti ovako:
mxmlc.exe zdravoflexl.mxml mxmlc.exe zdravoflex2.mxml

Tim e se proizvodi datoteka z d rav oflex 2 .sw f koja se m oe pokrenuti u Flashu ili sm estiti uz HTM L na neki HTTP server. (N akon uitavanja Flasha u ita Weba, esto je dovoljno dvaput pritisnuti SWF datoteku da biste je pokrenuli u itau.)

P a z ite - tre b a d a p re u z m e te Flex, a n e F lex B u ild er, to je a la tk a za p ro je k to v a n je p ro g ra m a .

Poglavjje 22: Grafika korisnika okruenja

1141

Kada pokrenete zdravoflex2.swf, videete u Flash Playeru sledee korisniko okruenje:


jThis vras not too hard to do...| H*allo1 This was not too hsrd to do.. .

U sloenijim aplikacijama m oete razdvojiti MXML i A ctionScript tako to ete funkcije referencirati u spoljnim ActionScript datotekama. U M XM L-u upotrebite sledeu sintaksu za kontrolu Script:
<mx:Script source="MojSpoljniSkript.as" />

Taj k od om oguuje MXML kontrolam a da referenciraju funkcije sm etene u datoteku

MojSpoljniSkript.as kao da su sm etene u sam u tu MXML datoteku.

MXML i ActionScript
MXML je deklarativna stenografija za ActionScript klase. Za svaku MXML oznaku postoji istoim ena ActionScript klasa. Kada Flexov prevodilac leksiki analizira (ralanjuje i razreava) MXML, prvo transform ie XML u ActionScript i uitava referencirane Ac~ tionScript klase, a potom prevodi i povezuje taj ActionScript u SWF datoteku. Celu Flex aplikaciju m oete napisati u sam om A ctionScriptu, a da uopte ne u potrebite MXML. Dakle, MXML nije neophodan, ali je pogodan. MXML se ob in o upotrebljava za deklarisanje kom ponenata korisnikog okruenja kao to su kontejneri i kontrole, dok se ActionScript i Java koriste za obradu dogaaja i ostalu klijentsku logiku. Imate m ogunost da pravite sopstvene MXML kontrole i da ih referencirate u ActionScript klasama. Postojee MXML kontejnere i kontrole m oete da kom binujete u novom MXML dokum entu koji m oe biti referenciran kao oznaka u drugim MXML dokum entim a. Vie informacija o tom e potraite na Web lokaciji kom panije Adobe.

Kontejneri i kontrole
V izuelno jezgro biblioteke Flex kom ponenata je skup kontejnera koji upravljaju rasporedom i niz kontrola koje idu 11 te kontejnere. M eu kontejnerim a su panoi, vertikalni i horizontalni pravougaonici, pravougaonici koji se slau kao ploice, pravougaonici koji se ire poput harm onike, pravougaonici sa unutranjom m reom , tabele itd. Kontrole su spravice (engl. widgets) korisnikog okruenja kao to su dugm ad, tekstualna polja, klizai, kalenari, tabele podataka itd. Sada em o napraviti Flex aplikaciju koja prikazuje i ureuje listu audio datoteka. Vid e e t a p r i m e r e kontejnera, kontrola i otkriete kako a ih iz Flasha poveete s Javom. Na poetak MXML datoteke postaviem o kontrolu DataGrid (koja spada u sofisticiranije Flex kontrole) u kontejner tipa Panel:
//:! gui/f1ex/pesme.mxml <?xml version="1.0" encoding="utf-8"?> <mx:Application

1142

Misliti na Javi

xmlns:mx="http://www.macromedi a .com/2003/mxml" backgroundColor="#B9CAD2" pageTitle="Flex program za rukovanje pesmama" initialize="dajPesme()"> <mx:Script source="skriptPesama.as" /> <mx:Style source="sti1ovi Pesama.css"/> <mx:Panel id="panoListePesama" ti tleStyleDeclarati on="headerText" title="Flex MP3 biblioteka"> < m x :HBox verticalA1i gn="bottom"> <mx:DataGrid id="tabelaPesama" c e n P r e s s = izaberiPesmu(dogadjaj)" rowCount="8"> <mx:columns> <mx:Array> <mx:DataGri dColumn columnName="naslov" headerText="Naslov pesme" width="120" /> < mx :DataGri dColumn columnName="i zvodja" headerText="Izvodja" width="180" /> <mx:DataGridColumn columnName="album" headerText="Album" width="160" /> </mx:Array> </mx:columns> </mx:DataGrid> <mx:VBox> <mx:HBox height="100" > <mx:Slika id="sl ikaAlbuma'' source="" height="80" width="100" mouseOverEffect="promVelPovecaj" mouseOutEffect="promVelSmanji" /> <mx:TextArea id="podaciOPesmi" styleName="boldText" height="100%" width="120" vScrol1Policy="off" borderStyle="none" /> </mx:HBox> <mx:MediaPlayback id="plejerZaPesme" contentPath="" mediaType="MP3" height="70" width="230" control 1erPolicy="on" autoPlay="false" visible="false" /> < / mx:VBox> </mx:HBox> <mx:ControlBar horizontalAlign="right"> <mx:Button id="dugme0svezi Pesme" 1 abel ="0svei pesme" w M t h = "100" toolTip=" Osvei listu pesama" cl ick="uslugaPesama.dajPesmeO" /> </mx:ControlBar>

Poglavjje 22: Grafika korisnika okruenja

1143

</mx:Panel> <mx:Effect> <mx:PromVel name="promVelPovecaj" heightTo="100" duration="500"/> <mx:PromVel name="promVelSmanji" heightTo="80" duration="500"/> </mx:Effect> <mx:RemoteObject id=''uslugaPesama" source="gui.f1ex.UslugaPesama" rezul tat="onPesme(dogadjaj.rezultat)" fault="alert(dogadjaj.fault.faultstring, <mx:method name="dajPesme"/> </mx:RemoteObject> </mx:Application>

'Greka')">

///:Kontejner tipa DataGrid sadri ugneene oznake za svoj niz kolona. Svaki atribut ili ugneeni elem ent na kontroli odgovara nekom svojstvu, dogadaju ili kapsuliranom objektu pripadne ActionScript klase. Taj DataGrid ima atribut id ija je vrednost tabelaPesama, pa ActionScript i MXML oznake m ogu programski da referenciraju tu tabelu koristei tabelaPesama kao im e prom enljive. DataGrid eksponira m n ogo vie svojstava nego to sam ja ovde pokazao; celokupni API za MXML kontrole i kontejnere m oete nai na adresi http://livcdocs.adobe.eom /flex/l/asdocs. Iza kontejnera DataGrid sledi VBox sa elem entom Slika koji pokazuje prednju stranu album a i podatke o pesm i, kao i s kontrolom MediaPlayback za reprodukciju MP3 datoteka. U ovom primeru sadraj se em ituje u realnom vrem enu, da bi se smanjila prevedena SWF datoteka. Ukoiiko slike, audio i video datoteke ugradite u Flex aplikaciju um esto da ih em itujete u realnom vrem enu, te datoteke postaju deo prevedenog SWF-a i isporuuju se zajedno s korisnikim okruenjem; u tom sluaju, u vrem e izvravanja nem a em itovanja u realnom vrem enu na zahtev. Flash plejer sadri ugraene kodeke za reprodukciju i em itovanje audio i video datoteka raznih formata u realnom vrem enu. Flash i FIex podravaju najkorienije form ate slika na Webu, a Flex um e da konvertuje datoteke skalabilne vektorske grafike (SVG) u SWF resurse koji se m ogu ugraditi u Flex klijentc.

Efekti i stilovi
Flash plejer iscrtava grafiku vektorski, pa u vrem e izvravanja m oe da obavi veom a izraajne transformacije. Flexovi efekti om oguuju da steknem o uvid u takve animacije. Efekti su transformacije koje p om ou MXML sintakse m oete prim eniti na kontrole i kontejnere. Oznaka Ettect prikazana u prethodnom MXML kodu proizvodi dva rezultata: prva ugnedena oznaka dinam iki poveava sliku kada se mi nalazi iznad nje, a druga je dinam iki sm anjuje kada mi ode s nje. Ti efekti se prim enjuju na dogadaje mia dostupne za kontrolu Slika nazvanu slikaAlbuma.

1144

Misliti na Javi

Flex im a efekte i za uobiajene anim acije kao to su prelazi, prebrisavanja i m odulirajui alfa kanali. Pored ugraenih efekata, Flex podrava i Flashov API za crtanje zaista inovativnih animacija. Dublje istraivanje ove tem e obuhvata grafiki dizajn i anim aciju, i prevazilazi opseg ovog odeljka. Flex podrava kaskadne opise stilova (Cascading Style Sheets, CSS), p a znai i standardne stilove. Ako MXML datoteci p ridruite CSS datoteku, Fiex kontrole e se pridravati tih stilova. U ovom prim eru, kaskadni opis stilova stiloviPesam a.css sadri sledeu CSS deklaraciju:
//:! gui/flex/stiloviPesama.css .headerText { font-family: Arial, "_sans"; font-size: 16; font-weight: bold;

}
,boldText { font-family: Arial, "_sans"; font-size: 11; font-weight: bold;

III-Aplikacija biblioteke pesama uvozi tu datoteku i upotrebljava je preko oznake Style u MXML datoteci. Nakon uvoza opisa stilova, njegove deklaracije se m ogu prim eniti na Flex kontrole u MXML datoteci. Na prim er, deklaraciju boldText iz opisa stilova upotrebljava kontrola TextArea iji je identifikator (id) podaciOPesm i.

Dogaaji
Korisniko okruenje je maina stanja; ono obavlja radnje kada se m enja stanje. U Flexu se te prom ene prijavljuju pom ou dogaaja. Flexova biblioteka klasa sadri m notvo najrazliitijih kontrola koje podravaju m notvo dogaaja za sve aspekte kretanja mia i korienja tastature. Prim era radi, atribut click elem enta Button predstavlja jedan od dogaaja dostupnih za tu kontrolu. Vrednost dodeljena atrib u tu click m oe biti neka funkcija ili um etnut skript. Prim era radi, u gornjoj MXML datoteci, elem ent ControlBar sadri dugm eOsveziPesme za osveavanje liste pesama. Iz oznake vidite da dogaaj click izaziva poziv m etode uslugaPesam a.dajPesm e( ). U tom prim eru, dogaaj click dugm eta Button referencira RemoteObject (udaljeni objekat) koji odgovara toj Java metodi.

Povezivanje s Javom
Oznaka RemoteObject na kraju MXML datoteke uspostavlja vezu sa spoljnom Java klasom, gui.flex.UslugaPesama. Pom enuti Flex klijent - m etodom dajPesm e( ) te Java klase pribavlja podatke za DataGrid. Da bi to m ogao da radi, m ora izgledati kao usluga -k ra jn ja

Poglavlje 22: Grafika korisnika okruenja

1145

taka s kojom klijent moe da razmenjuje poruke. Usluga definisana u oznaci RemoteObject im a atribut source koji naznauje Java klasu oznake RemoteObject, i specificira ActionScript povratnu funkciju, on P esm e( ), koja e biti pozvana kada se Java m etoda vrati. U gneena oznaka m ethod deklarie m etodu dajPesm e( ), to tu Java m etodu ini dostupn om ostatku Flex aplikacije. U Flexu se svi pozivi usluga vraaju asinhrono, preko dogaaja koji p rouzrokuju (izazivaju) te povratne funkcije. U sluaju greke, isti RemoteObject prikazuje dijalog upozorenja. M etodu dajPesm e( ) sada m oem o pozvati iz Flasha pom ou ActionScripta:
uslugaPesama.dajPesme();

Zbog konfiguracije MXML-a tim e e biti pozvana m etoda dajPesm e( ) u klasi UslugaPesama:
//: gui/flex/UslugaPesama.java package gui,flex; import java.uti1.*; public class UslugaPesama { private List<Pesma> pesme = new ArrayList<Pesma>(); public UslugaPesama() { popuniProbnePodatke(); } public List<Pesma> dajPesme() { return pesme; } public void dodajPesmu(Pesma pesma) { pesme.add(pesma); } public void ukloniPesmu(Pesma pesma) { pesme.remove(pesma); } private void popuniProbnePodatke() { dodajPesmu(new Pesma("Chocolate", "Snow Patrol", "Final Straw", "sp-final-straw.jpg", "chocolate.mp3")); dodajPesmu(new Pesma("Koncert br. 2 u E-duru", "Hilary Hahn", "Bach: violinski koncerti", "hahn.jpg", "bachvioli na2.mp3")); dodajPesmu(new Pesma("'Round Midnight", "Wes Montgomery", "The Artistry of Wes Montgomery", "wesmontgomery.jpg", "roundmidnight.mp3"));

} } ///= Svaki objekat tipa Pesma sam o je kontejner podataka:


//: gui/flex/Pesma.java package gui.flex; public class Pesma implements java.io.Serializable { private String naslov; private String izvodjac; private String album; private String urlS1ikeNaAlbumu;

1146

Misliti na Javi

private String urlNosacaZvuka; public P e smaO {} public Pesma(String naslov, String izvodjac, String album, String urlS1ikeNaAlbumu, String urlNosacaZvuka) { this.naslov = naslov; this.izvodjac = izvodjac; this.album = album; this.urlSlikeNaAlbumu = urlSlikeNaAlbumu; this.urlNosacaZvuka = urlNosacaZvuka;

}
public void setAlbum(String album) { this.album = album;} public String getAlbum() { return album; } public void setllrlSl ikeNaAlbumu(String urlSlikeNaAlbumu) { this.urlSlikeNaAlbumu = urlSlikeNaAlbumu;

}
public String getUrlSlikeNaAlbumu() { return urlS1ikeNaAlbumu;} public void setIzvodjac(String izvodjac) { this.izvodjac = izvodjac;

}
public String getlzvodjacO { return izvodjac; } public void setNaslov(String naslov) { this.naslov = naslov; } public String getNaslov() { return riaslov; } public void setUrlNosacaZvuka(String urlNosacaZvuka) { this.urlNosacaZvuka = urlNosacaZvuka;
\
i

public String getUrlNosacaZvuka() { return urlNosacaZvuka; }

} ///:Prilikom inicijalizacije aplikacije ili kada pritisnete dugm eOsveziPesm e, poziva se m etoda dajPesm e( ), a nakon povratka, poziva se A ctionScript onPesme(dogadjaj.rezultat) za popunjavanje tabele tabelaPesama. Sledi listing A ctionScripta koji sadri kontrola Script MXML datoteke:
//: gui/flex/skriptPesama.as function dajPesme() { uslugaPesama.dajPesmeO;

}
function izaberiPesmu(dogadjaj) { var pesma = tabelaPesama.getItemAt(dogadjaj.itemIndex); pri kazi podatkeOPesmi(pesma);

}
function prikazipodatkeOPesmi(pesma) {
po da c i O P e s m i .tekst = pesma.naslov + nevvline;

podaciOPesmi.tekst += pesma.izvodjac + newline; podaciOPesmi.tekst += pesma.album + newline; slikaAlbuma.source = pesma.urlS1ikeNaAlbumu;

Poglavlje 22: Grafika korisnika okruenja

1147

plejerZaPesme.contentPath = pesma.urlNosacaZvuka; plejerZaPesme.visible = true;

}
function onPesme(pesme) { tabelaPesama.dataProvider = pesme;

) ///:Da bism o obradili izbor D ataG rid elija, atrib u t dogaaja cellPress dodajem o deklaraciji elem enta D ataG rid u MXML datoteci:
cel1Press="i zaberi Pesmu(dogadj aj)"

Kada korisnik pritisne pesm u u tabeli DataGrid, pozvae m etodu izaberiPesm u( ) u gornjem ActionScriptu.

Modeli podataka i povezivanje podataka


Kontrole mogu neposredno da pozivaju usluge, a povratni pozivi ActionScript dogaaja daju priliku da program ski aurirate vizuelne kontrole kada usluge vrate podatke. Iako je skript za auriranje kontrola jednostavan, um e da postane obim an i zam oran, a njegove funkcije su toliko uobiajene da FIex autom atski upravlja tim ponaanjem tako to povezuje podatke. U svom najjednostavnijem obliku, povezivanje podataka om oguuje kontroli da podatke referencira neposredno um esto da nekim ablonskim kodom kopira podatke u kontrolu. Prilikom auriranja podataka, autom atski se aurira i kontrola koja ih referencira, bez ikakve intervencije program era. Infrastruktura Flexa tano odgovara na dogaaje prom ene podataka i aurira sve kontrole koje su povezane s tim podacim a. Ovo je jednostavan prim er sintakse povezivanja podataka:
<mx:Slider id="mojKlizac"/>

<mx:Text tekst="{moj K1 i z a c . v a l ue}"/>

Podatke povezujete kada smestite reference u vitiaste zagrade. Sve u n u tar tih vitiastih zagrada Flex sm atra izrazom koji treba izraunati. Vrednost prve kontrole, elem enta Slider (klizaa), prikazuje druga kontrola - jedno tekstualno polje. S prom enom vrednosti klizaa, svojstvo tekst tekstualnog polja automatski se aurira. Na taj nain, program er ne m ora da obrauje dogaaje prom ena vrednosti elem enta Slider da bi aurirao tekstualno polje. Ima i sofisticiranijih kontrola, kao to su Tree (stablo) i DataGrid u aplikaciji biblioteke pesama. Te kontrole imaju svojstvo dataprovider koje olakava povezivanje s kolekcijama podataka. A ctionScript funkcija on P esm e( ) prikazuje kako je m etoda UslugaPesam a.dajPesm ef ) povezana sa svojstvom dataprovider Flexovog elementa DataG rid. l\ao to je deklarisano u oznaci RemoteObject MXML datoteke, tu povratnu funkciju poziva A ctionScript kada se Java m etoda vrati. Sofisticiranija prim ena sloenijeg m odela podataka, kao to je in tran et aplikacija koja upotrebljava objekte za prenos podataka (Data Transfer Objects) ili dostavljae poruka

1148

Misliti na Javi

iji su podaci usklaeni sa sloenim em am a, m oe p o d sta dalje razdvajanje izvora podataka od kontrola. U razvoju Flex program a, to razdvajanje postiem o deklarisanjem objekta ,,modela, to je generiki MXML kontejner podataka. Taj model ne sadri logiku. O n je ekvivalentan objektu za prenos podataka kakav se sree p ri razvoju intranet program a i odgovarajuim strukturam a u ostalim program skim jezicima. Po tom m odelu, povezujemo podatke kontrola s m odelom i istovrem eno m odel povezuje svoja svojstva sa ulazim a i izlazima usluga. Tim e se izvori podataka, usluge, razdvajaju od vizuelnih potroaa podataka, im e se olakava korienje obrasca model-prikaz-kontroler ( ModelView-Controller, MVC). U veim, sofisticiranijim aplikacijama, poetno poveanje sloenosti zbog um etanja m odela esto se isplati je r se dobija MVC aplikacija s jasno razdvojenim podacim a od kontrola. Sem Java objekata, Flex pom ou kontrola WebService odnosno HttpService um e da pristupa i Web uslugam a iju osnovu ini SOAP - tj. RESTful H TTP uslugama. Pristupanje svim uslugam a podlee bezbednosnim ogranienjim a provere identiteta.

Izgradnja i primena
U prethodnim prim erim a mogli sm o da se provuem o bez indikatora -flexlib na kom andnoj liniji, ali da bism o preveli ovaj program , indikatorom -flexlib m oram o specificirati mesto datoteke flex-config.xml. Sledea kom anda je prikladna za m oju instalaciju, a vi ete m orati da je m odifikujete u skladu sa sopstvenom konfiguracijom. (Kom anda se pie u jednom redu koji se zbog duine nastavlja i u narednom ):
//:! gui/flex/bui1d-command.txt mxmlc -flexlib C:/"Program Files"/Adobe/Flex/jrun4/servers/default/flex/ WEB-INF/flex pesme.mxml

///-

Ta e kom anda izgraditi aplikaciju u obliku SWF datoteke koju moete prikazati u itau Weba, ali poto u datoteci za istribuciju koda ove knjige nem a ni MP3 datoteka niti JPG slika, kada pokrenete aplikaciju neete uti nijednu pesm u niti ete videti om ote album a, nego sam o osnovnu stru k tu ru (engl. fram eivork). Pored toga, m orate konfigurisati neki server da biste iz Flex aplikacije mogli da kom unicirate s Java datotekam a. Flexov probni paket obuhvata i server JRun, a njega nakon instalacije Flexa m oete pokrenuti bilo koristei menije operativnog sistema, bilo s kom andne linije:
jrun -start default

Proverite da li je server uspeno p o k ren ut tako to ete u itau Weba otvoriti http://localhost:8700/samples i p r ik n z a ti r n z n e u z o r k e (engl. samplcs). To ie i d o b n r nnin za u p o znavanje m ogunosti Flexa. Umesto da aplikaciju prevodite na kom andnoj liniji, moete da je prevedete posredstvom servera. To se radi tako to izvorne datoteke pesama, CSS opise stilova itd. smestite u direktorijum jrun4/servers/default/flex i pristupite im u itau Weba otvaranjem adrese http://localhost:8700/flex/pesm e.m xm l.

Poglavlje 22: Grafika korisnika okruenja

1 149

M orate konfigurisati i Javinu i Flexovu stranu da biste mogli pokrenuti aplikaciju. Java: prevedene datoteke Pesma.java i UslugaPesama.java smestite u direktorijum WEB-INF/cIasses. Prem a specifikaciji J2EE, to je m esto za WAR klase. Drugi nain bi bio da datoteke pretvorite u JAR arhive i sm estite ih u direktorijum WEB-INF/lib. O ne se m oraju nalaziti u direktorijum u ija se stru ktu ra po dudara sa odgovarajuim Java paketom . Ako koristite server JRun, datoteke treba sm estiti u direktorijum e jrun4/servers/defauIt/flex/W EB-INF/cIasses/gui/flex/Pesma.class i jrun4/servers/default/flex/W EBINF/classes/gui/flex/UslugaPesam a.class. Potrebne su vam i datoteke slika i MP3 audio datoteke koje e biti dostupne u Web aplikaciji. (Za JRun, korenski direktorijum Web aplikacije je jrun4/servers/default/flex.) Flex: iz bezbednosnih razloga, Flex ne moe da p ristupi Java objektim a ukoliko m u to ne dozvolite tako to ete m odifikovati datoteku flex-config.xml. Za server JRun, ona treba da se nalazi u direktorijum u jrun4/servers/default/flex/W EB-INF/flex/flex-config.xml. P ronaite stavku <rem ote-objects> u toj datoteci i njen odeljak <whitelist>; videete sledeu napom enu: <! For security, the w hitelist is locke dow n by default. U ncom m ent the source elem en t below to enable access to all classes during developm ent. We strongly rec o m m en d n o t allovving access to all source files in p ro d u ctio n , since this exposes Java and Flex system classes. <source>*</source> --> (<! Iz bezbednosnih razloga, lista dostupnih klasa je podrazum evano zakljuana. Ukoliko elite da tokom razvoja program a bude ozvoljen p ristup svim klasama, uklonite znakove koji od donjeg elem enta prave komentar. Izriito preporuujem o da u kodu koji se isporuuje ne dozvolite pristupanje svim izvornim datotekam a, poto biste tim e eksponirali klase Jave i sistema Flex. < source>*</source> -->) Da biste dozvolili pristupanje svim izvornim datotekam a, uklonite znakove koji od stavke < so urce> prave kom entar, tako da glasi < so urce> *< /so u rce > . Znaenje ove i d ru gih stavki opisano je u dokum entim a za konfigurisanje Flexa. Veba 37: (3) Izgradite goreprikazan jednostavan prim er sintakse za povezivanje podataka. Veba 38: (4) U datoteci koda ove knjige koja se moe preuzeti sa Weba nem a ni MP3 niti JPG datoteka navedenih u program u U slugaPesam a.java. Naite neku sliku i pesm u (M P3 odnosno IPG datoteku), izmenite program U slugaPesam a.java tako da sadri i im ena tih datoteka, preuziuite s VVeba pro bn u verziju Flexa i lzgradite gorenavedenu aplikaciju.

1150

Misliti na Javi

Izrada S\X/T aplikacija


Kao to sam ve rekao, Swing sve UI kom ponente pravi piksel po piksel pa zato m ogu postojati sve eljene kom ponente, bez obzira na to da li ih lokalni operativni sistem im a ili nem a. SWT koristi m atine kom ponente ako ih OS ima, a pravi samo ono to OS nema. D obija se aplikacija koja korisniku lii na m atinu, a esto je i znatno bra od ekvivalentnog Swing program a. Pored toga, SWT-ov program ski m odel je jednostavniji od Swingovog, to je poeljno u veini prim en a.1 3 Poto SWT upoljava m atini OS koliko god je to mogue, on autom atski moe da iskoristi m ogunosti operativnog sistema koje su Swingu nedostupne - recimo, Windows podrava iscrtavanje taaka m anjih od piksela, kojim a se na LCD ekranim a jasnije prikazuju fontovi. SWT um e da pravi ak i aplete. Ovaj odeljak nije sveobuhvatan uvod u SWT, nego sam o om oguuje da ga probate i vidite koliko se razlikuje od Swinga. Videete da SVVT im a m nogo elem enata (videta) i da se oni prilino lako koriste. Pojedinosti m oete da prouavate u sveobuhvatnoj dokumentaciji i m nogim prim erim a dostupnim na adresi www.eclipse.org. O program iranju u SW T-u ve postoji vie knjiga, a piu se i nove.

Instaliranje SWT-a
Za SWT aplikacije potrebno je da preuzm ete i instalirate SWT biblioteku projekta Eclipse. Izaberite neki od preslikanih servera na lokaciji www.eclipse.org/dow nloads/. Sledite veze do tekueg builda Eclipsea i pronaite kom prim ovanu datoteku ije ime poinje sa swt i sadri ime vae platform e (na prim er, win32). U toj datoteci nai ete swt.jar. Datoteku swt.jar najlake ete instalirati ukoliko je smestite u direktorijum jre/lib/ext (jer u tom sluaju neete m orati da m enjate svoju p u tan ju klasa). Kada dekom prim ujete biblioteku SWT, m oda ete nai jo datoteka koje treba instalirati na m estim a odgovarajuim za vau platform u. Na prim er, W in32 distribucija obuhvata i DLL datoteke koje treba smestiti negde u java.Iibrary.path. (O bino je ta putanja jednaka onoj koju opisuje sistemska prom enljiva PATH, ali stvarnu vrednost java.Iibrary.path moete saznati pom ou program om object/ShowProperties.java). Kada to uradite, trebalo bi da moete transparentno da prevodite i pokreete SWT aplikacije, kao sve druge Java programe. D okum entacija za SWT preuzim a se zasebno. D ruga m ogunost je da instalirate program za ureivanje teksta Eclipse; on obuhvata i SWT i SWT dokum entaciju koju m oete prikazati pom ou Eclipseovog sistema pomoi.

Zdravo, SW T
Ponim o najjednostavnijom m oguom aplikacijom u stilu zdravo sviina":
//: swt/ZdravoSWT.java // {Requires: o r g . e c l i ps e. sw t. wi dge ts.D i s p l a y ; Morate // instalirati SWT biblioteku s lokacije http://www.eclipse.org }

Chris G rind staff m i je m n o g o p o m o g a o p ri p re v o e n ju SWT p rin ie r a i iz b o ru in fo rm a c ija o SVVT-n.

Poglavlje 22: Grafika korisnika okruenja

1151

import org.eclipse.swt.widgets.*; public class ZdravoSWT { public static void main(String [] args) { Disp1ay prikaz = new Display(); Shell ljuska = new S h e l 1 (prikaz); 1juska.setText("Zdravo, S W T !"); // Na naslovnoj traci 1j u s k a . o p e n ( ) ; while(!l j u s k a . i s D i s p o s e d O ) if(!prikaz.readAndDispatch()) pri k a z . s l e e p O ; prikaz.disposeO;

} ///:-

Ukoliko preuzm ete izvorni ko ove knjige, videete da kom entarska direktiva ,,Requires zavrava u A nt datoteci build.xm l kao preduslov p o trebno je pravljenje poddirektorijum a swt; za sve datoteke koje uvoze org.eclipse.swt zahtevaju da instalirate biblioteku SWT s lokacije www.eclipse.org. Klasa Display upravlja vezom izm edu SWT-a i operativnog sistema - ona je deo M osta izm eu operativnogsistem a i SWT-a. Klasa Shell je glavni prozor najvieg nivoa; u njoj se grade sve ostale kom ponente. Kada pozovete m etodu setT ext( ), njen argum ent biva pretvoren u natpis na naslovnoj traci prozora. M etodom o p e n ( ) klase Shell prikazujete taj prozor, a tim e i aplikaciju. Dok Svving od program era skriva petlju za obradu dogaaja, SWT ga prim orava da je eksplicitno napie. Na vrhu te petlje proverite da li je ljuska ugaena - to vam prua priliku da um etnete kod za ienje. Ali to znai da je glavna nit m a in ( ) istovrem eno i n it korisnikog okruenja. U Swingu se iza scene pravi druga nit za obradu dogaaja, ali u SW T-u dogaaje korisnikog okruenja obrauje glavna nit, tj. nit m etode m a in ( ). Poto se podrazum eva postojanje sam o jedne niti a ne dve, neto je m anje verovatno da ete korisniko okruenje zatrpati nitim a. Im ajte u vidu da zadatke ne m orate da aljete niti korisnikog okruenja kao u Swingu. Ne sam o da se o tom e stara SWT, nego on baca izuzetak ako videt pokuate da obradite u pogrenoj niti. M edutim , ukoliko za dugotrajne operacije treba da napravite nove niti, dogaaje treba da aljete isto kao u Swingu. Za to, SWT im a tri m etode koje se m ogu pozvati za objekat tipa Display: asyncExec(Runnable), syncExec(Runnable) i timerExec(int, Runnable). U tom trenutku vaa glavna nit (m a in ( )) treba da pozove m etodu readAndDispatch( ) za objekat Display (to znai da aplikacija moe imati sam o jedan objekat tipa Display). M etoda readA ndD ispatch( ) vraa true ukoliko u redu za ekanje ima vie dogaaja koji ekaju na obradu. 11 tom sluaju, treba je odm ah ponovo pozvati. M eutim , ako nema poslova koji ekaju na izvravanje, pozovite m etodu sle e p ( ) objekta Display da biste neko kratko vrem e saekali pre nego to ponovo pozovete red za ekanje.

1152

Misliti na Javi

Kada se izvravanje program a zavri, m orate eksplicitno pozvati m eto d u d is p o s e () objekta Display. SWT esto zahteva eksplicitno ienje resursa, poto su to najee resursi pripadnog operativnog sistema, kojih bi inae m oglo ponestati. Sledei program pravi vie Shell objekata da bi pokazao da je ljuska (objekat tipa Shell) glavni prozor:
//: swt/ljuskeSuGlavniProzori.java import org.eclipse.swt.widgets.*; public class 1juskeSuGlavniProzori { static Shel 1 [] ljuske = new Shel 1 [10]; public static void main(String [] args) { Display prikaz = new Display(); for(int i = 0; i < 1juske.length; i++) { ljuske[i] = new Shell(prikaz); 1juske[i].setText("ljuska # + i); 1juske[i] .open();

}
whi 1e (!1juskeOciscene()) if(!prikaz.readAndDispatch()) prikaz . sle ep O; prikaz.disposeO;

}
static boolean 1juskeOciscene() { for(int i = 0; i < 1juske.length; i++) if (1juske[i] .isDisposedO) return true; return false;

} } ///:Kada pokrenete ovaj program , dobiete deset glavnih prozora. Zbog naina na koji je program napisan, ako zatvorite bilo koji od tih prozora, zatvoriete i sve ostale. I SWT koristi rasporeivae - razliite od onih u Svvingu, ali iste u principu. U narednom neznatno sloenijem prim eru dodaem o ljusci tekst rezultata m etode S y stem .g e tP ro p ertie s():
//: swt/SvojstvaKlaseDisplay.java import org.eclipse.swt.*; import org.eclipse.swt.widgets.*; import org.eclipse.swt.layout.*; import java.io.*;
public class SvojstvaKiaseDispiay {

public static void main(String [] args) { Display prikaz = new Display(); Shell Ijuska = new Shel1 (prikaz); 1juska.setText("Svojstva klase Display");

Poglavjje 22: Grafika korisnika okruenja

1153

1juska.setLayout(new FillLayout()); Text tekst = new Text(ljuska, SWT.WRAP | SWT.V_SCROLL); StringWriter svojstva = new StringWriter(); System.getProperti e s ().1i st(new Pri ntWri ter(svojstva)); tekst.setText(svojstva. t o S t r i n g O ) ; 1juska.open(); wh i1e (!1juska.isDisposed()) i f (!prikaz.readAndDi spatch()) p r i k az. s lee pO ; prikaz.disposeO;

} III-U SW T-u svi videti m oraju im ati roditeljski objekat opteg tipa C o m p o site koji konstruktoru videta m orate proslediti kao prvi argum ent. To vidite u k o n struktoru klase Text kojem je prvi argum ent ljuska. Gotovo svi konstruktori prim aju i indikatorski argum ent pom ou kojega moete zadati proizvoljan broj direktiva u vezi sa stilom , u zavisnosti od toga ta videt zahteva. Sve direktive u vezi sa stilom podvrgavaju se logikoj disjunkciji po bitovim a (operator I), kao u preth od no m prim eru. Prilikom pravljenja T e x t() objekta dodao sam indikatore stila koji prelam aju tekst (engl. w rap) i autom atski m u dodaju vertikalnu traku za pom eranje (engl. scroll bar), ako treba. Videete da se u SWT-u m nogo toga radi pom ou konstruktora; m noge atribute videta teko je iii nem ogue menjati sem pom ou konstruktora. U dokum entaciji videtovog konstruktora uvek m orate proveriti koji su dozvoljeni indikatori. Neki konstruktori zahtevaju indikatorski argum ent ak i kada u dokum entaciji nem aju nijedan dozvoljen" indikator. Time se om oguuje budue proirenje, a da se interfejs ne menja.

Izbegavanje redundantnosti
Pre nego to nastavimo, naveemo ta m orate da uradite u svakoj SWT aplikaciji. U SWT-u uvek m orate da napravite prikaz tj. objekat klase Display, od tog prikaza napravite ljusku tj. objekat kiase Shell, napiete petlju rea d A n d D isp a tc h () itd. Naravno, u nekim posebnim sluajevima to ne m orate da radite, ali o n i su toliko retki da se isplati sabrati sav taj kod u jedan program i izbei ponavljanje, kao to sm o za Swing uradili u program u net.mindvievv.util.SvvingKonzoIa. N ateraem o svaku aplikaciju da se usaglasi sa interfejsom :
//: swt/util/SWTAplikacija.java package swt.uti1 ; import org.eclipse.swt.widgets.*; public interface SUTAplikacija | void createContents(Composite roditelj);

} III--

1154

Misliti na Javi

Aplikaciji se predaje objekat tipa C om posite (ija je Shell potklasa), pom ou kojeg aplikaja m etodom createC ontents( ) m ora da napravi sav svoj sadraj. U odgovarajuem trenutku, SW TKonzola.run( ) poziva m etodu createC ontents( ), zadaje veliinu Ijuske u skladu sa argum entom koji je korisnik prosledio m etodi r u n ( ), otvara tu Ijusku, pokree petlju dogaaja i na kraju uklanja Ijusku kada program zavri s radom :
//: swt/util/SWTKonzola.java package swt.uti1; import org.eclipse.swt.widgets.*; public class SWTKonzola { public static void run(SWTAplikacija SWTApl, int sirina, int visina) { Display prikaz = new Display(); Shell ljuska = new Shell(prikaz); 1juska.setText(SWTApl ,getClass() .getSimpleNameO); SWTApl.createContents(ljuska); 1juska.setSize(sirina, visina); 1juska.open(); while(!ljuska.isDisposed()) { if (!pri kaz.readAndDi spatch()) pr ika z . s l e e p O ;

}
prikaz.dispose();

} } / // - Pored ve navedenog, tim e se na naslovnoj traci ispisuje im e klase kojoj pripada SWTAplikacija i zadaje vrednost param etara sirina i visina za ljusku tipa Shell. N apisaem o varijaciju program a SvojstvaKlaseDisplay.java koji pom ou objekta tipa SWTKonzoIa prikazuje sistemsko okruenje:
//: swt/PrikaziSistemskoOkruzenje.java import swt.uti1 .*; import org.eclipse.swt.*; import org.eclipse.swt.widgets.*; import org.eclipse.swt.layout.*; import java.util.*; public class PrikaziSistemskoOkruzenje implements SWTAplikacija { public void createContents(Composite roditelj) { roditelj,setLayout(new Fil1Layout()); Text tekst = new Text(roditelj, SWT.WRAP | SWT.V_SCR0LL); for(Map.Entry stavka: System.getenv().ent.rySet()) { tekst.append(stavka.getKey() + 1 1 : " + stavka.getValueO + "\n");

Poglavlje 22: Grafika korisnika okruenja

1155

public static void main(String [] args) { SWTKonzola.run(new PrikaziSistemskoOkruzenje(), 800, 600);

} lll-~
SWTKonzola e nam om oguiti da se usredsredim o na zanimijive aspekte aplikacije, um esto na kod koji se ponavlja. Veba 39: (4) Izmenite program SvojstvaKlaseDisplay.java tako da upotrebljava klasu SWTKonzola. Veba 40: (4) Izmenite program PrikaziSistemskoOkruzenje.java tako da ne upotrebljava klasu SWTKonzoIa.

Meniji
Prikazaem o osnovne menije tako to e sledei prim er uitati sopstveni izvorni kod i razdeliti ga na rei, a zatim njim a pop un iti menije:
//: swt/Meniji.java // Zabava s menijima. import swt.util.*; import org.eclipse.swt.*; import org.eclipse.swt.widgets.*; import java.util.*; import net.mindview.util.*; public class Meniji irnplements SWTApl i kaci ja { private static Shell ljuska; public void createContents(Composite roditelj) { Ijuska = roditelj.getShell (); Menu linija = new Menu(ljuska, SWT.BAR); 1juska.setMenuBar(linija); Set<String> recci = new TreeSet<String>( new TextFile("Meni ji .java", 1 1 \\W+")); Iterator<String> it = recci.iterator(); while(it.next(),matches("[0-9]+")) ; // Prei iza brojeva. Menultemf] stavkaMenija = new Menultem; for(int i = 0; i < stavkaMenija.length; i++) { stavkaMenija[i] = new Menultem(linija, SWT.CASCADE); stavkaMenija[i].setText(it.next()); Menu podmeni = new Menu(ljuska, SWT.DR0P_D0WN); stavkaMenija[i].setMenu(podmeni);

}
int l = 0 ;

whi1e(it.hasNext()) { addltem(linija, it, stavkaMenija[i]); i = (i + 1) % stavkaMenija.length;

1156

Misliti na Javi

} }
static Listener prijemnik = new Listener() { public void handleEvent(Event e) { System.out.println(e.toString());

void addItem(Menu linija, Iterator<String> it, Menultem stavkaMenija) { Menultem stavka = new MenuItem(stavkaMenija.getMenu(),SWT.PUSH); stavka.addListener(SWT.Selection, prijemnik); stavka.setText(it.next());

}
public static void main(String[] args) { SWTKonzola.run(new Meniji(), 600, 200);

} } ///= Meni (objekat tipa Menu) m o ra biti sm eten u neku ljusku tj. objekat tipa Shell, a natklasa C om posite om oguuje da tu Ijusku pribavite m etodom getS h ell( ). TextFile je deo paketa net.m indview .util i opisana je u p reth od no m delu knjige; ovde se reima popunjava skup tipa TreeSet, pa e njihov poredak biti ureen. Poetni elementi su brojevi, koji se potom odbacuju. Pom ou toka rei daju se im ena m enijim a najvieg nivoa u traci menija, zatim se prave podm eniji i popunjavaju reima dok ih ne ponestane. Kao odgovor na izbor stavke menija, prijem nik (objekat tipa Listener) samo ispisuje taj dogaaj tako da m oete videti vrstu inform acija koje sadri. Kada pokrenete program , videete da te inform acije obuhvataju i natpis na m eniju, pa na osnovu njega moete napraviti odgovor m e n ija -ili ete za svaki m eni napraviti zaseban prijem nik (to jebezbednije u pogledu internacionalizacije).

Okna s karticama, dugmad i dogaaji


SWT im a bogat skup kontrola koje se u njem u nazivaju spravice ili videti (engl. widgcts). O snovni videti su navedeni u dokum entaciji za org.eclipse.swt.widgets, a oni sloeniji u dokum entaciji od org.eclipse.sw t.custom . Prikazaemo nekoliko osnovnih videta tako to em o vie kom ponenata smestiti u okna s karticam a. Videete kako se prave objekti tipa C om posite (slino kao Swingovi JPaneli) da bi se stavke smestile u n u ta r drugih stavki.
//; swt/0knoSKarticama.java // Smetanje SWT komponenata u okna s karticama. import swt.uti1 .*; import org.eclipse.swt.*; import org.eclipse.swt.widgets.*; import org.eclipse.swt.events.*; import org.eclipse.swt.graphics.*; import org.eclipse.swt.layout.*; import org.eclipse.swt.browser.*;

Poglavjje 22: Grafika korisnika okruenja

1157

public class OknoSKarticama implements SWTAplikacija { private static TabFolder direktorijum; private static Shell ljuska; public void createContents(Composite roditelj) { Ijuska = roditelj.getShell(); rodi telj.setLayout(new Fi11Layout()); direktorijum = new TabFolder(ljuska, SWT.BORDER); labelTab(); directoryDialogTab(); buttonTab(); sl iderTab(); scribbleTab(); browserTab();

}
public static void labelTab() { Tabltem kartica = new Tabltem(direktorijum, SWT.CL0SE); kartica.setText("Natpis"); // Tekst na kartici kartica.setToolTipText("Kratak natpis"); Label natpis = new Label(direktorijum, SWT.CENTER); natpis.setText("Tekst natpisa"); kartica.setControl(natpis);

}
public static void directoryDialogTab() { Tabltem kartica = new Tabltem(direktorijum, SWT.CL0SE); kartica.setText("Dijalog unutar direktorijuma"); kartica.setToolTipText("Izaberite direktorijum"); final Button dugme = new Button(direktorijum, SWT.PUSH); dugme.setText("Izaberite direktorijum"); dugme.addListener(SWT.MouseDown, new Listener() { public void handleEvent(Event e) { DirectoryDialog dd = new DirectoryDialog(ljuska); String putanja = dd.open(); if(putanja != null) dugme.setText(putanja);

} });
karti ca.setControl(dugme);

}
public static void buttonTab() { Tabltem kartica = new Tabltem(direktorijum, SWT.CL0SE); kartica.setText("Dugmad"); kartica.setToolTipText("Razne vrste dugmadi"); Composite kompozita = new Composite(direktorijum, SWT.N0NE); kompozit a .setLayout(new GridLayout(4, true)); f or (i nt di r : new int[] { SWT.UP, SWT.RIGHT, SWT.LEFT, SWT.D0WN

}) {
Button dugme = new Button(kompozita, SWT.ARR0W | dir); dugme.addListener(SWT.MouseDown, prijemnik);

1158

Misliti na Javi

newButton(kompozita, SWT.CHECK, "Polje za potvrdu"); newButton(kompozita, SWT.PUSH, "Obino dugme"); newButton(kompozita, SWT.RADI0, "Radio-dugme"); newButton(kompozita, SWT.TOGGLE, "Preklopno dugme"); newButton(kompozita, SWT.FLAT, "Ravno dugme"); kartica.setControl(kompozita);

}
private static Listener prijemnik = new Listener() ( public void handleEvent(Event e) { MessageBox poruka = new MessageBox(ljuska, SWT.0K); poruka.setMessage(e.toStri ng()); poruka.open();

} };
private static void newButton(Composite kompozita, int tip, String natpis) { Button dugme = new Button(kompozita, tip); dugme.setText(natpis); dugme.addListener(SWT.MouseDown, prijemnik);

}
public static void sliderTab() { Tabltem kartica = new Tabltem(direktorijum, SWT.CL0SE); kartica.setText("Klizai i trake napredovanja"); kartica.setToolTipText("Kliza povezan s trakom napredovanja"); Composite kompozita = new Composite(direktorijum, SWT.N0NE); kompozita.setLayout(new GridLayout(2, true)); final Slider klizac = new Slider(kompozita, SWT.H0RIZ0NTAL); final ProgressBar napredovanje = new ProgressBar(kompozita, SWT.HORIZONTAL); klizac.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent dogadjaj) { napredovanje.setSelection(kl izac.getSelectionO);

} });
kartica.setControl(kompozita);

}
public static void scribbleTab() { Tabltem kartica = new Tabltem(direktorijum, SWT.CL0SE); kartica.setText("Za vrljanje"); kartica.setToolTipText("Jednostavna grafika: crtanje"); final Canvas platno = new Canvas(direktorijum, SWT.N0NE); PrijemnikMisaZaZvrljanje pmz= new PrijemnikMisaZaZvrljanje(); pl atno.addMouseLi stener(pmz); platno.addMouseMoveLi stener(pmz); kartica.setControl(platno);

}
private static class PrijemnikMisaZaZvrljanje

Poglavlje 22. Grafika korisnika okruenja

1159

extends MouseAdapter implements MouseMoveListener { private Point tacka = new Point(0, 0); public void mouseMove(MouseEvent e) { if((e.stateMask & SWT.BUTT0N1) == 0) return; GC grafKontekst = new GC((Canvas)e.widget); grafKontekst.drawLine(tacka.x, tacka.y, e.x, e.y); grafKontekst.dispose(); updatePoint(e);

}
public void mouseDown(MouseEvent e) { azurirajTacku(e); } private void azurirajTacku(MouseEvent e) { tacka.x = e.x; tacka.y = e.y;

} }
public static void browserTab() { Tabltem kartica = new TabItem(direktorijum, SWT.CL0SE); kartica.setText("ita"); kartica.setToolTipText("ita Weba"); Browser ci tac = nul1 ; try { citac = new Browser(direktorijum, SWT.N0NE); } catch(SWTError e) { Label natpis = new Label(direktorijum, SWT.B0RDER); natpis.setText("Neuspela inicijalizacija itaa"); kartica.setControl(natpis);

}
if(citac != null) { ci tac.setUrl("http://www.mindview.net"); kartica.setControl(citac);

} }
public static void main(String[] args) { SWTKonzola .run(new OknoSKarticama(), 800, 600);

} } ///:Ovde m etoda createC ontents( ) zadaje raspored i zatim poziva m etode koje prave razliite vrste kartica. Tekst na svakoj kartici zadaje se m etodom setT ext( ) (pored toga, na karticam a m oete praviti dugm ad i crtee), a za svaku je zadat i njen priruni tekstualni opis. Na kraju svake m etode poziva se setC on trol( ) koja kontrolu napravljenu u m etodi smeta u prostor ijaloga te kartice. M etoda labelTabC ) prikazuje jednostavan tekstualni natpis. M etoda directorvDialo g Iab ( ) sadri dugm e koje otvara stanardan objekat tipa D irectoryDialog, u kojem korisnik bira direktorijum . Rezultat te m etode se prikazuje kao tekst tog dugm eta.

1160

Misliti na Javi

M etoda buttonTab( ) prikazuje raznu osnovnu dugm ad. M etoda sIiderTab( ) ponavlja Swing prim er iz prethodnog dela poglavlja u kojem se kliza vee s trakom za prikazivanje napredovanja. M etoda scribbleTab( ) aljivo prikazuje grafike m ogunosti. Program za crtanje je napravljen pom ou nekoliko redova koda. Najzad, m etoda browserTab( ) prikazuje snagu SWT kom ponente Browser - to je potpuni ita Weba u jednoj kom ponenti.

Grafika
Ovo je Swing progrSm Sinusoida.java preveden u SWT:
//: swt/Sinusoida.java // Swing program Sinusoida.java preveden u SWT. import swt.uti1 .* ; import org.ecl1pse.swt.*; import org.eclipse.swt.widgets.*; import o rg.eclipse.swt.events.*; import org.eclipse.swt.layout.*; class CrtajSin'usoidu extends Canvas { private static final int FAKTORSKALE = 200; private int ciklusi; private int tacke; private double[] sinusi; private int[] tck; public CrtajSinusoidu(Composite roditelj, int stil) { super(roditelj, stil); addPaintListener(new PaintListener() { public vojd paintControl(PaintEvent e) { int maksimalnaSirina = getSize().x; double 'hkorak = (double)maksimalnaSirina / (double)tacke; int maksimalnaVisina = getSize().y; tck = new int[tacke]; , for(int i = 0; i < tacke; i++) tck[i]*= (int) ((sinusi [i] * maksimalnaVisina / 2 * '.95) + (maksimalnaVisina / 2)); e.grafKontekst.setForeground( e.priJ<az.getSystemColor(SWT.COLOR_RED)); for(int/n = 1; i < tacke; i++) { int xl = (int)((i - 1) * hkorak); int x2 = (int)(i * hkorak); int yl = tck[i - 1]; i nt y2 = tck[i]; e.grafKontekst.drawLine(xl, yl, x2, y 2 ) ;

} } });

Poglavlje 22: Grafika korisnika okruenja

1161

setCycles(5);

}
public void zadajCikluse(int noviCiklusi) { ciklusi = noviCiklusi; tacke = FAKTORSKALE * ciklusi * 2; sinusi = new double[tacke]; for(int i = 0; i < tacke; i++) { double radijani = (Math.PI / FAKTORSKALE) * i; sinusi[i] = Math.sin(radijani);

}
redraw();

} }
public class Sinusoida implements SWTAplikacija { private CrtajSinusoidu sinusi; private Slider klizac; public void createContents(Composite roditelj) { roditelj ,setLayout(new GridLayout(1, true)); sinusi = new CrtajSinusoidu(roditelj, SWT.N0NE); sinusi,setLayoutData( new GridData(SWT.FILL, SWT.FILL, true, true)); sinusi.setFocus(); klizac = new S1ider(roditelj, SWT.H0RIZ0NTAL); klizac.setValues(5, 1, 30, 1, 1, 1); klizac.setLayoutData( new GridData(SWT.FILL, SWT.DEFAULT, true, false)); klizac.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent dogadjaj) { si nusi.zadajCi klusefkl izac.getSel ection());

} }); }
public static void main(String[] args) { SWTKonzola.run(new Sinusoida(), 700, 400);

1III-Kao to je JPanel u Svvingu osnovna podloga (povrina) za crtanje, tako je u SWT-u objekat tipa Canvas, tj. ,,platno (engl. canvas). Ako ovu verziju program a uporedite sa Swing verzijom, videete da je m etoda CrtajSinusoidu gotovo identina. U SWT-u se grafiki kontekst grafKontekst dobija iz objekta dogaaja koji se prosleduje prijem niku PaintListener, a u Swingu se objekat tipa G raphics iK 'p o s re d n o p rosledu jc' m e t o d i p a in tC o m p o n e n t(). Ali su o p e r a c ij e k oje se izvravaju nad grafikim objektom jednake, a identina je i m etoda zadajC ikluse( ). Za m etodu createC ontents( ) p otrebno je neto vie koda nego za Swing verziju za rasporeivanje kom ponenata i priprem u klizaa i njegovog prijem nika, ali su osnovne operacije koje se izvravaju opet priblino jednake.

1 162

Misliti na Javi

Paralelno izvravanje u SU/T-u


Iako se AWT/Swing izvrava u jednoj niti, to je lako naruiti i proizvesti program koji se izvrava nedeterm inistiki. U sutini, prikaz ne sm ete da ispisujete pom ou vie niti, inae e one poeti da se sudaraju na neverovatne naine. SWT to ne dozvoljava - bacie izuzetak kada prikaz pokuate da ispiete p om ou vie niti. Tim e se spreava da neiskusni program eri nenam erno naprave tu greku i tako u program unesu ,,bubicu koja se teko otkriva. Ovo je Swing program O bojeneK utije.java preveden u SWT:
//: swt/ObojeneKutije.java // SWT prevod Swing programa ObojeneKutije.java. import swt.util.*; import org.eclipse.swt.*; import org.eclipse.swt.widgets.* ; import org.eclipse.swt.events.*; import org.eclipse.swt.graphics.*; import org.eclipse.swt.1ayout.*; import java.util.concurrent.*; import java.util.*; import net.mindview.util.*; class ObKut extends Canvas implements Runnable { class PrijemnikZalscrtavanjeObKut implements PaintListener { public void paintControl(PaintEvent e) { Color boja = new Color(e.prikaz, bBoja); e.grafKontekst.setBackground(boja); Point velicina = getSize(); e.grafKontekst.fillRectangle(0, 0, ve!icina.x, velicina.y); boja.dispose();

} }
private static Random pseudoSlucajanBroj = new Random(); private static RGB newColor() { return new RGB(pseudoSlucajanBroj.nextInt(255), pseudoSlucajanBroj.nextlnt(255), pseudoSlucajanBroj.nextlnt(255));

}
private int pauza; private RGB bBoja = newColor(); public ObKut(Composite roditelj, int pauza) { super(roditelj, SWT.N0NE); this.pauza = pauza; addPaintListener(new PrijemnikZalscrtavanjeObKut());

}
public void run() { try { while(IThread.interrupted()) { bBoja = newColor();

Poglavlje 22: Grafika korisnika okruenja

1163

getDisplay().asyncExec(new Runnable() { public void run() { try { redraw(); } catch(SWTException e) {} // Izuzetak SWTException ne smeta kada je roditelj // okonan na niem nivou izvravanja.

} });
Timellni t.MILLISECONDS.sleep(pauza);

}
} catch(InterruptedException e) { // Prihvatljiv nairl izlaska } catch(SWTException e) { // Prihvatljiv nain 'ilaska: roditelj // okonan na niem nivou izvravanja.

} } }
public class ObojeneKutije implements SWTAplikacija { privat.e int brojcelija = 12; private int pauza = 50; public void createContents(Composite roditelj) { GridLayout tabela = new GridLayout(brojcelija, true); tabeia.horizontalSpacing = 0; tabela.verticalSpacing = 0; rodi telj.setLayout(tabela); ExecutorService exec = new DaemonThreadPoolExecutor(); for(int i = 0; i < (brojcelija * brojcelija); i++) { final ObKut ok = new ObKut(roditelj, pauza); ok.setLayoutData(new GridData(GridData.FILL_B0TH)); exec.execute(ok);

public static void main(String[] args) { ObojeneKutije kutije = new ObojeneKutije(); if(args.1ength > 0) kutije.brojcelija = new Integer(args); if(args.length > 1) kutije.pauza = new Integer(args); SWTKonzola.run(kutije, 500, 400);

} } ///:Kao u prethodnom prim eru, iscrtavanjem se upravlja tako to se napravi objekat tipa PaintL istener s m etodom p a in tC o n tro l() koja se poziva kada je SVVT nit sprem na da iscrta kom ponentu. Taj prijem nik tipa P aintL istener prijavljuje se (registruje) u konstruktoru klase ObK ut.

1164

Misliti na Javi

U ovoj verziji program a ObKut znatno se razlikuje m etoda r u n ( ) koja m etodu za iscrtavanje redraw ( ) ne moe da pozove neposredno, nego m ora da je poalje m etodi asyncE xec( ) objekta tipa Display, to radi priblino kao m etoda SwingUtilities.invokeLater( ). Ukoliko takav poziv zam enite neposrednim pozivom m etode redraw (), videete da e program stati. Kada pokrenete prethodni program , videete da se u prozoru povrem eno pojavljuju nekakve male horizontalne linije. To je posledica injenice da SWT podrazum evano tiije dvostruko baferisan kao Swing. Prim etiete to jasnije kada Swing verziju pokrenete naporedo sa SWT verzijom. Kod za dvostruko baferisanje SWT-a m oete sam i da napiete; prim ere za to nai ete na Web lokaciji www.eclipse.org. Veba 41: (4) Izm enite swt/ObojeneKutije.java tako da poinje prskanjem taaka (,,zvezdica) po platnu, kojim a se p otom boje m enjaju nasum ino.

Poreenje SWT-a i Svvinga


Iz ovako kratkog uvoda teko je stei p o tp u n u sliku, ali trebalo bi da barem ponete da uviate kako SWT u m nogim situacijam a om oguuje pisanje jednostavnijeg koda nego Swing. M eutim , program iranje grafikih korisnikih okruenja u SWT-u ipak je sloeno, pa bi razlozi za korienje SWT-a verovatno trebalo da budu sledei: prvo, om oguiti korisniku transparentniji doivljaj prilikom korienja vae aplikacije (poto ona izgleda/ ponaa se kao ostale aplikacije na platform i), i drugo, ukoliko je vana brzina reagovanja koju SWT donosi. U protivnom , odgovarajui izbor je verovatno Swing. Veba 42: (6) Izaberite neki od Swing prim era koji u ovom odeljku nije bio preveden i prevedite ga u SWT. (Napom ena: ovo je d o bar dom ai zadatak za ceo razred, poto vodi nesadri reenja.)

Saetak
Od svih biblioteka u Javi, biblioteka grafikih okruenja pretrpela je najvee izmene. AWT iz Jave 1.0 esto je kritikovan kao jedan od najgorih projekata koji su se ikada pojavili, i m ada je om oguavao izradu prenosivih program a, dobijeno grafiko korisniko okruenje bilo je podjednako osrednje na svim platformama". Bilo je ogranienih m ogunosti i nezgrapno, a koristilo se tee nego m atine alatke za razvoj aplikacija dostupne za razne platforme. Kada su u Javu 1.1 uvedeni nov model dogaaja i zrna Jave, stanje je poboljano: sada je m ogue napraviti kom ponente grafikog okruenja koje se lako prevlae u n u tar alatki za vizuelno pravljenje aplikacija. O sim toga, m odel dogaaja i zrna jasno je pokazao kako se vodilo rauna o lakoi program iranja i odravanja koda (to nije bilo oigledno u biblioteci AWT Jave 1.0). M eutim , tek kada su se pojavile klase IFC/S\ving, posao je okonan. Uz kom ponente biblioteke Svving, program iranje grafikih okruenja prenosivih na razne platform e postalo je relativno bezbolno. Prava revolucija odigrala se u oblasti alatki za pravljenje aplikacija. Ako elite da se pobolja neka kom ercijalna alatka za pravljenje aplikacija, m orate da drite paleve i nadate

Poglavlje 22: Grafika korisnika okruenja

1165

se da e vam autori okruenja dati ono to elite. Ipak, Java je otvoreno okruenje, to znai da dozvoljava konkurentska okruenja za pravljenje aplikacija, i podrava takav pristup. D a bi te alatke iko uzeo u obzir, one m oraju da podravaju Javina zrna. To podrazum eva otvoreno igralite: ako se pojavi bolja alatka za pravljenje aplikacija, vie neete m orati da koristite o nu koju ste dotad koristili, ve moete da preete na novu i tim e poveate produktivnost. Ovakva vrsta konkurentskog okruenja za vizuelne alatke ranije nije postojala, a trite koje se na ovaj nain bude form iralo m oe dati sam o pozitivne rezultate u pogledu produktivnosti program era. Ovo poglavlje trebalo je da vas uvede u osnove program iranja GKO i da vam prikae osnovne tehnike za prilino lako pronalaenje sopstvenog p u ta kroz biblioteku. O no to ste dosad videli verovatno e zadovoljiti dobar deo vaih potreba pri projektovanju korisnikih okruenja. M eutim , Swing, SWT i Flash/Flex m ogu i m nogo vie od toga, jer sadre p o tp u n asortim an alatki za grafika okruenja. Verovatno postoji nain da postignete gotovo sve to m oete da zamislite.

Resursi
M rene prezentacije Bena G albraitha na adresi w w w .galbraiths.org/presentationslepo objanjavaju i Swing i SWT. Reenja odabranih vcbi data su u elektronskom dokumentu The Thinking in Java Annotated Solution Gtiide, koji se moe kupiti na lokaciji www.MindView.cotn.

A: Docfaci
Ova knjiga im a vie dodataka, m e u kojim a su i sadraji, sem inari i usluge dostupni na Web lokaciji M indView .

Te d o p u n e s u o p i s a n e u o v o m d o d a t k u k a k o b i s t e m o g l i d a p r o c e n i t e d a l i v a m one m ogu koristiti. O bratite panju na to da se sem inari obino dre javno, ali m ogu biti drani i privatno, na vaoj lokaciji, sam o za vae osoblje.

Dodaci koji se mogu preuzeti sa Interneta


Kod za ovu knjigu m oete preuzeti na adresi w w w .M in d V iew .n et. Obuhvaene su i Ant build datoteke i druge datoteke za podrku neo phodne za uspeno autom atizovano prevoenje i pakovanje program a i izvravanje prim era iz ove knjige. Sem toga, neki elovi knjige prebaeni su u elektronski oblik. U njim a su obraene teme: Kloniranje objekata Prosleivanje i vraanje objekata Analiza i projektovanje Delovi drugih poglavlja iz treeg izdanja knjige M isliti na Javi koji nisu bili dovoljno relevantni da bi bili objavljeni i u etvrtom izdanju.

Misliti na jezik u C: osnova za Javu


Na lokaciji w w w .M in d V iew .n et m oete besplatno preuzeti sem inar T h in kin g in C. Prezentaciju je smislio C huck Allison, a razvila kom panija MindVievv. Radi se o m ultim edijalnom Flash kursu koji daje uvod u sintaksu, operatore i funkcije jezika C, na osnovu kojih je napravljena Java. Vodite rauna o tom e da na raunaru na kojem elite da pokrenete sem inar T hinking in C m orate im ati instaliran Flash Player (koji m oete besplatno preuzeti na lokaciji
w w w .A dobe.com ).

Seminar Thinking in Java


Moja kom panija (MindVievv, Inc.) dri petodnevne javne ili privatne sem inare za obuku kroz neposredno iskustvo, iju osnovu ini materijal ove knjige. Ovaj sem inar (ije je prethodno ime bilo H an d s-O n Java) na je glavni uvodni sem inar i osnova za naprednije seminare. Svaku lekciju ini izabrani materijal iz pojedinog poglavlja. Zatim sledi period vebanja pod nadzorom , kada se svakom .studentu pa/nja posvecuje pojedinano. Na lokaciji w w w .M in dV iew .net pronai ete inform acije o rasporedu i m estu odravanja, svedoenja bivih polaznika i razne druge pojedinosti.

Dodatak A: Dodaci

1167

CD sa seminarom Hands-On Java


CD H a n d s-O n Java napravljen je na osnovu ove knjige, a sadri proirenu verziju m aterijala sa sem inara T h in kin g in Java. Barem ete delim ino iskusiti kako je biti na sem inaru a pri tom neete putovati ni plaati kotizaciju. Svako poglavlje ove knjige predstavljeno je na C D -u odgovarajuim zvunim zapisom jednog predavanja i prateim slajdovima. Ja sam osm islio sem inar i ja izgovaram tekst predavanja na CD -u. M aterijal je u form atu Flash i m oe biti reprodukovan na svakoj platform i koja podrava Flash Player. CD H a n d s-O n Java m oete kupiti na lokaciji w w w .M indV iew .net, a tam o su i probni prim eri koji se m ogu besplatno preuzeti.

Seminar Thinking in O bjects


Ovaj sem inar predstavlja objektno orijentisano program iranje vieno oim a projektanta. Ispituje se postupak razvoja i izgradnje sistema, prvenstveno ,,brzim ili ,,lakim m etodama, naroito m etodam a Ekstrem nog program iranja (XP). Predstavljam metodologije uopte, male alatke kao to je tehnika planiranja pom ou indeksnih kartica" (opisna u knjizi P lanning E xtrem e Program m ing Becka i Fowlera, Addison-Wesley, 2001), CRC kartice za projektovanje objekata, program iranje parova, planiranje iteracija, testiranje jedinica, autom atizovanu izgradnju program a, kontrolu izvornog koda i sline teme. Kurs obuhvata i jedan XP projekat koji razvijamo nedelju dana. Ako kreete u neki projekat i eleli biste da upotrebljavate objektno orijentisane tehnike projektovanja, m oem o upotrebiti va projekat kao p rim er i do kraja sedmice napraviti prvu grubu verziju reenja. Na lokaciji w w w .M in d V iew .n et pronai ete inform acije o rasporedu i m estu odravanja, svedoenja bivih polaznika i razne druge pojedinosti.

Thinking in Enterprise Java


Ova knjiga je nastala od nekih naprednijih poglavlja iz ranijih izdanja knjige M isliti na Javi. To nije drugi tom knjige M isliti na Javi, nego fokusirano objanjenje naprednijih tema program iranja za poslovne prim ene. Trenutno je d ostupna (u nekom obliku, verovatno se jo razvija) kao besplatan elektronski dokum ent koji se moe preuzeti na lokaji w w w .M in d V iew .n et. Poto se radi o zasebnoj knjizi, ona se iri kad god treba da obuhvati neku novu tem u. Svrha je ista kao ona knjige M isliti na Javi: dati veoma razumljiv uvod u osnovne tehnologije program iranja za poslovne prim ene, da bi italac bio sprem an za naprednije obrade tih tema. M eu obraenim tem am a bie i: Uvod u program iranje za poslovne prim ene M re/.no program iranje pom ou utinica i kanala Daljinsko povezivanje m etoda (RMI) Povezivanje s bazam a podataka Usluge im enovanja i usluge imenika Servleti

1168

Misliti na Javi

Javine serverske stranice (JSP) Oznake, JSP fragm enti i jezik za izraze A utom atizovanje pravljenja korisnikih okruenja Z rna Jave u poslovnim prim enam a XML Web usluge A utom atsko testiranje Tekui sadraj knjige T hin kin g in Enterprise ja va m oete preuzeti na lokaciji
w w w .M indV iew .net.

Thinking in Patterns (with Java)


Jedan od najvanijih doprinosa u projektovanju objektno orijentisanih program a jesu projektni obrasci" iji je razvoj hronoloki opisan u knjizi Design Patterns, koju su napisali G am m a, Helm , Johnson i Vlissides (Addison-Wesley, 1995). Ta knjiga opisuje 23 opte klase problem a i njihova program ska reenja, napisana prvenstveno na C + + -u. Knjiga Design P atternsje postala kljuni.gotovo obavezan izvor O O P program era. Knjiga T h in kin g in Patterns predstavlja osnovne pojm ove projektnih obrazaca na prim erim a iz Jave. To nije puki prevod knjige Design Patterns na Javu, ve jedna nova perspektiva data sa stanovita Jave. Ne bavi se sam o sa 23 klasina obrasca, nego, po potrebi, i drugim idejam a i tehnikam a za reavanje problem a. Nastala je kao poslednje poglavlje prvog izdanja knjige M isliti na javi. Kako su se razvijale te ideje, postalo je jasno da je za njih potrebna zasebna knjiga. U vrem e pisanja ovog teksta on a se jo razvija, ali je m aterijal iz nje obraivan i preraivan u m nogim prezentacijam a sem inara Objects & Patterns (koji je sada podeljen u sem inare D esigning Objects & System s i T h in king in Patterns ). Vie inform acija o toj knjizi potraite na adresi w w w .M in d V iew .n et.

Seminar Thinking in Patterns


Ovaj sem inar se razvio iz sem inara Objects & Patterns ( O bjekti & obrasci), koji sm o Bill Venners i ja drali u nekoliko proteklih godina. O n je postao preopiran, pa sm o ga podelili na dva: ovaj i prethodno spom enuti D esigning Objects & System s ( P rojektovanje objekata i sistem a).

Sem inar se strogo dri materijala i naina predstavljanja m aterije iz knjige T h in kin g in Patterns, pa ete sadraj sem inara najbolje upoznati iz te knjige, koju m oete preuzeti na Iokaciji w w w .M indV iew .n et.
U o b r o m d e lu p re z e n ta c ijc nagla.ava sc p r o c c s e v o lu c ijc p r u j c k t a - o d p o c c t n o g

reenja, preko logike i evolucije reenja do prikladnijeg dizajna. Poslednji prikazani projekat (simulacija reciklae smea) evoluirao je tokom vrem ena, i njegov razvoj moe da poslui kao prototip za nain na koji va dizajn moe da nastane: kao adekvatno reenje odreenog problem a, koje evoluira u prilagodljivo reenje cele klase problem a.

Dodatak A: Dodaci

1169

Ovaj sem inar e vam pom oi da: znaajno poveate prilagodljivost svojih projekata; dizajnirate projekat tako da bude proiriv i viekratno upotrebljiv; ostvarite bolju kom unikaciju o projektim a tako to ete koristiti jezik obrazaca. N akon svake lekcije sledi niz vebi koje reavate pom ou obrazaca. Biete voeni tokom pisanja program a u kojim a se konkretni obrasci p rim enjuju kao reenja program skih problem a. Na lokaciji w w w .M itidV iew .net moete pronai inform acije o rasporedu i m estu odravanja, svedoenja bivih polaznika i razne druge pojedinosti.

Konsultacije i revizije dizajna


Moja kom panija prua i usluge konsaltinga, m entorskog rada, te revizije dizajna i realizacija koie olakavaju vodenje projekta kroz ceo njegov ciklus razvoja; sve to m oete dobiti i za prvi Java projekat vaeg preduzea. Vie inform acija o pojedinostim a i vremenskoj dostupnosti tih usluga potraite na adresi w w w .M in dV iew .n et.

B: Resursi
Softver
R azvojni p ro g ra m sk i paket za Javu (engl. Java D evelo p m en t Kit, JDK) m oe se preuzeti s lokacije http://java.sun.com . ak i ako oluite da koristite razvojno okruenje drugih proizvodaa, uvek je dobro im ati JDK pri ruci ako naiete na neto to bi moglo da bude greka prevodioca. JDK je standard, pa su greke u prevodiocu tog paketa uglavnom poznate. H TM L d o k u m en tacija razvojnog p rog ram sk og p ak eta za Javu na lokaciji h ttp ://ja va.sun.com . Jo nisam pronaao referentni prirunik o standardnim Javinim bibliotekam a koji nije bio zastareo ili onaj koji je sadrao dovoljnu koliinu inform acija. Iako je H TM L dokum entacija kom panije Sun buna, im a greaka i ponekad je toliko tura da je p o tp u n o neupotrebljiva, barem sadri sve klase i m etode. Ljudi esto u poetku im aju otpor prem a korienju Web resursa jer vie vole tam panu knjigu, aii vredi uloiti malo tru d a pa to prevazii i naviknuti se na HTM L dokum ente, barem zato to je pregled potpuniji nego u bilo kojoj knjizi. Tek ako ne m oete da shvatite o em u se radi u uputstvu na W ebu, potraite tam pane knjige.

Programi za ureivanje teksta i alatke za pravljenje aplikacija


U ovoj oblasti vlada zdrava konkurencija. M nogo toga je besplatno (a ono to nije, najee se m oe besplatno isprobati), pa je najbolje da sami isprobate vie alatki i naete onu koju vam odgovara. Evo nekih od njih: JEdit je besplatan program za ureivanje teksta iji je au to r Slava Pestov. Napisan je na Javi, pa je doatna korist to to moete videti na delu ovu lava aplikaciju za stone raunare. Ovaj program za ureivanje teksta im a m nogo softverskih dodataka, od kojih su najvei deo napisali lanovi zajednice korisnika Jave. Preuzm ite sa adrese www .jedit.org. N etB eans, Sunova besplatna alatka za pravljenje aplikacija koja se moe preuzeti sa w w w .netbeans.org. Projektovana za pravljenje GKO prevlaenjem kom ponenata, za ureivanje koda, otkrivanje i otklanjanje greaka itd. Eclipse, projekat otvorenog izvornog koda koji podrava IBM, m eu ostalima. Platform a Eclipse je osnova koja se moe proirivati, pa povrh nje moete praviti sopstvene sam ostalne aplikaje. Deo tog projekta je biblioteka SWT opisana u poglavlju Grafika korisnika okruenja. Preuzmite sa adrese www.Eclipse.org. IDEA kom panije IntelliJ, omiljen komercijalni proizvod veine program era na Javi, od kojih m nogi tvrde da je IDEA uvek korak-dva ispred Eclipsea, m oda i zato to IntelliJ nc stvara i alatku za pravljenje aplikacija i platlorm u za razvoj, nego sam o 0 1 1 0 prvo. Probnu verziju m oete besplatno preuzeti sa adrese w w w .jetbrains.com .

Dodatak B: Resursi

1171

Knjige
Efikastto program iranje na Javi, Joshua Bloch (M ikro knjiga, 2004). Obavezna lektira. Napisao ju je ovek koji je popravio Javinu biblioteku kolekcija. Pisano po u zoru na klasik Effective C++ koji je napisao Scott Meyer. Core Java 2, sedm o izdanje, H orstm ann & Cornell, u dva tom a (Prentice Hall, 2005). Velika, sveobuhvatna, prva knjiga koju uzm em u ruke kada m i zatrebaju odgovori. Preporuujem vam ovu knjigu kada proitate M isliti na Javi i poelite da preete na vii nivo. The Java Class Libraries: An A nnotated Reference, Patrick C han i Rosana Lee (Addison-Wesley, 1997). Naalost, zastarela je i rasprodata. Takva je trebalo da bude Sunova HTM L okum entacija razvojnog program skog paketa za Javu: sa dovoljno opisa da bude upotrebljiva. O bim na je, skupa, a ni kvalitet prim era m e ne zadovoljava. Pa ipak, m oe vam posluiti kada zapadnete u nevolje, a izgleda da su navedena detaljnija objanjenja nego u veini drugih knjiga. M eutim , Core Java 2 im a aurnija objanjenja m nogih kom ponenata biblioteka. Java N etw ork Programming, drugo izdanje, Eliotte Rusty H arold ( 0 Reilly, 2000). Nisam razum eo umreavanje u Javi (niti um reavanje uopte, to se toga tie) sve dok nisam pronaao ovu knjigu. Sm atram i da Web lokacija autora knjige, Cafe au Lait, prua stim ulativan, struan i aktuelan pogled na poboljanja u Javi, neoptercen obavezama prem a bilo kom proizvoau. Zbog redovnih auriranja, lokacija je u toku s novinam a u Javi koje se javljaju veoma brzo. Posetite lokaciju w w w .cafeaulait.org. Design Patterns, Gamm a, Helm, Johnson & Vlissides (Addison-Wesley, 1995). O riginalna knjiga koja je zapoela razm atranje projektnih obrazaca i koja se spom inje na vie mesta u ovoj knjizi. Refactoring to Patterns, Joshua Kerievsky (Addison-Wesley, 2005). Spaja preradu program a (ponovnu podelu na proste faktore) i projektne obrasce. Najvredniji aspekt ove knjige jeste to to pokazuje kako dalje razviti projekat korienjem projektnih obrazaca po potrebi. The A rt o f UNIX Programming, Eric Raymond (Addison-Wesley, 2004). Iako je Java jezik za pisanje program a koji se izvravaju na svim platform am a, U nix/Linux treba znati zbog dom inacije Jave na serverima. Ericova knjiga je odlian uvod u istoriju i filozofiju ovog operativnog sistema. Zanimljivo tivo i za one koji sam o ele da saznaju neto o korenim a raunarstva.

Analiza i projektovanje
Extreme Program m ing Explained, drugo izdanje, Kent Beck uz pom o Cynthiae Andres (Addison-Wesley, 2005). Oduvek sam oseao da bi m orao postojati m nogo drugaiji, m nogo bolji postupak razvijanja program a, i mislim da m u je XP priao veom a blizu. Jedina kniiga koia je ostavila slian utisak na m ene bila je Peopleware (opisana u nastavku), koja se pretezno bavi okruenjem i opstankom u poslovnom svetu. E xtrem e Program niing Explained govori o program iranju i posm atra m nogo toga, ak i najnovija dostignua, iz

1172

Misliti na Javi

tog ugla. A utori ak tvrde da su slike u redu sve dok ne provodite m nogo vrem ena gledajui ih i sprem ni su da ih izbace. (Prim etiete da se na knjizi ne nalazi peat UML.) Odluivao sam se da li u raditi za neku kom paniju iskljuivo na osnovu toga da li koriste XP. Mala knjiga, kratka poglavlja koja se lako itaju, a teraju na razmiljanje. Poeete da zamiljate kako radite u takvoj atm osferi i otvorie vam se novi vidici. UML ukratko, M artin Fovvler (M ikro knjiga, 2004). Kada se prvi p u t sretnete s jezikom UML, uplaiete se jer im a m nogo dijagram a i detalja. Prem a Fowleru, vei deo toga je nepotreban i on e vam objasniti o n o osnovno. Za veinu projekata potrebno je da poznajete sam o nekoliko alatki za crtanje dijagram a, a Fowlerov cilj je da dobije dobar program , a n e da brine o tom e kako je stigao do njega. Lepa, tanka, itljiva knjiga; prva koju treba da nabavite ako elite da razum ete UML. D om ain-D riveti Design, Eric Evans (Addison-Wesley, 2004). Bavi se tnodelom dom ena kao prvenstvenim obelejem postupka projektovanja. Na osnovu sopstvenog iskustva uvideo sam da je to prem etanje naglaska vano, jer pom ae program erim a da ostanu na pravom nivou apstrakcije. The Unified Sofhvare D evelopm ent Process, Ivar Jacobsen, G rady Booch i James Rum baugh (Addison-Wesley, 1999). Bio sam p o tp u n o siguran da mi se ova knjiga nee svideti. Izgledalo je kao da ima sva obeleja dosadnog fakultetskog udbenika. Ali, prijatno sam se iznenadio: na vrlo malo mesta sreu se objanjenja za koja bi se m oglo pomisliti da ni autorim a nisu jasna. Vei deo knjige ne sam o da je jasan, ve je i prijatan. Najbolje od svega je to to su objanjenja prilino praktina. To ipak nije E xtrem e Program m ing (i nije toliko jasna u pogledu testiranja), ali je takoe deo UML menaerije: ak i ako vam se ne svia XP, veina program era je sada usvojila stavU M L je d o b a r (bez obzira na njihov stvarni nivo iskustva s tom m etodom ). Na kraju, i vi ete m orati da se ukrcate u taj voz. M islim da je ova knjiga najbolja za prouavanje UML-a, i trebalo bi da je proitate ako posle Fowlerove knjige U M L ukratko poelite da saznate vie detalja. Pre nego to se odluite za bilo koju m etodu, korisno je da se posavetujete s nekim ko ne eli da vas ubedi da je koristite. esto se deava da se neka m etoda usvoji, a da se pri tom ne razum e zaista ta se eli od nje ili koje e prednosti ostvariti. Drugi je koriste, to izgleda kao dovoljno privlaan razlog. Ljudi im aju jednu udnu osobinu: ako ele da veruju kako e neto reiti njihove problem e, oni e to i probati. (To je eksperimentisanje, to je dobro.) M eutim , ako ne ree svoje problem e, m oda e udvostruiti napore i na sva usta objaviti kako su otkrili neto sjajno. (To je nepriznavanje stvarnosti, to nije dobro.) M oda je pouka da neete biti usam ljeni, ako nagovorite druge ljude da se ukrcaju na isti brod, ak i ako taj brod ne ide nikuda (ili tone). Ovo ne znai da nijedna m etoologija nikuda ne vodi, ve da treba da budete oprem ljeni m entalnim alatkam a koje e vam pom oi da ostanete na nivou eksperimentisanja (Ovo ne radi: hajde da probam o neto drugo.), a da izbegavate odricanje (,,To zapravo i nije problem . Sve ie divno, ne m oram o nita da m eniam o"). Mislim da e vam sledee knjige, ako ih proitate pre nego to izaberete neku m etodu, obezbediti te alatke. Softvvare Creativity, Robert Glass (Prentice Hall, 1995). Ovo je najbolja knjiga koja razm atra perspektivu celog problem a m etodologije. To je zbirka kratkih radova koje je Glass pisao, ponekad i dobijao od drugih (jedan od saranika je P. J. Plauger), a koji izraavaju njegovo dugogodinje razm iljanje i prouavanje ove teme. Dovoljno su zabavni,

Dodatak B: Resursi

1173

a dugaki sam o toliko koliko je potrebno da kau ono to je neophodno. Takoe, radovi nisu sam o uplja pria; nude na stotine referenci na druge radove i istraivanja. Svi program eri i m enaderi trebalo bi da proitaju ovu knjigu pre nego to se zapute na klizav teren m etodologije. Softw are Runaways: M onum ental Software Disasters, Robert Glass (Prentice Hall, 1997). O dlina osobina ove knjige je ta to naglas govori o onom e o em u se ne pria: koliko projekata ne sam o da je propalo, ve je propalo spektakularno. U stanovio sam da veina program era misli: ,,To m eni ne moe da se desi (ili ,,Ne moe da se desi ponovo') i sm atram da to nije dobro. Ako im ate na um u da neto uvek moe da krene naopako, u m nogo ste boljem poloaju da uinite da sve radi. Peopleware, drugo izdatije, Tom DeM arco i T im othy Lister (Dorset H ouse, 1999). Ovu knjigu m orate da proitate. Ne sam o da je zabavna, ona e uzdrm ati svet u kojem prebivate i unititi pretpostavke na kojim a poiva. Iako su autori projektanti softvera, ova knjiga je posveena projektim a i tim ovim a uopte. M eutim , istaknuta je vanost Ijudi i njihovih potreba, a ne tehnologije i njenih potreba. A utori govore o pravljenju okruenja u kom e e ljudi biti zadovoljni i produktivni, a ne donose pravila koja bi ljudi trebalo da potuju kako bi bili odgovarajui delovi maine. Mislim da je ovaj stav najvie doprineo tom e se program eri podsm evaju usvajanju m etode XYZ i da nastave da rade kako su oduvek radili. Secrets o f Consulting: A Guide to G iving & G etting Advice Successfully, Gerald M. VVeinberg (D orste H ouse, 1985). Izvanredna knjiga, jedna od najboljih koju sam proitao. Savrena je ukoliko pokuavate da radite kao konsultant ili plaate konsultante i niste zadovoljni njihovim rezultatim a. Iz kratkih poglavlja ispunjenih priam a i anegdotam a, uite kako doi do sutine uz najm anje napora. Proitajte i nastavak M ore Secrets o f Consulting objavljen 2002. ili gotovo bilo koju drugu W einbergovu knjigu. Com plexity, M. Mitchell W aldrop (Sim on & Schuster, 1992). Knjiga opisuje sastanke grupe naunika razliitih struka u gradu Santa Fe u Meksiku, koji su raspravljali o razliitim problem im a to u njihovim disciplinam a nisu reeni (berza u ekonom iji, nastanak ivota u biologiji, zato Ijudi rade to to rade u sociologiji itd.). Suoavanjem gledita fizike, ekonom ije, hem ije, m atem atike, raunarstva, sociologije i drugih nauka, razvijen je m ultidisciplinarni p ristup tim problem im a. M eutim , vanije je bilo to to se poelo na drugi nain razm iljati o pom enutim , veom a sloenim problem im a: udaljavanje od matem atikog determ inizm a i iluzije da se m oe napisati jednaina koja predvia ponaanje, i pribliavanje stavu da treba prvo posm a tra ti i traiti neku pravilnost, a p otom je oponaati na sve m ogue naine. (Knjiga dokum entuje, na prim er, pojavljivanje genetskih algoritam a.) Sm atram da je ovakav nain razm iljanja koristan jer ukazuje na naine upravljanja sve sloenijim softverskim projektim a.

Jezik Python
L e a rn in g P y th o n , d ru g o izd a n je, M ark Lutz i David Ascher ( 0 Reilly, 2003). Lep uvod za program ere na m om om iljenom jeziku, odlinom pratiocu Jave. Knjiga sadri i kratak uvod u Jython koji om oguuje kom binovanje Jave i Pythona u istom program u (interpretator jezika Jvthon pravi ist Javin bajtkod, pa vam ne treba nita posebno da biste to postigli). U jedinjenje ova dva jezika obeava velike m ogunosti.

1174

Misliti na Javi

Spisak mojih knjiga


N abrojane su po redosledu objavljivanja, ali su neke rasprodate. C om puter Interfacing w ith Pascal & C (objavljena sam ostalno u kui Eisys im print, 1988. M oe se nai sam o na lokaciji w w w .M in d V iew .n et). Uvod u elektroniku iz vrem ena kada je CP/M jo uvek bio kralj, a DOS poetnik. Koristio sam jezike visokog nivoa, a esto i paralelni prikljuak raunara za izvoenje raznih projekata u elektronici. Knjiga je sastavljena od m ojih kolum ni iz M icro Cornucopiae, prvog i najboljeg asopisa za koji sam pisao. Naalost, M icro C je ugaen m nogo pre nego to se pojavio Internet. Pisanje ove knjige za m ene je bilo veom a lepo iskustvo. Using C + + (Osborne/M cG raw -HiIl, 1989). Jedna od prvih knjiga o jeziku C ++. Vie se ne tam pa i zam enjena je drugim izdanjem , ije je im e C ++ Inside & Out. C ++ Inside & O ut (O sborne/M cG raw -H ill, 1993). Kao to sam rekao, ovo je zapravo drugo izdanje knjige Using C++. Jezik C + + je u ovoj knjizi dosta tano opisan, ali je ona iz 1992. godine, pa je knjiga T h in k in g in C++ napisana da bi je zamenila. Vie o ovoj knjizi m oete da saznate na lokaciji w w w .M ind V iew .n et, odakle m oete da preuzm ete i izvorni kod. Thinking in C++, p rvo izdanje (Prentice Hall, 1995). O d asopisa Software Developm e n t M agazine dobiia nagradu za knjigu - podstrek godine. Thinking in C++, drugo izdanje, Volume I (Prentice Hall, 2000). Moe se preuzeti s lokacije w w w .M ind V iew .n et. A urirana u skladu s konanim standardom jezika. Prevod na srpski objavila je M ikro knjiga, 2003. Thinking in C++, drugo izdanje, Volume II, pisano zajedno sa Chuckom Alissonom (Prentice Hall, 2003). Moe se preuzeti s lokacije w w w .M in d V iew .n et. Black Belt C++, the M aster s Collection, Bruce Eckel, urednik (M&T Books, 1994). Rasprodata. Zbirka radova raznih strunjaka za C + + zasnovana na njihovim prezentacijam a na konferenciji Software Developm ent, kojom sam predsedavao. O m ot ove knjige me je naterao da ubudue kontroliem sve korice svojih knjiga. M isliti na Javi, prvo izdanje (Prentice Hall, 1998). Prvo izdanje ove knjige osvojilo je nagradu za produktivnost od asopisa Softw are D evelo p m en t M agazine, nagradu za najbolju knjigu po izboru urednika asopisa Java D eveloper s Journal i nagradu za najbolju knjigu po izboru italaca asopisa JavaWorld. Moe se preuzeti s lokacije w w w .M indView.net.

M isliti na Javi, drugo izdanje (Prentice Hall, 2000, M ikro knjiga, 2002). Ovo izdanje je osvojilo nagradu za najbolju knjigu po izboru urednika asopisa JavaWorld. Moe se preuzeti s lokacije w w w .M in d V iew .n et. M isliti na Javi, tree izdanje (Prentice Hall, 2003). Ovo izdanje je osvojilo nagradu asopisa Softw are D evelop m en t M agazine za knjigu - podsticaj godine, kao i druge nagrade navedene na zadnjoj korici. Moe se preuzeti s lokacije w w w .M in dV iew .n et.

Spisak termina korienih u knjizi


alatka za o b ra d u ano tacija alijas, p seu d o n im a n aliza to r p ro g ram sk ih jezika au to m atizo v an o prevoenje build i pakovanje, izgradnja pro g ram a au to m a tsk o pakovanje bacan je izuzetka, gen erisan je izuzetka b it in d ik a to r b lok za o b ra d u izuzetaka

atinotation processing tool, apt alias scanner

isp itn i blok isp re tu ra n o izg rad n ja p ro g ram a , a u to m a tiz o v an o prev o en je i pakovanje

try block shufflcd build

exception izuzetak izuzetak to k o m izvravanja runtime exception izvrilac executor


jed in ica za p revoenje je d in i n o te stiranje je d n o k ra tn o o tk riv an je tip a k asno vezivanje kliza k lju za h eiranje k o m p le t alatki za a p stra k tn e p rozore k o n s tru k to r k o n s tru k to r bez arg u m e n a ta konverzija h vatanjem k o o p e ra tiv n o v ien itn o p ro g ram ira n je k o stu r p ro g ram a , stru k tu ra p ro g ram a lak o bjekat laka tra jn o st lani objekat leksem a lenja pro cen a literal klase lo kalna m e to d a m e h a n iza m brzog otkazivan ja m eka referenca m iksin n a b ro ja n i tip najd avn ije korienje nasledivanje nastavljanje natklasa n ep ro vereni izuzetak nit nit izvravanja nit za o tp re m u d ogaaja o b jek at lan o b last vaenja o b ra d a izuzetaka od re d ite

autoboxing throvving an exception

bit flag exception handler b lo k za pozive invocation handler brava lock cev pipe cu re n je m em o rije niemory leak ita klasa class browser u v an a oblast guarded rcgion daljinsko pozivanje m etoda Rcmotc Mcthod Invocation, R M l d a to te k a preslikana memory-tnappcd file
u m e m o riju deljen a brava

compilation unit, translation unit unit testing single dispatching late binding slider hash code Abstract Window Toolkit, A \V T constructor no-arg constructor capturc cotiversion cooperative multithrcading application frame\vork light-weight object lightweight persistence mock object token lazy evaluation class literal tiative method fail-fast soft reference mixin enumerated type least-recently-used, LRU inheritance resumption superclass unchecked exception thread thrcad oj cxccution event dispatch thread metnbcr object scopc exception handling targct

shared lock

din am ik a oblast m em orije hcap d in a m i k o vezivanje dynamic binding dn ev n ik , zap isnik log dod eljiv assignablc d o k u m en tacio n a oznaka d v o stra n i red za ekanje d v re d n o st ek sp licitn a konverzija elem en t, spravica fan tom sk a referenca g en erisan je izuzetka, b acanje izuzetka granica h eiran je, tra n sfo rm isa n je kljua identifikacija tipa u v rem e izvravanja id en tifik ato r im ensk i p ro sto r in lin inaciici l ipo\ ima p rilik o m izvravanja in te g risa n o razv ojn o o k ru e n je iskljuiva brava ispis steka

doc tag doublc-cndcd ijueue. deque rvaluc casting widgct phantom refcrence throvving an cxception bound hashing run-tim c type idcntification, RTTI hatidle namcspacc run-tim c typc information, RTTI Intcgratcd Dcvelopment Environmcnt, IDE exclusivc lock stack trace

1176

Misliti na Javi

odseak o k ru e n je za brzo razv ijan je aplikacija o p e ra to r p o m e ra n ja opseg oznak a o zn ak a kraja o zn ak a tip a p a ra le ln o st, p a ra le ln i rad p o istiti p o d o b je k a t pok aziva steka poslati p o sre d n ik i p ro g ra m p o sre d n ik p o v ezan o zakljuavanje poveziva p o v ra tn i poziv p o v rn o ko p iran je poziv u g ra e n d ire k tn o u kod p ra z n o fin aln o polje p re d u p re d n o p rek id an je p rekJapanje o p e ra to ra p rijem n ik prilag o dljiv izgled i p o n a a n je p riru n i savet p ro d u a v an je uz d o d av an je n ula p ro d u av an je uz o u v anje znaka profajler p ro g ram za u itav anje p ro g ra m ira n je s klijentske stra n e p ro g ra m ira n je sa serverske stra n e p s e u d o n im , alijas p u ta n ja klasa ra n o vczivanje raspo rediv a red za ekanje red za ekanie d o g a aja
r c d e f in is a n je

slice Rapid Application Development, RAD shift operator range label end sentinel type tag concurrency clean up subobject stack pointer submit middleware proxy hand-over-hand locking, lock coupling linker callback shallow copy inline call blank final preemptive termination operator overloading listener pluggable look & feel tool tip zero extension sign extension profiler loader client-side programming server-side programming alias classpath early binding layout manager queue eventqueue overriding event handler garbage collector daemon thread event-driven system

skladite sk u plja s k o p ira n je m slaba referenca sp ecifik ator p ris tu p a sp o ljn o n adovezivanje sp o re d an efekat spravica, elem ent stablo stan je o k o n an ja sta n d a rd n a biblio teka ab lo n a statu s p re k in u to sti s tru k tu ra p ro g ram a , k o s tu r p ro g ram a sudar, su k ob svojstvo sv oen je nanie sv oen je nanie koje uva

pool copy collector weak referetice access specifier external chaining side effect widget tree termination condition Standard Template Library, STL interrupted status application framework collision property downcasting type-safe downcast upcasting template stream tuple persistent object progress bar hashing class loader terminated backward chaining inner class comparable race condition deadlock m utex m utual exclusion cause exceptional condition binding runtimc binding stub highly coupled m u Itiple dispa tclii ilg multitasking time stamp timcom closure log firewaU busy waiting Java Beans

tip
svo enje navie ab lon to k p o d a ta k a to rk a tra jn i objekat traka n ap redo v an ja tra n sfo rm isa n je kljua, heiran je uitava klasa ugaen u lan av an je unazad u n u tra n ja klasa u p o red iv uslov za trk u uzajam na blokada u z ajam n o iskljuiva brava u z ajam n o iskljuivanje uzro k v a n re d n o stan je vezivanje vezivanje p rilik om izvravanja vezivna funkcija v isoko s p re g n u t v iek ratn o o tk riv an je tipa vieprogram ski
v rem en sk a o zn ak a v ren ien .sk o o d la g a n je

rukovalac d o g ad ajem saku plja sm ea serv isn a nit sistem k o jim up ravljaju dogadaji

zaklj uak zapisn ik, d n ev n ik zatitn a b arijera zau zeto st ekanjem zrn a Jave

Kompletan spisak tcrmina koji se koriste u izdanjim a Mikro knjigc nalazi se na adresi www.mk. c o .y u /r e c m k.

Indeks
i !>75 !=, 73 @TestObjectCreate, za @Unit, 869 @throws, 61 @Unit, 864 upotreba, 864 @version, 60 > >, 73 >=, 73 ,8 0 = ,8 1

&
&, 79 &&, 75 &=, 80

f
[], operator indeksiranja, 145
A

A
abecedno uredivanje, 325 abstract (apstraktna) klasa, 239 nasleivanje od apstraktnih klasa, 240 rezervisana re, 240 u odnosu na interfejs, 253 AbstractButton, 1069 AbstractSequentialList, 684 AbstractSet, 629 ActionEvent, 1091, 1130 ActionListener, 1055 ActionScript, za Adobe Flex, 1139 Adapter, projektni obrazac, 250,257, 338,495,582, 585,631 adapteri prijemnika, 1065 adapterska metoda, 338 a d d ( ), m etoda klase ArrayList, 303 addActionListener( ), 1128, 1134 addChangeListener, 1094 addListener, 1060 Adler32, 777 Adobe Flex, 1138 Adobe, proizvoa sistema Flex, 1138 agentski orijentisano program iranje, 1042 agregacija, 21

.NET, 39 .new, sintaksa, 270 .this, sintaksa, 270

A, 80 ^ = ,8 0

@
@, simbol za anotacije, 845 @author, 60 @Deprecated, anotacija, 845 @deprecated, Javadoc oznaka, 62 @docRoot, 60 @inheritDoc, 60 @interface, i rezervisana re extends, 854 @link, 60 @Override, 845 @param, 61 @Retention, 846 @return, 61 @see, 60 @since, 61 @SuppressWarnings, 845 @Target, 846 < ' 'l'est, 8 16 @Test, za @Unit, 864 @TestObjectCleanup, oznaka za alatku @Unit, 872

I,79
l= , 80

II,75 + +,71 konverzija u tip String operatorom +, 67, 85, 393 < < ,73 <=, 73
, 80 = , 81

==,73

1178

Misliti na Javi

aktivni objekti, u paralelnom izvravanju, 1039 alatka za prikazivanje, za Swing, 1050 Allison, Chuck, 3,11, 1166, 1174 allocate(), 755, 797-798 allocateD irect(), 755 angaovanje, Teorija rastueg, 915 anonim na unutranja klasa, 275,721, 1053 generika, 508 i kod upravljan tabelom, 684 anotacija, 845 apt, alatka za obradu, 857 dozvoljeni tipovi elemenata, 849 elementi, 847 marker anotacija, 847 podrazumevana vrednost, 853 podrazum evanevrednosti elemenata, 847-848, 850 procesor, 848 procesor koji radi na osnovu refleksije, 854 apstrakcija, 15 apt, alatka za obradu anotacija, 857 argum ent finalni, 204, 722 konstruktor, 116 kovarijantni tipovi argumenata, 559 liste promenljivih parametara ( n e p o z n a t b r o j i tip argum enata), 150 zakljuivanje o tipovima generikih argumenata, 497

argum enti promenljive duine, 150, 578 i generike metode, 499 Arnold, Ken, 1046 ArrayBlockingQueue, 971 ArrayList, lista, 311, 644 a d d ( ), metoda, 303 get( ), m etoda, 303 size( ), metoda, 303 Arrays asL ist(), 307, 340, 648 binarySearch( ), 624 usluna m etoda za pretvaranje sekvence ili niza u kontejner, 616 asC harB uffer(), 757 asocijativan niz, 302, 306 drugo ime za mapu, 661 aspektno orijentisano program iranje (AOP), 565 assert, i @Unit, 867 Atomiclnteger, 932 AtomicLong, 932 AtomicReference, 932 atomska operacija, 926 autom atska konverzija tipova, 181 autom atsko pakovanje, 326, 495 i generiki tipovi, 497, 549 available( ), 740

B
bafer, nio, 754 BASIC, Microsoftov Visual BASIC, 1120 BasicArro\vButton, 1070 Beanlnfo, namenska klasa, 1137 Beck, Kent, 1171 bezuslovni skok, 106

biblioteka autor u odnosu na program era klijenta, 159 projekat, 159 upotreba, 159 binarni brojevi, 78 brojevi, ispisivanje, 84 operatori, 80 binarySearch( ), 624, 706 BitSet, 716 Bloch, Joshua, 130,805,914, 929 BlockingQueue, 971, 988 blok za obradu izuzetka, 348 blok za pozive, 466 blokiranje i metoda available(), 740 u program im a za paralelno izvravanje, 887 Booch, Grady, 1172 boolean, 98 algebra, 79 i eksplicitna konverzija tipova, 87 operatori koji ne rade s logikim argum entim a, 73 u odnosu na C i C++, 75 Borlandov Delphi, 1120 BoxLayout, 1059 Brajanovo pravilo sinhronizacije, 922 brave eksplicitne, u paralelnom izvravanju, 923 optimistiko zakljuavanje, 1034 takmienje za, u paralelnom izvravanju, 1019 u paralelnom izvravanju, 922

Indeks

1179

break, rezervisana re, 107 brisanje, 551 u generikom kodu, 512 brojanje referenci, sakupljanje smea, 133 brojevi, binarni, 78 Budd, Timothy, 16 BufferedlnputStream, 733 BufferedOutputStream, 734 BufferedReader, 377, 736, 738 BufferedVVriter, 736, 740 ButtonGroup, 1070, 1079 ByteArrayInputStream, 731 ByteArrayOutputStream, 732 ByteBuffer, 754

c
C#, programski jezik, 39 C++, 73 obrada izuzetaka, 384 Standardna biblioteka ablona (Standard Template Library, STL), 718 abloni, 485, 513 CachedThreadPool, 894 Callable, paralelno izvravanje, 896 case, naredba, 112 CASF._INSENSITIVE_ ORDER, String Com parator, 705, 720 cast( ),444 catch hvatanje izuzetka, 348 hvatanje bilo kog izuzetka, 356 iv/'iTvisanu re, 348 cepanje rei, u paralelnom izvravanju, 926 cepanje, cepanje rei, 926

cev, 731, 745 i ulazno/izlazne operacije, 976 Chain of Responsibility, projektni obrazac, 826 CharArrayReader, 735 CharArrayWriter, 735 CharBuffer, 757 CharSequence, interfejs, 414 Charset, 758 checkedCollection(), 562 CheckedlnputStream, 775 checkedList(), 562 checkedM ap(), 562 CheckedOutputStream, 775 checkedSet( ), 562 checkedSortedM ap(), 562 checkedSortedSet(), 562 Checksum, klase, 777 Chiba, Shigeru, Dr., 881,883 Class, 1071 forName( ),436, 1063 generike reference klasa, 441 getCanonicalNamef ),438 getClass( ), 357 getConstructors( ), 464 getlnterfaces( ), 438 getMethods( ), 464 getSimpleName( ), 438 getSuperclass(), 438 isAssignableFrom( ),454 islnstance(), 453 islnterface( ), 438 newlnstance( ),438 objekat tipa Class, 434, 795, 922 postupak pravljenja objekata, 142 reference, i dokeri, 442 reterence, i ogranienja, 442 RTTI pomou objekta tipa Class, 434

ClassCastException, 237,445 ClassNotFoundException, 450 classpath, sistemska promenljiva, 163 clea r(), nio, 756 clo se(), 738 collection, 332, 705 Collections, klasa en u m eratio n (), 714 fill(), 629 m etoda addAll( ), 307 unmodifiableList( ), 648 Com mand, projektni obrazac, 295,473, 822, 893 Comparable, interfejs, 619, 653, 658 Comparator, interfejs, 621 compareTo( ), u paketu java.lang.Comparable, 619,655 ConcurrentHashM ap, 664, 1027, 1032 ConcurrentI.inkedQueue, 1027 ConcurrentM odificationException, 709 izbegavanje pom ou klase CopyOnWriteArrayList, 1027, 1041 Condition, klasa, u paralelnom izvravanju, 968 continue, rezervisana re, 107 Coplien, Jim ablonski obrazac koji se neobino ponavlja, 556 CopyOnWriteArrayList, 1003, 1027 CopyOnWriteArraySet, 1027

1180

Misliti na Javi

CountDownLatch, za paralelno izvravanje, 983 CRC32, 777 crtanje na panou u Swingu, 1093 crtanje u Swingu, 1092 CSS (kaskadni opisi stilova), i Adobe Flex, 1144 CyclicBarrier, za paralelno izvravanje, 986

ista supstitucija, 25, 235 ienje i skuplja smea, 191 izvravanje, 131 provera stanja okonanja metodom finalize(), 131 uz finally, 368 ita klasa, 175 itanje standardnog ulaznog toka, 749 lan funkcija lanica, 19 inicijalizatori, 225 objekat, 21 uvana oblast, u obradi izuzetaka, 348

gubitak podataka izlazne datoteke, greke i pranjenje bafera, 741 JARarhiva, 161 obeleja, 728 preslikana u mem oriju, 769 zakljuavanje, 772 datoteka klase, analiza, 879 decode( ), ema kodiranja, 759 Decorator, projektni obrazac, 568 default, rezervisana re u naredbi switch, 112 defauItR eadO bject(), 792 defaultW riteObject( ), 792 D eflaterOutputStream , 775 Delayed, 990 DelayQueue, za paralelno izvravanje, 988 delegiranje, 188, 568 Delphi, kompanije Borland,
1120

D
DatagramChannel, 773 D atalnput, 737 D atalnputStream, 733, 736, 739 D ataOutput, 737 DataOutputStream, 734, 737 datoteka dijalozi, 1099 File, klasa, 719, 731,737 File.list( ), 719

deljenje, 69 DeMarco, Tom, 1173 deque, dvostrani red za ekanje, 319, 660 destruktor, 129, 131,368 Java ga nem a, 191 dijagram diiagrami nasledivanja klasa, 199 nasleivanje, 28 dijalog okvir za, 1095 okno s jezicima, 1083 za rad s datotekam a, 1099 Dijkstra, F.dsger, 978 dim, dinamika bezbednost tipova i kontejneri, 562 posrednik, 466

prom ena ponaanja pom ou kompozicije, 234 provera tipova u Javi, 647 sintaksa za skupnu inicijalizaciju nizova, 597 vezivanje, 211,214 direktorijum i paketi, 168 listanje, 719 pravljenje direktorijuma i putanja, 728 disjunkcija, 86 (II), 75 dispose( ), 1096 dodela objekata, 67 dodela vrednosti, 67 dogaaj dogaaji i prijemnici, 1059-1060 dogaaji i zrna Jave, 1121, 1130 model, Swing, 1059 odgovor na Svving dogaaj, 1052 program iranje upravljano dogadajima, 1052 sistem upravljan dogaajima, 290 dokumentacija, 11 kom entari i ugradena dokumentacija, 57 dostavlja poruka, idiom, 487, 632, 684 dostini objekti i skupljanje smea, 710 double i vienitni rad, 926
m arker (d ili D ' literala,

78 do-whi!e, 101 drugi komplement, 84

Indeks

1181

dugme pravljenje sopstvenog, 1066 radio-dugm e, 1078 Swing, 1051, 1069 dvokratno otkrivanje tipa, 836 pom ou mape EnumMap, 842 dvostrani red za ekanje (deque), 319 dvrednost, 67

D
dokeri i Class reference, 442. nadtipova, 539 neogranieni, 541 u generikim tipovima, 534

E
East, BorderLayout, 1056 efikasnost i nizovi, 593 i finalnost, 207 eksplicitno zadavanje tipov'a argum enata generikih metoda, 309, 499 eksponencijalni zapis, 78 else, rezervisana re, 99 encode( ), ema kodiranja, 759 endian big endian, 763 little endian, 763 entrySet( ), metoda interfejsa Map, 673 enum. I 'idcli nabrojani tipovi Enum eration, 713 EnumMap, 821

EnumSet, 505, 718 um esto indikatora, 819 equals( ), 73 i hashCode( ), 653,679 i strukture podataka s transform isanim kljuevima, 671 redefinisanje za HashMap, 670 uslovi za pravilno definisanje, 670 Erlang, programski jezik, 888 EventSetDescriptor, 1126 Exchanger, klasa, u paralelnom izvravanju, 1001 Executor (izvrilac), paralelno izvravanje, 893 ExecutorService, 893 extends, rezervisana re, 172, 185,235 i @interface, 854 i interfejs, 254 rezervisana re, 183 Externalizable, 785 alternativa za, 791 Extreme Programm ing (XP), 1171

Fa<;ade, 452 Factory Method, projektni obrazac, 262 faktor optereenja, objekata tipa HashMap ili HashSet, 701 false, 75 FeatureDescriptor, 1137 Fibonacci, 494 Field, klasa za refleksiju, 462

FIFO (prvi izlazi onaj koji je prvi uao), 329 FileChannel, 754 FileDescriptor, 731 FilelnputReader, 738 FilelnputStream, 731 FileLock, 773 FilenameFilter, 719 FileNotFoundException, 378 FileOutputStream, 732 FileReader, 377, 735 FileWriter, 735, 740 fillInStackTrace( ), 359 filozofi, koji veeraju, prim er uzajamne blokade u paralelnom izvravanju, 978 FilterlnputStream, 731 FilterOutputStream, 732 FilterReader, 736 FilterWriter, 736 final, 488 final (finalno), 243 argument, 204, 722 i efikasnost, 207 i privatnost, 205 i reference objekata, 201 i statinost, 201 klase, 206 metoda, 215 metode, 204, 232 podaci, 200 prazna finalna polja, 203 rezervisana re, 200 statini prosti tipovi, 202 finalize( ), 129, 194, 379 direktno pozivanje, 131 i nasleivanje, 226 finallv, 191, 194 i konstruktori, 377 i return, 371 mana, 372

1 182

Misliti na Javi

ne izvrava se u sluaju servisnih niti, 905 rezervisana re, 367 FixedThreadPool, 894 Flex OpenLaszlo, alternativa za Flex, 1138 sistem kompanije Adobe, 1138 flip( ), nio, 755 float istina i neistina u poreenju brojeva s pokretnim zarezom, 76 marker (F) literala, 78 FlowLayout, 1057 Flyweight, projektni obrazac, 635, 1043 for, rezervisana re, 101 foreach, 103, 107, 150-151, 167,291,305,328, 334, 426,494-495, 549, 805, 827 i Adapterska metoda, 338 i interfejs Iterable, 336 i Iterable, 336 format preciznost, 402 prikazivanja znakovnih nizova, 400 specifikatori, 402 format( ), 400 Formatter, 401 forName( ),436, 1063 FORTRAN, programski jezik, 79 Fowler, Martin, 159, 386, 1172 funkcija funkciju lanica, 19 redefinisanje, 24 funkcija za transformisanje kljueva (he funkcija), 675

funkcijski objekat, 585 funkcijski programski jezici,


888

Future, 897

G
Gecov test, za izbegavanje sinhronizacije, 926 Generator, 218, 492, 500, 508, 550, 580, 606, 621, 630,813 opte nam ene, 501 popunjavanje kontejnera (podtipova klase Collection), 500 generiki tipovi anonim ne unutranje klase, 508 argum enti promenljive duine i generike metode, 499 brisanje, 512, 551 Class reference, 441 dokerski argum enti, 534 dokerski argum enti nadtipova, 539 eksplicitna konverzija, 551 eksplicitna konverzija putem generike klase, 553 eksplicitno zadavanje tipova argum enata generikih metoda, 309,499 granice, 514, 531 i kontejneri za bezbedan rad s tipovima, 303 instanceof, naredba, 522, 551 islnstancef ^22 izuzeci, 563 koji se neobino ponavljaju, 556 konkretizacija, 516

kontravarijansa, 539 metode, 496, 631 najjednostavnija definicija klase, 321 neogranien dokerski argum ent, 541 niz generikih objekata, 677 osnovni uvod, 303 oznaka tipa, 522 preklapanje, 553 prim er strukture, 1028 samoogranieni tipovi, 555 testiranje sa @Unit, 873 unutranje klase, 508 generisanje izuzetka, 347 get( ), m etoda klase ArrayList, 303 HashMap, realizacija klase Map, 326 u interfejsu Collection nema m etode get( ), 644 getBeanInfo( ), 1124 getBytes( ), 740 getCanonicalName( ), 438 getChannei( ), 755 getClass( ), 357, 436 g etC o n stru cto r(), 1071 getConstructors( ),464 getenv( ), 337 getenv( ), metoda, 337 getEventSetDescriptors( ), 1126 getlnterfaces( ), 438 getM ethodDescriptors( ), 1126 getMethods( ), 464 getName( ), I 126 getPropert) 1)e.scriptoi's( j,
1126

getPropertyType( ), 1126 getReadMethod( ), 1126 getSelecteValues( ), 1081

Indeks

1183

getSim pleN am e(), 438 getState( ), 1090 getSuperclass(), 438 getW riteM ethod(), 1126 Glass, Robert, 1172 Goetz, Brian, 922,926,1019, 1044 goto, nepostojanje te naredbe u Javi, 108 grafiko korisniko okruenje (GUI), 290, 1045 grafika, 1098 Graphics, klasa, 1093 granice i Class reference, 442 natklasa i Class reference, 444 samoogranieni generiki tipovi, 555 u generikim tipovima, 514, 531 greka obrada pom ou izuzetaka, 345 oporavak, 345 prijavljivanje, 385 prijavljivanje greaka u knjizi, 14 standardni izlazni tok za greke, 350 GridBagLayout, 1058 GridLayout, 1058, 1119 Grindstaff, Chris, 1150 grupa niti, 914 grupa objekata, 997 grupe, regularnog izraza, 417 GUI grafiki korisniki interfejs, 290, 1045 razvojna okruenja za GUI, 1046 GZIPInputStream, 775 GZIPOutputStream , 775

H
Harold, Elliotte Rusty, 1137, 1171 XOM, XML biblioteka, 799 h ash C od e(), 663, 668, 670, 675 recept za pisanje pristojne metode, 679 hash C od e() eq u als(), 653 i strukture podataka s transform isanim kljuevima, 671 na ta obratiti panju prilikom pisanja sopstvene metode, 678 HashMap, 664, 700, 1032, 1068 HashSet, realizacija skupa Set, 322,653, 696 Hashtable, 700, 714 hasN ex t(), m etoda klase Iterator, 316 heksadecimalno, 78 Holub, Allen, 1039 HTML u Swing kom ponentam a, 1101 I IdentityHashM ap, 664, 700 if-else naredba, 84, 99 ikonice i klasa Icon, 1071 IlIegalAccessException, 449 IllegalMonitorStateException, 958 ima, relacija, 21 relacija kompozicije, 197 Imagelcon, 1071 ime pravljenje jedinstvenih iinena paketa, 163 puno, 438

sukobljavanje prilikom kombinovanja interfejsa, 255 sukobi, 165 im e klase, otkrivanje u datoteci klase, 879 imenski prostori, 160 implements, rezervisana re, 243 im port, rezervisana re, 160 indeksiranje, operator [], 145 indeksirano svojstvo, 1137 indexO f(), metoda klase String, 464 indikator, korienje skupa EnumSet umesto, 819 InflaterlnputStream, 775 inicijalizacija lana klase, 181 i nasledivanje, 208 i uitavanje klasa, 208 inicijalizacija instance, 144,277 inicijalizacija konstruktorim a tokom nasleivanja i kompozicije, 189 inicijalizacija nestatinih instanci, 144 inicijalizacija niza, 145 inicijalizacija pomou konstruktora, 115 inijalizatori lanova, 225 klase, 440 lenja, 182 osnovna klasa, 185 polja klase, 136 promenljivih u metodama, 136 redosled inicijalizacije, 139,232 statinih, 209 inicijalizovanje izvedene klase, 185 InputStream, 730

1184

Misliti na Javi

InputStreamReader, 735 instanca inicijalizacija instance, 277 inicijalizacija nestatinih instanci, 144 klase, 16 instanceof, 451 dinamiko ispitivanje tipa objekta m etodom isln stan ce() umesto sa instanceof, 453 i generiki tipovi, 551 rezervisana re, 445 Integer parselnt( ), 1099 omotaka klasa, 148 interfejs i enum , 815 i generiki kod, 484 i nasleivanje, 253 i razdvajanje od realizacije, 20,174, 1060 inicijalizacija polja u interfejsima, 259 interfejs osnovne klase, 218 klase ugnedene unutar, 283 privatni, kao ugneeni interfejsi, 261 rezervisana re interface, 242 sukobljavanje imena prilikom kombinovanja interfejsa, 255 svoenje navie na interfejs, 245 u odnosu na apstraktnu klasu, 253 u odnosu na realizaciju, 196

ugneivanje interfejsa unutar klasa i drugih interfejsa, 260 za objekat, 17 zajedniki interfejs, 239 internacionalizacija, u U/I biblioteci, 735 interrupt( ) paralelno izvravanje, 946 vienitni rad, 912 Introspector, 1124 inenjering bajtkoda, 879 Javassist, 881 isAssignableFrom( ), m etoda klase Class, 454 iscrpljenje memorije, reenje pom ou referenci, 710 isDaemon( ), 903 islnstance(), 453 i generiki tipovi, 522 islnterface( ),438 Iterable i foreach, 336 Iterable, interfejs, 494, 632 i foreach sintaksa, 336 i niz, 337 Iterator, 332 Iterator, klasa, 315, 318, 332 hasNext( ), metoda, 316 next( ), metoda, 316 Iterator, projektni obrazac, 269 izgled i ponaanje, promenljiv, 1103 izuzetak blok try, 348 blok za obradu, 346 blok za obradu izuzetka, 348 uvana oblast, 348 Error, klasa, 365 Exception, klasa, 365

FileNotFoundException, 378 fillInStackTrace( ), 359 finally, 367 generiki kod, 563 generisanje izuzetka, 346, 347 gubljenje izuzetka, mana, 372 hijerarhije klasa, 382 hvatanje bilo kog izuzetka, 356 hvatanje izuzetka, 348 i konstruktori, 376 i konzola, 387 i nasleivanje, 374, 382 i paralelnost, 924 konstruktori, 377 menjanje mesta nastanka izuzetka, 360 neprovereni, 366 NullPointerException, 366 obrada, 33 obrada izuzetaka, 345 ogranienja, 374 ponovno generisanje izuzetka, 359 pravljenje sopstvenih, 349 prekidanje ili nastavljanje, 349 pretvaranje proverenih u neproverene izuzetke, 388 prijavljivanje izuzetaka putem zapisnika, 353 printStackTrace( ), 359 problemi pri projektovanju, 379 pronalaenje slinih izuzetak.i, 382 proveren, 356, 384 RuntimeException, 366 specifikacija, 355, 385 Throwable, klasa, 356

Indeks

1185

tipine upotrebe izuzetaka, 390 try, 368 ulanani izuzeci, 388 ulanavanje, 362 vanredno stanje, 346 zapisivanje, 352 izvedena klasa, 214 izvedeni tipovi, 22 izvorni kod, 12 napom ena o zatiti autorskih prava, 12 izvravanje Java programa, 56 izvravanje program a operativnog sistema iz Jave, 752

J
Jacobsen, Ivar, 1172 JApplet, 1056 meniji, 1085 JAR, 1135 arhiva, 161 jar arhive i promenljiva classpath, 164 usluni program, 779 Java AWT, 1045 bajtkod, 394 i aparati za prikazivanje Interneta na TV-u, 79, 394 Java Web Start, 1105 Javina virtuelna maina (JVM), 434 Javine osnovne klase (JFC/Svving), 1045 iavni seminari o Javi, 9 prevoenje i izvravanje program a, 56 JavaBeans, videti zrna Jave,
1120

javac, 57 javadoc, 58 javap, prevodilac unazad, 393,479, 520 Javassist, 881 Javina standardna biblioteka, i bezbedno vienitno izvravanje, 985 JButton, 1071 Swing, 1051 JCheckBox, 1071,1077 JCheckBoxMenuItem, 1086, 1090 JComboBox, 1080 JComponent, 1073, 1093 IDialog, 1096 meniji, 1085 JDK 1.1 U/I tokovi, 734 JDK, preuzimanje i instaliranje, 56 je, relacija, 235 i svoenje navie, 198 relacija nasleivanja, 197 u odnosu na relacije je-kao, 25 je-kao, relacija, 235 jedinica za prevodenje, 161 jednakost ==, 73 jednakost objekata, 73 jednokratno otkrivanje tipa, 836 jednosm erni (dogaaj), 1130 JFC, Javine osnovne klase (Swing), 1045 JFileChooser, 1099 JFrame, 1056 meniji, 1085 iri', prevodioci ba-kad-treba, 135 JLabel, 1075 JList, 1081 JMenu, 1085,1090

JMenuBar, 1085,1091 JMenuItem, 1071,1085, 1090,1092 JNLP, Java Netvvork Launch Protocol, 1105 jo in ( ), vienitni rad, 912 JOptionPane, 1083 Joy, Bill, 73 JPanel, 1070,1093,1119 JPopupMenu, 1091 JProgressBar, 1103 JRadioButton, 1071,1079 JScrollPane, 1055,1082 JSlider, 1103 JTabbedPane, 1083 JTextArea, 1054 JTextField, 1052, 1073 JTextPane, 1076 IToggleButton, 1070 JUnit, problem i sa, 864 JVM (Javina virtuelna maina), 434

K
kanal, nio, 754 kapacitet, objekata tipa HashMap ili HashSet, 700 kapsuliranje, 174 upotreba refleksije za ponitavanje, 476 kaskadni opisi stilova (CSS), i Adobe Flex, 1144 kasno vezivanje, 27, 211, 214 keySet( ), 700 kidanje veze, putem polimorfizma, 27, 211 klasa, 17 anonim na unutrania klasa, 275, 721, 1053 apstraktna klasa, 239 autori, 20 ita, 175

1186

Misliti na Javi

dijagrami nasleivanja, 199 ekvivalentnost, i instanceof/ isln stan ce(), 459 finalne klase, 206 hijerarhije klasa i obrada izuzetaka, 382 inicijalizacija, 440 inicijalizacija lanova, 181 inicijalizacija i uitavanje klasa, 208 inicijalizacija polja, 136 inicijalizovanje izvedene klase, 185 inicijalizovanje osnovne kJase, 185 izvedena klasa, 214 javna klasa i jedinice za prevoenje, 161 literal klase, 439, 451 metode, 53 nasleivanje od apstraktnih klasa, 240 nasledivanje unutranje klase, 296 osnovna klasa, 172, 183, 214 podaci, 53 podobjekat, 185 povezivanje, 440 primerak, 16 pristup, 175 privatna unutranja klasa, 291 redosled inicijalizacije, 139 rezervisana re class, 22 statine unutranje klase, 282 stil pruvljenja klasa, 174 uitavanje, 209,440 ugnedena klasa (statina unutranja klasa), 282

ugnedivanje unutar interfejsa, 283 unutranja klasa, 266 unutranja klasa i prava pristupa, 268 unutranja klasa i svoenje navie, 271 unutranja klasa i Svving, 1060 unutranja klasa, i redefinisanje, 297 unutranja klasa, i rezervisana re super, 297 unutranja klasa, identifikatori i .class datoteke, 300 unutranja klasa, u metodama i oblastima vaenja, 273 unutranja klasa, ugneivanje unutar proizvoljne oblasti vaenja, 274 upuivanje na objekat spoljne klase iz unutranje klase, 270 viestruko ugnedena, 284 klasa, uitava, 434 klijent, programer, 20 kliza, 1102 konjunkcija nad bitovima, 86 logika (&&), 75 kod izvorni, 12 podela i smetanje, 168 ponovna upotreba, 180 standardi programiranja, 13 stil program iranja, 63 kod bez zakljuavanja, u paralelnom izvravanju, 926

kod upravljan tabelom, 823 i anonim ne unutranje klase, 684 kolekcija, 29, 306, 332 klase, 302 popunjavanje pomou Generatora, 500 spisak metoda za, 643 uslune metode, 701 kolekcije, parametar, 565, 589 kom anda akcije, 1090 kombinovane liste, 1080 komentari i ugraena dokumentacija, 57 kompatibilnost vertikalna, 516 migracijska, 516 komplet alatki za apstraktne prozore (Abstract WindowToolkit,AWT), 1045 komponenta, i zrna Jave,
1121

kompozicija, 21, 180 i dinamika promena ponaanja, 234 i projektovanje, 233 kombinovanje kompozicije i nasleivanja, 189 u odnosu na nasleivanje, 196,200, 660,714 komprimovanje, biblioteka za, 775 komunicirajui sekvencijalni procesi (CSP), 1042 konferencija, za razvoj softvera, 9 konkretizacija i generiki tipovi, 516 konsalting i obuka koju prua MindVievv, Inc., 1166

Indeks

1187

konstanta grupe konstantnih vrednosti, 258 posredne konstante i String, 393 pri prevoenju, 200 uklapanje konstante, 201 konstruktor, 115 argum enti, 116 bez argum enata, 116, 124 Constructor, klasa za refleksiju, 462 i anonim ne unutranje klase, 275 i finally, 377 i obrada izuzetaka, 376-377 i paralelno izvravanje, 907 i polim orfizam, 224 i preklapanje, 117 inicijalizacija instance, 277 inicijalizacija tokoni nasleivanja i kompozicije, 189 konstruktor osnovne klase, 225 nivo pristupa automatski napravljenog podrazum evanog konstruktora, 464 podrazum evani, 124 ponaanje polim orfnih metoda unutar konstruktora, 230 povratna vrednost, 1 17 pozivanje iz drugih konstruktora, 127 pozivanje konstruktora osnovne klase sa argum entim a, 186 redosled poziva konstruktora uz nasleivanje, 224

statika odredba konstruktora, 143 statine metode, 142 kontejner, 29 ispitivanje performansi, 684 klasa, 302 poreenje s nizom, 593 kontejneri bez zakljuavanja, 1027 koji brzo otkazuju, 708 osnovno ponaanje, 309 za bezbean rad s tipovima i generiki tipovi, 303 kontravarijansa i generiki kod, 539 kontrola pristupa, 20, 178 konverzija automatska, 181 proirujua, 87 suavajua, 87 konverzija tipova, 28 asSubc!ass( ), 445 i generiki tipovi, 551 i prosti tipovi, 98 operatori, 87 putem generike klase, 553 konverzija vremenskih jedinica, 990 konzola slanje izuzetaka na, 387 Svving alatka za prikazivanje u net.mindview.util. SwingKonzola, 1050 kopiranje niza, 617 korisniki interfejs grafiki korisniki interfeis (GUI), 290, 1045 koje brzo reaguje, realizacija pomou niti, 913

kostur upravljanja i unutranje klase, 290 kovarijantni, 441 nizovi, 535 povratni tipovi, 232, 559 tipovi argumenata, 559 kritian odeljak i sinhronizovani blok, 933 kvantifikator pohlepno, 413 posesivno, 413 regularnog izraza, 413 rezervisano, 413

L
lak objekat, 315 trajnost, 781 latentni tipovi, 572, 582 lani objekat, 476 leksikografsko ureivanje, 325 u odnosu na abecedno ureivanje, 623 length lan niza, 147 za nizove, 595 lenja inicijalizacija, 182 lepak, rasporeivaa BoxLayout, 1059 LIFO (poslednji koji ue, prvi izlazi), 320 LineNumberlnputStream, 733 LineNumberReader, 736 linija napredovanja, 1102 LinkedBlockingQueue, 971 LinkedHashMap, 664, 667, 700 I.inkedHashSet, realizacija skupa Set, 323, 653, 696-697

1188

Misliti na Javi

LinkedList, ulanana lista, 311,319, 329,649 List, klasa, 302, 306, 311, 649,1081 poreenje performansi, 688 ureivanje i pretraivanje, 705 lista grafike liste, 1081 padajua lista, 1080 Lister, Timothy, 1173 Listlterator, 649 literal double, 78 float, 78 klase, 439,451 long, 78 vrednosti, 77 little endian, 763 logaritmi, prirodni, 79 logika konjunkcija, 86 disjunkcija, 86 operator i nepotpuno izraunavanje, 76 operatori, 75 lokalna unutranja klasa, 274 promenljiva, 50 long i vienitni rad, 926 marker (L) literala, 78 LRU, algoritam najdavnijeg korienja, 667 Ivrednost, 67

M
main( ), 184 manitest datoteka, za JAR datoteke, 779, 1135 manje ili jednako (<=), 73 manje od (<), 73

Map, klasa, 302, 306, 326 EnumM ap, 821 iscrpno istraivanje, 661 poreenje performansi, 698 Map.Entry, 673 MappedByteBuffer, 769 m a rk (), 737 m arker anotacija, 847 maine stanja i nabrojani tipovi, 830 matcher( ), m etoda za pronalaenje regularnog izraza, 414 matches( ), m etoda klase String, 409 matematiki operatori, 69, 773 M ath.random ( ), metoda opseg rezultata, 695 M ath.random ( ), metoda za generisanje sluajnih brojeva, 326 mehanizam za raspodelu procesorskog vremena nitim a, 891 meni JDialog, JApplet, JFrame, 1085 JPopupM enu, 1091 metaanotacije, 848 Metapodaci, 845 Method, 1126 klasa za refleksiju, 462 M ethodDescriptor, 1126 metoda alatka za pronalaenje, 1061 dodavanje metoda program u, 179 finalna, 204,215,232 generika, 496 inicijalizacijapromenljivih u metodam a, 136

pojava pseudonim a pri pozivanju metoda, 68 polim orfni pozivi metoda,


211

ponaanje polim orfnih m etoda unutar konstruktora, 230 preklapanje, 117 prim ena m etode na sekvencu, 577 privatna, 232 razlikovanje preklopljenih metoda, 119 redefinisanje privatnih,
221

rekurzivna, 398 statina, 129, 215 ugraivanje poziva metoda direktno u kod, 204 unutranje klase u m etodam a i oblastima vaenja, 273 vezivanje poziva metoda, 214 zatiene metode, 198 Meyer, Jeremy, 845, 879, 1105 Meyers, Scott, 20 Microsoftov Visual BASIC,
1120

migracijska kompatibilnost, 516 m ikroporeenje perform ansi, 694 miksin, 565 mkdirs( ), 730 mnemonici (preice s tastatiiff), ! 090 mnoenje, 69 modulo, 69 m ogunost ponovnog korienja, 21

Indeks

1189

monitor, za paralelno izvravanje, 922 Mono, 39 mrea objekata, 782 mreni ulaz/izlaz, 753 MXML, ulazni format Adobe Flexa, 1138 mxmlc, prevodilac za Adobe Flex, 1140

N
nabrojani tipovi, 155 dodavanje metoda, 807 grupe konstantnih vrednosti u jezicima C i C++, 258 i interfejsi, 815 i maine stanja, 830 i naredba svvitch, 809 i nasleivanje, 813 i nasumian izbor, 814 i projektni obrazac Chain of Responsibility, 826 i uvoz statinih lanova, 806 i viekratno otkrivanje tipa, 836 metode koje se menjaju u zavisnosti od konstante (nabrojanog tipa), 823, 840 rezervisana re enum, 155, 805 values( ), 805, 810 nain pisanja koda, 13 nad bitovima disjunkcija, 86 iskljuiva disjunkcija XOR ( a ). 80 konjunkcija, 86 negacija ~, 80 operator disjunkcije (I), 79

operator konjunkcije (8c), 79 operatori, 79 nadtipovi, dokerski argum enti, 539 najdavnije korieno (algoritam, LRU), 667 napom ena o zatiti autorskih prava, izvorni kod, 12 nasleivanje, 22,172,180, 183,211 dijagram, 28 dijagrami nasleivanja klasa, 199 i finalize( ), 226 i finalnost, 206 i generiki kod, 484 i nabrojani tipovi, 813 i rezervisana re synchronized, 1135 inicijalizacija i nasleivanje, 208 kombinovanje kompozicije i nasleivanja, 189 od apstraktnih klasa, 240 poreenjc potpunog nasleivanja i proirivanja, 235 preklapanje m etoda u poreenju redefinisanjem, 194 projektovanje pomou nasleivanja, 233 proirivanje interfejsa pom ou nasleivanja, 253 proirivanje klase tokom, 23 specijalizacija, 197 u odnosu na kompoziciju, 196, 200, 660,714

unutranjih klasa, 296 viestruko nasleivanje u jezicima C++ i Java, 251 nasleivanje viestruke realizacije, 287 nastavljanje, prekidanje ili nastavljanje, obrada izuzetaka, 349 nasum ian izbor i nabrojani tipovi, 814 natklasa, 185 ogranienje, 444 negacija, logika (!), 75 neobino ponavljanje generiki tipovi, 556 ablonski obrazac u jeziku C++, 556 neogranien dokerski argum ent u generikom kodu, 541 nepodrane metode, u Java kontejnerim a, 646 nepotpuno izraunavanje i logiki operatori, 76 nepravilan niz, 600 neprekidnost, u paralelnom izvravanju, 918 nepromenljivost, 470 nepromenljivost, kako kolekciju ili m apu uiniti nepromenljivom, 706 neprovereni izuzeci, 366 pretvaranje proverenih u, 388 net.mindview.util.SwingKonzola, 1050 Neville, Sean, 1138 new, operator, 129 i niz prostih tipova, 147 newlnstance( ), 1071 refleksija, 438

1 190

Misliti na Javi

next( ),metoda klase Iterator, 316 nio, 753 bafer, 754 i prekid, 950 kanal, 754 perform anse, 770 nit bezbednost, Javina standardna biblioteka, 985 grupa, 914 in te rru p t(), 946 isD aem o n (), 903 lokalno skladite niti, 940 mehanizam za raspodelu procesorskog vremena, 891 notifyAIl( ), 957 prioritet, 899 resu m e () i uzajamna blokada, 946 stanja, 945 s to p ( ) i uzajamna blokada, 946 su sp e n d () i uzajamna blokada, 946 u odnosu na zadatak, terminologija, 911 wait( ), 957 niz asojativan niz, 306 generikih objekata, 677 inicijalizacija, 145 kao prvorazredni objekti, 595 kopiranje niza, 617 kovarijansa, 535 length, lan, 147, 595 nepravilan, 600 nije iterabilan, 337 objekata, 595 poreenje elemenata, 619 poreenje nizova, 618

poreenje s kontejnerom, 593 prostih tipova, 595 provera granica, 147 sintaksa dinamike agregatne inicijalizacije, 597 viedimenzionalni, 599 vraanje, 597 N orth, BorderLayout, 1056 notifyA ll(), 957 notifyListeners(), 1134 nove U/I klase, 753 n-torka, 487, 502, 509 null, 46 Null iterator, projektni obrazac, 469 Null objekat, projektni obrazac, 469 NullPointerException, 366

o
obavezno proveravanje statikih tipova, 384 O bjectO utputStream , 781 objekat, 16 brava, za paralelno izvravanje, 922 lan, 21 dodela objekata kopiranjem referenci, 67 equals( ), 73 finalni, 201 getClass( ), 436 hashCode( ), metoda korenske klase Object, 663 interfejs za, 17 jenakost, 73 jednakost u odnosu na jednakost referenci, 73

mrea objekata, 782 nizovi su prvorazredni objekti, 595 objektno orijentisano program iranje, 432 pojava pseudonim a, 68 postupak pravljenja, 142 pravljenje, 116 serijalizovanje, 780 tipa Class, 434, 795, 922 w a it() i notifyAll( ), 958 objekat za prenos podataka, 487, 632 objekat za prenos podataka (idiom Prenosilac), 684 oblast vaenja ugneivanje unutranje klase unutar proizvoljne oblasti vaenja, 274 unutranje klase u m etodam a i oblastima vaenja, 273 oblik primer, 22, 215 primer, i prepoznavanje tipa u vreme izvravanja, 432 odeljak, kritian oeljak i sinhronizovani blok, 933 oduzimanje, 69 ograniena svojstva, 1137 okno s jezicima, 1083 okruenja koja brzo reaguju, 913 oktalno, 78 okviri za poruke, u Swingu, 1083 OOP osnovna obeleja, 16 osnovni koncepti, 15 protokol, 243

Indeks

1191

Simula-67, programski jezik, 17 zamenljivost, 17 opcione metode, u Java kontejnerima, 646 OpenLaszlo, alternativa za Flex, 1138 operacija, atomska, 926 operativni sistem, izvravanje programa iz Jave, 752 operator oznaenog pom eranja udesno ( ) , 80 operator pomeranja u le v o ( ) , 80 operator prvog komplementa, 80 operator umanjenja, 72 operator uslovljavanja, 84 operator uveanja, 72 operatori, 66 +, za String, 393 binarni, 80 este greke, 86 konverzija u tip String operatorom +, 67, 85 logiki, 75 logiki operatori i nepotpuno izraunavanje, 76 nad bitovima, 79 operator ineksiranja [j, 145 pomeranja, 80 poreenja, 73 preklapanje, 85 preklapanje + i += za String, 184 preklapanje operatora z.a String, 393 prioriteti, 66 prvog komplementa, 80 ternarni, 84

unarni, 71, 80 za eksplicitnu konverziju tipova, 87 zarez kao operator, 103 ordinal( ), za nabrojane tipove, 806 OSIzvrenje, 752 osnova, 8,16, 78 osnovna klasa, 172, 183,214 apstraktna osnovna klasa, 239 inicijalizacija, 185 interfejs, 218 konstruktor, 225 osnovni koncepti objektno orijentisanog program iranja (OOP), 15 osnovni tipovi, 22 otkrivanje tipa dvokratno, 836 viekratno nabrojani tipovi, 836 O utputStream, 730, 732 OutputStreamVVriter, 735 oznaen break, 108 continue, 108 oznaka, 108

P
padajua lista, 1080 paintCom ponent( ), 1093, 1098 paket, 160 i struktura direktorijuma, 168 imena, pisanje velikim slovima, 52 paketni i prijateljski pristup, 169 paketni pristup i zatieni lanovi, 197

podrazumevani, 161, 170 pravljenje jedinstvenih im ena paketa, 163 pakovanje, 326, 495 i generiki tipovi, 497, 549 paraleino izvravanje aktivni objekti, 918,1039 ArrayBlockingQueue, 971 BlockingQueue, 971, 988 Brajanovo pravilo sinhronizacije, 922 cepanje rei, 926 Condition, klasa, 968 CountDownLatch, 983 CyclicBarrier, 986 DelayQueue, 988 eksplicitne brave, 923 Exchanger, 1001 Executor (izvrilac), 893 gaenje zadataka, 942 Gecov test za izbegavanje sinhronizacije, 926 i izuzeci, 924 i kontejneri, 708 i Swing, 1110 interfejs Callable, 896 kod bez zakljuavanja, 926 konstruktori, 907 LinkedBlockingQueue, 971 lokalno skladite niti, 940 neprekidnost, 918 nit u odnosu na zadatak, terminologija, 911 operacije na prostim tipovima long i double nisu neprekidne, 926 optimizacija performansi, 1018 prioritet, 899 PriorityBlockingQueue, 991 proizvoa potroa, 965

1192

Misliti na Javi

proputeni signali, 961 ReadWriteLock, 1036 ScheduledExecutor, 994 semafor, 997 servisne niti, 901 sle ep (), 897 SynchronousQueue, 1008 takmienje, za brave, 1019 ulazno/izlazne operacije izmedu zadataka, realizovane pom ou cevi, 976 UncaughtExceptionHandler, 916 uslov za trku, 919 parametar kolekcije, 565,589 parametrizovani tipovi, 484 perform anse i finalnost, 207 nio, 770 optimizacija, za paralelno izvravanje, 1018 PhantomReference, 709 PipelnputStream , 731 PipedOutputStream, 731-732 PipedReader, 735, 976 PipedWriter, 735, 976 pisanje imenaa paketa velikim slovima, 52 pisanje imena slinih grbama kamile, 63 Plauger, P.J., 1172 poetni kapacitet, objekata tipa HashMap ili HashSet, 700 podaci finalni, 200 inicijalizacija statinih,
140

podela i smetanje koda, 168 posrednik podobjekat, 185,196 i klasa java.lang.ref.Refepodrazum evani rence, 648 konstruktor, 124 za nepromenljive metode dobija isti nivo pristupa klase Collections, 648, kao klasa, 464 710 pravljenje, 186 potpis, metode, 50 podrazum evani paket, 161, povezano svojstvo, 1137 170 povezivanje, klase, 440 podupirai, rasporedivaa povratni poziv, 720, 1052 BoxLayout, 1059 i unutranje klase, 288 pohlepni kvantifikatori, 413 prazna finalna polja, 203 pokaziva, nem a ga u Javi, pranjenje bafera izlaznih 288 datoteka, 741 polim orfizam, 26, 211, 238, prebacivanje, konteksta 432,482 u paralelnom i konstruktori, 224 izvravanju, 887 i viekratno otkrivanje preice, s tastature, 1090 tipa, 836 preduslovi za razumevanje ponaanje polim orfnih knjige, 15 m etoda unutar Preferences, API, 802 konstruktora, 230 prefiksno umanjenje, 72 poloaj, apsolutan, prilikom prefiksno uveanje, 72 rasporeivanja Svving prekidanje ili nastavljanje, kom ponenata, 1059 obrada izuzetaka, 349 polja, inicijalizacija u preklapanje interfejsima, 259 generikih metoda, 553 polje za potvrdu, 1077 i konstruktori, 117 pom eranje sadraja izostanak sakrivanja u Swingu, 1055 imena tokom ponovna podela na proste nasleivanja, 194 faktore, 159 metoda, 117 ponovna upotreba operatora + i += za String, koda, 180 184,393 ponovno generisanje povratnih vrednosti, 123 izuzetka, 359 razlikovanje preklopljenih ponovno transformisanje metoda, 119 kljueva (heiranje), u odnosu na redefinisanje, 701 194
p o r e d e n j e n i z o v a , 61 S p r e k o r a e n i e , i pro.sti t i p o v i, 98

prosti tipovi podataka i kako se upotrebljavaju sa operatorim a, 89

poreenje perform ansi, 1019 poruka, slanje, 17 posesivni kvantifikatori, 4 1 3

prenosivost u jezicima C, C ++ i Java, 89

Indeks

1193

prepoznavanje tipa u vreme izvravanja (RTTI), 237 ClassCastException, 445 Constructor, klasa za refleksiju, 462 Fiel, klasa, 462 getConstructor( ), 1071 instanceof, rezervisana re, 445 islnstance( ), 453 M ethod, klasa, 462 newlnstance( ), 1071 objekat tipa Class, 434, 1071 prim er obiika, 432 raziika izmeu refleksije i, 462 refleksija, 461 svoenje nanie koje uva tip, 445 zloupotreba, 482 pretraivanje niza, 624 ureivanje i pretraivanje Listi, 705 preusmeravanje standardnog ulaza/izlaza, 751 prevodilac unazad, javap, 393, 479, 520 prevoenje Java programa, 56 prijavljivanje greaka u knjizi, 14 prijem nik adapteri, 1065 i dogaaji, 1060 interfejsi, 1064 prim ena m etode na sekvcncu, 377 prim ordijalni uitava klasa, 434 printff ), 400 printStackTrace( ), 357, 359

PrintStream, 734 PrintWriter, 736, 740-742 prigodni konstruktor u Javi SE5, 746 prioritet, paralelno izvravanje, 899 PriorityBlockingQueue, za paralelno izvravanje, 991 PriorityQueue, 330, 657 PriorityQueue, prioritetni red za ekanje, 330, 657 prirodni logaritmi, 79 priruni saveti, 1073 pristup k'asi, 175 kontrola, 159, 178 krenje kontrole pristupa pomou refleksije, 476 paketni i prijateljski pristup, 169 specifikatori, 20, 159, 168 unutar direktorijuma, preko podrazumevanog paketa, 171 unutranja klasa i prava pristupa, 268 private, rezervisana re, 20, 159, 168, 171, 197, 922 iluzija preklapanja privatnih metoda, 205 interfejsi, kada su ugneeni, 261 metode, 232 redefinisanje metoda, 221 unutranje klase, 291 proces, paralelan, 887 ProcessBuilder, 732 ProcessFiles, 878 produavanje uz ouvanje znaka, 80 uz dodavanje nula, 80

program
builder, 1121 kostur, 290 program za ureivanje teksta, pravljenje pom ou Swing klase JTextPane, 1076 program er klijent, 20 programer, klijent u odnosu na autora biblioteke, 159 programiranje Extreme Programm ing (XP), 1171 m ultistandarno, 16 objektno orijentisano, 432 osnovni koncepti OOP-a, 15 programi voeni dogaajima, 1052 proizvoa potroa, u paralelnom izvravanju, 965 projektni obrazac Adapter, 250, 257, 495, 582,585,631 adapterska metoda, 338 Chain of Responsibility, 826 Com mand, 295, 473, 822, 893 Decorator, 568 Fa^ade, 452 Factory Method, 262 Flyweight, 635,1043 Iterator, 269, 315 Null iterator, 469 Null objekat, 469 objekat za prenos podataka (idiom Dostavlja poruka), 487, 632, 684 Proxy, 465

1 194

Misliti na Javi

Singleton, 177 Strategy, 247, 256, 585, 606,619, 621,720, 726, 826, 990 Template Method, 290, 449, 684, 772, 937, 1025,1030 Visitor, 861 projektovanje, 235 dodavanje metoda projektu, 179 i kompozicija, 233 i nasleivanje, 233 projekat biblioteke, 159 promenljiv izgled i ponaanje, 1103 promenljive definisanje, 102 inicijalizacija u metodama, 136 liste promenljivih param etara (nepoznat broj i tip argum enata), 150 lokalne, 50 promene, vektor, 292 PropertyChangeEvent, 1137 PropertyDescriptor, 1126 ProptertyVetoException, 1137 proputeni signali, u paralelnom izvravanju, 961 srosti tipovi finalni, 201 finalni statini prosti tipovi, 202 inicijalizacija polja klase, 136 poreenje, 73 tipovi, 44 tipovi podataka, i kako se upotrebljavaju sa operatorim a, 89

prostor imenski prostori, 160 prostor problema, 15 prostor reenja, 15 proiriv program, 218 proirivanje klase tokom nasleivanja, 23 proirivanje, u odnosu na potpuno nasleivanje, 235 proirujua konverzija, 87 protected, rezervisana re, 20, 159,168, 172, 197 i paketni pristup, 197 takoe daje paketni pristup, 174 protokol, 243 provera granica niza, 147 provereni izuzeci, 356, 384 pretvaranje u neproverene, 388 Proxy, projektni obrazac, 465 public, 159, 168-169 i rezervisana re interface, 243 klasa i jedinice za prevoenje, 161 public, rezervisana re, 20 puno ime, 438 PushbacklnputStream , 733 PushbackReader, 736 Python, 1 ,3,6, 36,41,572, 626,888, 1173

R
RAD (okruenje za brzo razvijanje aplikacija), 461 radio-dugme, 1078 random ( ), metoda za generisanje sluajnih brojeva, 326

RandomAccess, interfejs za kontejnere, 344 RandomAccessFile, 737, 744,755 rano vezivanje, 27,214 raspored, kontrolisanje pom ou rasporedivaa, 1056 rastue angaovanje, teorija, 915 razdvajanje interfejsa i realizacije, 20,174, 1060 razliito (!=), 73 read( ), 730 nio, 755 readDouble( ), 743 Reader, 730, 734-735 readExternal( ), 785 readLine( ), 379, 736, 741, 750 readObject( ), 781 dodavanje metode interfejsu Serializable, 791 ReadWriteLock, 1036 realizacija, 18 i interfejs, 196, 243 i razdvajanje od interfejsa, 20, 174, 1060 sakrivanje, 159, 174,271 renik, 306 red za ekanje, 302,319,329, 657 performanse, 688 sinhronizovan, u paralelnom izvravanju, 971 redefinisanje funkcije, 24 i uiuitrasnjc klase, 297 privatne metode, 221 u odnosu na preklapanje, 194

Indeks

1195

redosled inicijalizacije, 139,208, 232 pozivanja konstruktora s nasleivanjem, 224 ReentrantLock, 925, 953 reference dodela objekata kopiranjem referenci, 67 jednakost referenci u odnosu na jednakost objekata, 73 null, 46 otkrivanje tanog tipa na koji upuuje referenca na osnovnu klasu, 434 Reference, klasa paketa java.lang.ref, 709 referenciranje unapred, 138 refleksija, 461, 1061, 1123 i neobavezno proveravanje tipova, 387 i zrna Jave, 1121 latentni tipovi i generiki kod, 576 ponitavanje kapsuliranja pom ou, 476 primer, 1070 procesor anotacija, 848, 854 razlika izm edu RTTI i refleksije, 462 regex, paket, 411 registrovane proizvodne metode, varijanta projektnog obrasca Factorv Method, 456 regularni izrazi, 408 rekurzija, nenam erna, koju prouzrokuje metoda toString( ), 397

removeActionListener(), 1128,1134 removeXXXListener(), 1060 renameTo( ), 730 reset( ), 737 resu m e () i uzajamna blokada, 946 re w in d (), 759 rezervisani kvantifikatori, 413 RoShamBo, 836 Rumbaugh, James, 1172 RuntimeException, 366, 388

s
sabiranje, 69 sakrivanje realizacije, 174 sakupljanje smea, 129, 131 dostini objekti, 709 i ienje, 191 kako radi skuplja, 133 redosled ienja objekata, 194 samoogranieni tipovi u generikom kodu, 555 savrena funkcija za transformisanje kljueva, 675 Schedu!edExecutor, za paralelno izvravanje, 994 seek( ), 737, 744 sekvenca, prim ena metode na sekvencu, 577 semafor, 997 seminari javni o Javi, 9 obuka koju prua MindVievv, Inc., 1166 SequenceInputStream, 731, 737

Serializable, 781, 785, 789, 798, 1130 readObject( ), 791 w riteO bject(), 791 serijalizovanje defaultReadO bject(), 792 defaultW riteO bject(), 792 i rezervisana re transient, 789 i skladitenje objekata, 793 upravljanje postupkom serijalizovanja, 785 zadavanje verzije, 793 servisne niti, 901 Set, 302, 306, 322,652 matematiki odnosi, 504 poreenje performansi, 696 setActionCommand( ), 1090 setBorder( ), 1075 setErr(PrintStream ), 751 setIcon( ), 1073 setln(InputStream ), 751 setLayout( ), 1056 setMnemonic( ), 1090 setO ut(PrintStream ), 751 setToolTipText( ), 1073 shuffle( ), 706 signali, proputeni, u paralelnom izvravanju, 961 Simula-67, programski jezik, 17 simulacija alterskog slubenika, 1003 SingleThreadExecutor, 895 singularni projektni obrazac, 177 sinusoida, 1092 sirov tip, 513 siz e(), metoda klase ArrayList, 303

1 196

Misliti na Javi

sizeof(), operator za odreivanje veliine koji Java nema, 89 skok, bezuslovni, 106 skupna inicijalizacija nizova, 146 slanje poruke, 17 sleep (), u paralelnom izvravanju, 897 Smalltalk, 16 SocketChannel, 773 SoftReference, 709 softver, konferencija za razvoj, 9 SortedMap, 666 SortedSet, 656 South, BorderLayout, 1056 specifikacija, specifikacija izuzetaka, 355, 385 specifikator pristupa, 20, 159, 168 specijalizacija, 197 sp lit(), metoda klase String, 247, 409 sporedan efekat, 66, 72, 123 sprintf( ), 406 SQL kod generisan putem anotacija, 850 stanje okonanja i finalize( ), 131 standardni ulazni tok, itanje, 749 stateChanged( ), 1095 static, 243 finalni statini prosti tipovi, 202 i finalnost, 201 inicijalizacija, 209,436 inicijalizacija statinih podataka, 140 metoda, 129, 215 obavezno proveravanje tipova, 384 provera tipova, 483

rezervisana re, 53, 129 sinhronizovane statike metode, 922 statika odredba konstruktora, 143 statiki blok, 143 u odnosu na dinamiku proveru tipova, 647 unutranje klase, 282 uvoz i nabrojani tipovi, 806 stek, 319,320, 714 generiki s kojeg se prvo uzima ono to je na njega poslednje stavljeno, 490 stil stil program iranja, 63 pravljenja klasa, 174 STL, C h t, 718 stop( ) i uzajamna blokada, 946 Strategy, projektni obrazac, 247, 256, 585,606,619, 621,720, 726, 826, 990 StreamTokenizer, 736 String CASE_INSENSITIVE_ ORDER Com parator, 705 format( ), 406 indexOf( ), 464 konverzija pom ou operatora +, 67, 85 leksikografsko u odnosu na abecedno uredivanje, 623 m etoda toString( ), 181 metode, 398 nadovezivanje operatorom + = ,85 nepromenljivost, 392 podrka za regularne izraze u klasi, 409

preklapanje operatora + i +=, 184 s p lit(), metoda, 247 ureivanje, CASE_INSENSITIVE _ORDER, 720 form at( ), 406 StringBuffer, 731 StringBufferlnputStream, 731 StringBuilder, poredenje s klasom String i m etodom to S trin g (), 394 StringReader, 735, 739 StringVVriter, 735 Stroustrup, Bjarne, 158 strukturni tipovi, 572 sufiksno, 72 sufiksno umanjenje, 72 sufiksno uveanje, 72 sukob tokom heiranja, 675 ime, 165 super, 186 i unutranje klase, 297 rezervisana re, 185 supstitucija nasledivanje u odnosu na proirivanje, 235 princip, 25 suspend( ) i uzajamna blokada, 946 suavajua konverzija, 87 svite, @Unit u odnosu na lUnit, 874 svodenje nanie, 200, 236 koje uva tip, 445 svoenje navie, 28, 198, 211 i interfejs, 245 i prepoznavanje tipa u vreme izvravanja, 434 i unutranje klase, 271

Indeks

1197

svojstvo, 1121 indeksirano, 1137 nam enska klasa za ureivanje svojstava, 1137 nam enski spisak svojstava, 1137 ograniena svojstva, 1137 povezana svojstva, 1137 SWF, Flashov bajtkod format, 1138 Swing, 1045 i paralelno izvravanje, 1110 kom ponente, korienje HTML-a sa, 1101 model dogaaja, 1059 primeri kom ponenata, 1068 switch i enum , 809 rezervisana re, 112 synchronized, rezervisana re, 921 blok i kritian odeijak, 934 Brajanovo pravilo sinhronizaeije, 922 i m etode wait( ) i notifyAll( ),957 i nasleivanje, 1135 odluivanje koje metode sinhronizovati, 1134 red za ekanje, 971 sinhronizovani kontejneri, 708 statiko, 922 SynchronousQueue, za paralelno izvravanje, 1008 System.arraycopy( ), 617 System.err, 350, 749 System.in, 749 System.out, 749

System.out, omotavanje toka u PrintW riter, 750 systemNodeForPackage(), API Preferences, 803

abloni, C++, 485, 513

tabela baze podataka, generisana SQL kodom putem anotacija, 850 takmienje, za brave, u paralelnom izvravanju, 1019 tastatura navigacija, i Swing, 1046 preice, 1090 Template M ethod, projektni obrazac, 290,449, 684, 772,845,850,937,1025, 1030 Teorija rastueg angaovanja, 915 ternarni operator, 84 testiranje jedinica na osnovu anotacija @Unit, 864 jedinice, 184 tehnike, 284 this, rezervisana re, 125 ThreadFactory, namenska realizacija interfejsa, 902 throw, rezervisana re, 347 Throvvable, natklasa od Exception, 356 Timer, objekat tipa, 964 TimeUnit, 898, 990 up bezbednost tipova u Javi, 86 dinamika bezbednost tipova i kontejneri, 562

ekvivalencija tipa podataka i klase, 17 generiki tipovi i kontejneri za bezbedan rad s tipovima, 303 izveden, 22 latentni tipovi, 572,582 osnovni, 22 otkrivanje tanog tipa na koji upuuje referenca na osnovnu klasu, 434 oznaka tipa u generikom kodu, 522 parametrizovani, 484 prosti, 44 prosti tipovi podataka i kako se koriste sa operatorim a, 89 provera tipova i nizovi, 593 statika provera, 384,483 strukturni tipovi, 572 svoenje nanie koje uva tip, 445 zakljuivanje o tipovima generikih argumenata, 497 To, 646 toArray( ),700 tok, U /1,730 TooManyListenersException, 1130 toString( ), 181 smernice za upotrebu klase StringBuilder, 396 trajnost, 793 laka trajnost, 781 traka napredovanja, 1102 transferFrom( ), 756 transferTo( ),756 transformisanje kljueva (heiranje), 672, 674 i kljuevi za heiranje, 668 savrena funkcija za, 675 spoljno nadovezivanje, 675

1198

Misliti na Javi

transient, rezervisana re, 789 traenje .class datoteka tokom uitavanja, 163 TreeMap, 664, 666, 700 TreeSet, realizacija skupa Set, 323, 653, 655-656, 696 true, 75 try, rezervisana re, 194, 368 blok try u izuzecima, 348 tryLock( ), zakljuavanje datoteke, 773 TYPE polje, za literale prostih klasa, 439

u
u/i
available( ), 740 biblioteka, 719 biblioteka za komprimovanje, 775 blokiranje i metoda available( ), 740 BufferedlnputStream, 733 BufferedOutputStream, 734 BufferedReader, 377, 736, 738 BufferedVVriter, 736, 740 ByteArrayInputStream, 731 ByteArrayOutputStream, 732 cev, 731 cevovodi, 745 CharArrayReader, 735 CharArrayWriter, 735 CheckedlnputStream, 775 CheckedOutputStream, 775 close( ), 738 D atalnput, 737

D atalnputStream, 733, 736, 739 D ataO utput, 737 DataOutputStream , 734, 737 DeflaterOutputStream, 775 direktorijum, pravljenje direktorijuma i putanja, 728 Externalizable, 785 File, klasa, 719, 731,737 File.list(), 719 FileDescriptor, 731 FilelnputReader, 738 FilelnputStream, 731 FilenameFilter, 719 FileOutputStream, 732 FileReader, 377, 735 FileWriter, 735, 740 FilterlnputStream, 731 FilterOutputStream, 732 FilterReader, 736 FilterVVriter, 736 GZIPInputStream, 775 GZIPOutputStream, 775 InflaterlnputStream, 775 InputStream, 730 InputStreamReader, 735 internacionalizacija, 735 izlaz, 730 izmedu zadataka, realizovane pomou cevi, 976 karakteristike datoteka, 728 koje se mogu prekidati, 950 laka trajnost, 781 LineNumberlnputStream,
733

LineNumberReader, 736 listanje direktorijuma, 719

m a rk (), 737 m k d irs(), 730 mreni ulaz/izlaz, 753 nove U/I klase (nio), 753 O bjectOutputStream, 781 O utputStream , 730, 732 OutputStrearhVVriter, 735 PipedlnputStream , 731 PipedOutputStream, 731-732 PipedReader, 735 PipedWriter, 735 preusmeravanje standardnog ulaza/izlaza, 751 primeri jednostavne upotrebe, 737 PrintStream, 734 PrintVVriter, 736, 740-742 PushbacklnputStream, 733 PushbackReader, 736 RandomAccessFile, 737, 744 read( ), 730 readDoublef ), 743 Reader, 730, 734-735 readExternal(), 785 readLine( ), 379, 736, 741, 750 readObject( ), 781 renameTo( ), 730 reset( ), 737 seek( ), 737, 744 SequenceInputStream, 731, 737 Serializable, 785 setErr(PrintStream ), 751 setln(InputStream ), 751 setO ut(PrintStream ), 751 standardnog ulaznog toka, 749 StreamTokenizer, 736 StringBuffer, 731

Indeks

1 199

StringBufferlnputStream, 731 StringReader, 735, 739 String\Vriter, 735 System.err, 749 System.in, 749 System.out, 749 tipine U/I konfiguracije, 737 transient, rezervisana re, 789 ulaz, 730 Unicode, 735 upravljanje postupkom serijalizovanja, 785 w rite (), 730 w riteD ouble(), 743 writeExternal( ), 785 writeObject( ), 781 Writer, 730, 734-735 ZipEntry, 778 ZipInputStream, 775 ZipO utputStream , 775 uitava klasa, 434 uitavanje datoteka .class, 163 inicijalizacija i uitavanje klasa, 208 klase, 209, 440 ugneena klasa (statina unutranja klasa), 282 ugneivanje interfejsa, 260 ugradivanje poziva metoda direktno u kod, 204 uklapanje konstante, 201 ulanani izuzeci, 362, 388 ulazno/izlazneoperacijekoje se mogu prekidati, 950 umanjenje, operator, 72 UMI jezik za modelovanje, 19, 21, 1172 pokazivanje kompozicije,
21

unapreenje tipova, u int, 89, 98 unarni minus (-), 71 operator, 80 operatori, 71 plus (+), 71 UncaughtExceptionHandler, klase Thread, 916 Unicode, 735 Unified Modeling Language (UML, unifikovani jezik za modelovanje), 19,21, 1172 unmodifiableList( ),m etoda klase Collections, 648 U nsupportedO perationException, 647 unutranja klasa, 266 anonim na, 721, 1053 generika, 508 i kod upravljan tabelom, 684 i kosturi upravljanja, 290 i niti, 907 i redefinisanje, 297 i rezervisana re super, 297 i svodenje navie, 271 i Swing, 1060 identifikatori i .class datoteke, 300 lokalna, 274 motivacija, 285 nasleivanje, 296 povratni poziv, 288 prava pristupa, 268 privatna, 291 skrivena referenca objekta okruujue klase, 269 statine unutranje klase, 282

u m etodam a i oblastima vaenja, 273 ugneivanje unutar proizvoljne oblasti vaenja, 274 upuivanje na objekat spoljne klase, 270 zakljuak, 287 upravljanje procesima, 752 uredivanje, 619 abecedno, 325 i pretraivanje listi, 705 leksikografsko, 325 userNodeForPackage( ),API Preferences, 803 uslov za trku, u paralelnom izvravanju, 919 uslovno prevodenje, 168 uslune metode, klase java.util.Collections, 701 uveanje, operator, 72 i paralelno izvravanje, 920 uzajamna blokada uprkos izvravanja, 1043 uzajamna blokada, u paralelnom izvravanju, 978 uzajamno iskljuivanje (mutex), u paralelnom izvravanju, 921 uzorak, regularnog izraza, 412 V values( ), za nabrojane tipove, 805, 810 Varga, Ervin, 5, 951 Vector, 694, 713 veera filozola, prim er uzajamne blokade u paralelnom izvravanju, 978

12 0 0

Misliti na Javi

vee ili jednako (>=), 73 vee od (>), 73 vektor prom ene, 292 veliina, objekata tipa HashM ap ili HashSet, 700 Venners, Bill, 131 vertikalna kom patibilnost, 516 vezivanje dinamiko, 214 dinamiko, kasno, ili vezivanje prilikom izvravanja, 211 kasno, 27, 214 poziva metoda, 214 prilikom izvravanja, 214 rano, 27 vezivna funkcija, 476 Visitor (projektni obrazac) i anotacije, API mirror, 861 Visual BASIC, Microsoftov,
1120

vraanje rezultata i finally, 371 kovarijantni povratni tipovi, 232, 559 povratna vrednost konstruktora, 117 preklapanje povratnih vrednosti, 123 u obliku niza, 597 vraanje vie objekata, 487 vrednost, spreavanje izmena u vreme izvravanja, 200

Y
yPos, 797-798

z
zadavanje verzije, serijalizovanje, 793 zadravanje pokazivaa mia iznad dugmeta bez pritiskanja, 1073 zahtev, u OOP, 17 zajedniki interfejs, 239 zakljuak, i unutranje klase, 288 zakljuavanje, datoteke, 772-773 zakljuivanje, o tipu generikog argumenta, 497 zamenljivost, u OOP, 17 zapisivanje, ugradnja u izuzetke, 352 zarez kao operator, 103 zauzetost ekanjem, u paralelnom izvravanju, 957 ZipEntry, 778 ZipInputStream, 775 ZipOutputStream, 775 zrna Jave alatka za pravljenje aplikacija, 1121 dogadaji, 1121 EventSetDescriptor, 1126 FeatureDescriptor, 1137 getBeanInfo( ), 1124 getEventSetDescriptors( ), 1126 getM ethodDescriptors( ),
I 126

w
w a it(), 957 Waldrop, M. Mitchell, 1173 WeakHashMap, 664, 712 VVeakReference, 709 Web Start, Java, 1105 West, BorderLayout, 1056 while, 100 w indow C losing(), 1096 write( ), 730 nio, 756 writeDouble( ), 743 writeExternal( ), 785 writeObject( ), 781 dodavanje m etode interfejsu Serializable, 791 Writer, 730, 734-735 X XDoclet, 845 XML, 799 XOM, XML biblioteka, 799 XOR uskljuiva disjunkcija), 80 xPos, 797-798

viedimenzionalni nizovi, 599 viekratno otkrivanje tipa i nabrojani tipovi, 836 pom ou m ape EnumMap, 842 vieprogramski rad, 887 viesmerni, 1130 dogaaj, i zrna Jave, 1130 viestruko nasleivanje, u jezicima C ++ i Java, 251 viestruko ugneena klasa, 284 vizuelno programiranje,
11 20

okruenja, 1046 volatile, modifikator, 918, 926, 930

getName( ), 1126 getPropertyD escriptors(), 1126

Indeks

1201

getPropertyType( ), 1126 getReadMethod( ), 1126 getW riteM ethod( ), 1126 i Borlandov Delphi, 1120 i Microsoftov Visual BASIC, 1120 indeksirano svojstvo, 1137 Introspector, 1124 JAR datoteke za arhiviranje, 1135 kom ponenta, 1121

manifest datoteka, 1135 Method, 1126 MethodDescriptor, 1126 namenska klasa Beanlnfo, 1137 namenska klasa za ureivanje svojstava, 1137 namenski spisak svojstva, 1137 ograniena svojstva, 1137

pravilo za imenovanje,
1122

PropertyChangeEvent, 1137 PropertyDescriptor, 1126 ProptertyVetoException, 1137 refleksija, 1121, 1123 Serializable, 1130 svojstva, 1121 vizuelno program iranje, 1120

Das könnte Ihnen auch gefallen