Misliti Na Javi PDF

You might also like

You are on page 1of 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 O lg a M ila n k o
R e d a k to ri Stela Spasi
S n ean a B iseni
T ehniki u re d n ik S an ja Tasi
L e k to r i k o re k to r Vesna u k i
R ealizacija k o ric a N ataa Pavlov
P relom tek sta i o b r a d a slika San ja Tasi
M iiica D ean sk i
N ataa Pavlov

Izdava M ik ro k n jig a , B eo g rad


D ire k to r D ra g a n T anaskoski

ta m p a 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 - 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 ll, In c, a C o m p a n y .

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 1
U vod 8
1: U poznavanje sa objektim a 15
2: Sve je objekat 42
3: O p erato ri 65
4: K ontrolisanje izvravanja 99
5: Inicijalizacija i ienje 115
6: K ontrola p ristupa 159
7: Ponovno korienje klasa 180
8: Polim orfizam 211
9: Interfejsi 239
10: U nutranje klase 266
11: uvanje objekata 302
12: O b rad a greaka pom ou izuzetaka 345
13: Znakovni nizovi 392
14: Podaci o tip u 432
15: G eneriki tipovi 484
16: Nizovi 593
17: D etaljno razm atranje kontejnera 628
18: Javin ulazno-izlazni sistem 719
19: N abrojani tipovi 805
20: A notacije 845
21: Paralelno izvravanje 885
22: Grafika korisnika okruenja 1045
A: D odaci 1166
B: Resursi 1170
Spisak term in a korienih u knjizi 1175
Indeks 1177
Sadraj
Predgovor 1 Paralelni r a d ...................................34
Ja v a i I n t e r n e t ....................................... 34
Java SE5 i S E 6 ...................................2 Sta je W eb?............................................ 34
Java SE6..........................................2 Programiianje s klijentske stra n e .. . . 36
etvrto izdanje.................................3 Programiranje sa serverske strane . . . 40
I z m e n e ..........................................................3 S a e ta k .......................................................41
N apom ena o dizajnu k o r ic a .........4
Z ahvalnice......................................... 5 2: Sve je objekat 42
R a d sa o b je k tim a
U vod 8
p r e k o r e f e r e n c i .................................... 42
P reduslovi......................................... 8 M o r a te d a n a p r a v ite sve o b je k te . 43
Uenje Ja v e .......................................9 G de se nalazi skladite............................43
C ilje v i................................................9 Specijalan sluaj: p rosti tip o v i............44
Pouavanje na osnovu 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
ove k n jig e .......................................10
D okum entacija na W ebu............. 11 o b j e k a t ...................................................... 46
Oblast vaenja......................................46
V ebe................................................11
Oblast vaenja o b je k a ta ..................... 47
Javini tem elji.................................. 11
P ra v lje n je n o v ih tip o v a
lzvorni k o d .....................................12 p o d a t a k a : k l a s a .................................... 48
Nain pisanja korisen u knjizi............13
Polja i m e to d e ..................... ................ 48
G re k e ............................................. 14
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
1: U p o z n a v a n j e s a o b j e k t i m a 15 Lista a rg u m e n a ta .....................................51

Razvoj apstrakcije..........................15 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
O bjekat im a in te rfe js................... 17
Korienje d rugih k o m p o n e n a ta . . . . 52
O bjekat prua u slu g e................... 19 Rezervisana re s t a t i c ............................53
Skrivena realizacija........................20 V a p r v i p r o g r a m n a J a v i ................. 5 4
Ponovno korienje realizacije.. .21 Prevodenje i iz v rav a n je ....................... 56
N asleivanje...................................22 K o m e n ta ri i u g ra e n a
Relaeije !! i IE-KAO.......................25 d o k u m e n t a c i j a ........................................ 5 7
Virtuelizacija objekata preko D okum entacioni k o m e n ta ri................ 58
p o lim o rfiz m a................................ 26 S in ta k s a .....................................................58
H ijerarhija s jedinstvenim U gradeni H T M L .....................................59
P rim eri o z n a k a ....................................... 60
k o r e n o m ......................................... 29
P rim er d o k u m e n ta c ije ......................... 62
K ontejneri.......................................29
S til p r o g r a m i r a n j a ............................... 6 3
Paramctri/uvani (generieki) tipuvi .. 30
S a e t a k ........................................................... 6 3
O brada izuzetaka: postupanje
V e b e .............................................................. 6 3
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 5: I n i c i j a l i z a c i j a i i e n je 115
Korienje operatora u Jav i.........66
P rio rite ti.........................................66 G a r a n t o v a n a in ic ija liz a c ija
Dodela v re d n o s ti..........................67 p o m o u k o n s t r u k t o r a .................. 115
Pojava pseudonima P r e k l a p a n je m e t o d a ........................ 117
pri pozivanju metode.....................68 Razlikovanje preklopljenih m etoda ..1 1 9
Matematiki o p erato ri................. 69 Preklapanje i prosti t i p o v i ................ 120
Unarni operatori minus i plus.........71 Preklapanje p o v ra tn ih v re d n o s ti. . . 123

Automatsko uveanje 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


i um anjenje.....................................72 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
O peratori poreenja......................73
iz k o n s tr u k to r a ..................................... 127
Ispitivanje jednakosti objekata.........73
Z naenje rezervisane rei s t a t i c . . . . 129
Logiki o p e r a to ri..........................75
i e n je : f in a liz a c ija
N ep o tp u n o iz ra u n a v a n je ...................76
i s a k u p l ja n je s m e a .......................... 129
Literali............................................. 77
em u slui m etoda fin a liz e ()? ......... 130
E ksponencijalni z a p i s ............................78
M o rate da istite s a m i......................... 131
O peratori nad bitovim a............... 79
Stanje o k o n a n ja ................................... 131
O peratori p om eranja................... 80 Kako radi sakuplja s m e a ? .............. 133
Ternarni operator uslovljavanja. .84 I n i c i j a l i z a c i j a l a n i c a ....................... 136
O peratori + i += za znakovne Z adavanje in ic ija liz a cije ..................... 137
n iz o v e ............................................. 85 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
este greke prilikom Redosled in ic ija liz a cije ....................... 139

korienja operatora......................86 Inicijaiizacija statinih elem enata .. 140


I:ksplicitna inicijalizacija statinih
Eksplicitna konverzija tipova . . .87
e le m e n a ta ................................................. 143
Odsecanje i zaokruivanje..............88 Inicijaliz.acija nestatin ih instanci .. 144
Unapredenje tipova........................ 89
I n ic ija liz a c ija n i z o v a ........................ 145
Java nema operator
E.iste promenljivih param etara........ 150
za odreivanje veliine................. 89 N a b r o ja n i t i p o v i ............................... 155
Saet pregle o p e ra to ra ............... 89 S a .e ta k .................................................... 158
Saetak............................................. 98
6: K o n tro la p r is tu p a 159
4: K o n tr o lis a n je i z v r a v a n j a 99
P a k e t: b i b l i o t e k a j e d i n i c a .......... 160
Logike v red n o sti.......................... 99 O rg anizacija k o d a ................................. 10 1
Naredba if-e ise .............................. 99 PravI jenje jedinst ven i h
P e tlje ............................................. 100 im en a p a k e ta .......................................... 1^2
Petlja do-vvhile....................................... 101 Lina bihlioteka sa a la tk a m a ............ 166
Petlja f o r ...................................................101 K orienje tivoenja za p ro m e n u
O p e ra to r z a r e z ....................................... 103 po n aan ja p ro g r a m a ............................ 16S
Foreach sin tak sa.......................... 103 U pozorenje pri rad u s pakelim a . . . 168

Rezervisana re r e tu r n ............... 106 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
Rezervisane rei break
p u b lic: intertejs za p r i s t u p ................ 169
i c o n tin u e .....................................107 p riv a te : to ne sm e da se d ir a ! ............ 171
Cuveni s;oto...................................108 p ro te c te d . pristup nasledi\'anjem . . 172
Sadraj v ii

Interfejs i realizacija....................174 Projektovanje pom ou


Pristup k la s a m a ..........................175 n asle iv an ja ................................ 2 3 3
Saetak........................................... 178 P oredenje zam ene i proirivanja . . . 235
Svoenje navie i podaci o tip u
p rilikom iz v ra v a n ja ............................236
7: P o n o v n o k o r i e n j e k la s a 1 80
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 9: Interfejsi 239
Inicijalizacija osn ov ne k la s e ..............185
D e l e g i r a n j e ..........................<.................. 1 8 8
Apstraktne klase i m e to d e .........239
K o m b in o v a n je k o m p o z ic ije
In te rfe jsi....................................... 242
i n a s l e i v a n j a ........................................1 8 9
Potpuno razd v ajan je................. 246
G aran to v an je prav ilno g ienja . . . 191 Viestruko nasleivanje u Javi. .251
Sakrivanje im e n a .................................. 194 Proirivanje interfejsa
I z b o r iz m e u k o m p o z ic ije nasleivanjem .............................. 253
i n a s l e i v a n j a ........................................1 9 6 Sukobljavanje imena prilikom
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 kombinovanja interfejsa................255
S v o e n j e n a v i e ..................................... 1 9 8
Prilagoavanje interfejsu...........256
Z ato svoenje navie ? .....................199 Polja u in te rfe jsim a ....................258
Ponovo o hiran ju izm eu Inicijalizacija polja u interfejsim a . . 259
kom pozicije i n a sle iv a n ja................ 200 Ugneivanje in terfejsa............. 260
R e z e r v i s a n a r e f i n a l .......................2 0 0 Interfejsi i Proizvoai............... 262
Finalni p o d a c i .......................................200 Saetak........................................... 265
Finalne m e t o d e .....................................204
Finalne klase............................................206
Faljivo sa f i n a l .....................................207
10: Unutranje klase 266
Inicijalizaja i uitavanje Pravljenje unutranjih klasa .. .266
klasa................................................208 Veza ka spoljnoj k lasi................. 268
Inicijalizacija i n a sle d iv a n je ..............208 U potreba sintaksi .this i .new . .270
Saetak........................................... 210 Unutranje klase i svoenje
navie..............................................271
8: P o l i m o r f i z a m 211 Unutranje klase u m etodam a
Ponovo o svoenju navie.........211 i oblastim a vaenja......................273
Z an em ariv an je tipa o b jc k ta ..............213 A nonim ne unutranje klase . . .275
Z ako ljica.....................................214 Ponovo o p ro izvodnim m e to d a m a . 279
Vezivanje poziva m e to d a .....................214 Ugnedene klase.......................... 2 8 2
D obijanje p ravilnog ponaanja . . . . 215 Klase u n u ta r in te rf e js a ....................... 283
P r o ir iv o s l..............................................218 P ristu p an je spoljanjosti iz viestruko
( 'ireka: ,,redel l n isan je ugneenih k la s a .................................. 284
priv atn ih m e to d a .................................. 221 Zato unutranje k lase?............. 285
Cireka: polja i statine m e to d a ......... 222 Zakljuci i p o v ra tn i p o z iv i................ 287
K onstruktori i p olim orfizam .. .224 U nu tran je klase i kosturi
Redosled po/.iva k o n s tr u k to r a ......... 224 u p ra v lja n ja .............................................. 290
N asledivanje i ienje......................... 226 Nasleivanje unutranjih
Ponaanje po lim o rfn ih m etoda klasa................................................296
u n u ta r k o n s tr u k to r a ............................230
Da li unutranja klasa moe
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
da se redefinie?.......................... 297
v iii Misliti na Javi

Lokalne unutranje klase...........299 i e n j e o d r e d b o m f i n a l l y . . . .3 6 7


Identifikatori unutranjih klasa.300 em u slui o d red b a finally?..............368
Saetak...........................................301 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
11: u v a n je o b je k a ta 302
M ana: izgubljeni iz u z e ta k ...................372

Generike klase i kontejneri O g r a n i e n j a i z u z e t a k a .................... 3 7 4

za bezbedan rad s tipovim a. .. .303 K o n s t r u k t o r i ........................................... 3 7 7

Osnovni p o jm o v i........................306 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

Dodavanje grupa elemenata .. .307 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
Ispisivanje sadraja kontejnera. .309
Perspektive.............................................. 386
L iste............................................... 311 Prosleivanje izuzetaka na k o n z o lu . .387
I t e r a t o r i .......................................315 P retvaranje pro v eren ih izuzetaka
Listlterator.................................318 u n e p ro v e re n e ....................................... 388
Ulanana lis ta .............................. 319 U p u t s t v a z a i z u z e t k e ..........................3 9 0
Pravljenje steka od ulanane S a e t a k ......................................................... 3 9 1
liste................................................. 320
Funkcionalnost s k u p a ............... 322 13: Znakovni nizovi 392
Funkcionalnost m ape................. 326
Nepromenljivi znakovni nizovi .392
Pravljenje reda za ekanje
Poreenje preklapanja
(od ulanane lis te ) ......................329
Prioritetni red ekanja
operatora + i StringB uildera . .393
(P rio rity Q u e u e )............................... 330 N enam erna rekurzija................. 397
Poreenje kolekcija i Iteratora .332 Operacije sa znakovnim
Foreach i iteratori........................336 n izov im a....................................... 398
Adapterska metoda.......................338 Formatiranje izlaza......................400
Saetak...........................................341 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
12: O b r a d a g re a k a p o m o u
Specifikatori f o r m a t a ......................... 402
iz u z e ta k a 345 Konverzije klase F o r m a t t e r ..............403
S tr in g .f o rm a t() ..................................... 406
K o n c e p t i .............................................. 34 5
R e g u l a r n i i z r a z i ..................................... 4 0 8
O s n o v n i i z u z e c i ............................... 3 4 6 O s n o v e .....................................................408
Argumenti izuzetku........................... 347 Pravljenje regiilarnih i / r a z a ..............411
H v a t a n j e i z u z e t k a ............................ 3 4 8 K v antilikatori..........................................413
Blok trv ................................................ 34S C h a rS e q u e n c e ....................................... 414
Blokovi za obradu izuzetaka...............348 Klase P a tte rn i M a tc h e r .....................414
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 M etoda s p l i t ( ) ....................................... 422
Izuzeci i zapisivanje........................... 352 O p eracije z a m e n e ................................ 422
S p e c if ik a c ija i z u z e t a k a .................. 3 5 5 M etoda r e s e t( ) ....................................... 424
R egularni i/.ra/.i i Javin
H v a t a n j e b ilo k o g iz u z e tk a . . . .3 5 6
ula/.no i/Iazni s iste m ............................425
Poloaj nastanka izuzetka na steku
izvravanja..........................................358
Leksiko analiziranje ulaza . . . .427
C ran inici k la se S c a n n e r .................. 42l>
Ponovno generisanje izu z e tk a ........ 359
l.eksika anali/a p om ou
Ulanavanje izuzetaka....................... 362
reg u larnih i / r a / a ...................................430
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
Klasa S tringT okenizer............... 431
R untim eE xcep tio n........................... 305 Saetak........................................... 4 3 1
Sadraj ix

P roblem s b ris a n je m ............................517


14: Podaci o tipu 432 ta se zbiva na g ra n ic a m a ...................518
Potreba za prepoznavanjem 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
tipa tokom izvravanja............... 432 Pravljenje instanci tip o v a ...................523
Objekat tipa Class........................434 Nizovi generikih tip o v a .....................526
Literali k la se ........................................... 439 G r a n i c e ...................................................... 531
G enerike reference k la s a .................. 441 D o k e r s k i a r g u m e n t i ....................... 5 3 5
Nova sintaksa za konverziju tipova . 444 Koliko je prevodilac p a m e ta n ? ......... 537
Provera pre konverzije tipa . . . .445 K o n tra v a rija n sa ..................................... 539
Korienje literala ld ase....................... 451 N eogranieni dokerski
D inam iki in sta n ce o f . . : ...................453 a rg u m e n ti................................................ 541
Rekurzivno b r o ja n je ............................454 Konverzija h v a ta n je m ..........................547
Registrovane proizvodne N e d o s t a c i ................................................... 5 4 9

m etode...........................................456 P rosti tipovi ne m o g u biti


p a ra m e tri t i p a ....................................... 549
Poreenje instanceof
Realizacija p aram etrizo v an ih
sa ekvivalencijama klase............. 459 in terfeisa...................................................551
Refleksija: informacije o klasi E ksplicitna konverzija tipova
u vreme izvravanja....................461 i u p o z o re n ja ............................................551
itanje metoda klase.....................462 P r e k la p a n je ............................................553
Dinamiki posrenici................. 465 O snovna klasa o tim a in te r f e js ......... 554

Null o b jek ti...................................469 S a m o o g r a n i e n i t i p o v i .................... 5 5 5

Lani objekti i vezivne funkcije . . . . 476 G eneriki ko d koji se neobino


p o n a v lja ...................................................555
Interfejsi i podaci o tip u ............. 476
S a m o o g ra n i e n je ...................................557
Saetak........................................... 482 K ovarijansa a rg u m e n a ta .....................559
D in a m i k a b e z b e d n o s t t ip o v a . .562
15: Generiki tipovi 484 I z u z e c i .................................................... 563
Poreenje sa C + + - 0 1 1 1 ............... 485 M i k s i n i ................................................. 565
M iksini u jeziku C + + ......................... 566
Jednostavni generiki tipovi . . .485
M eanje p o m o u interfejsa................ 567
Bihlioteka n - t o r k i ................................ 487
K orienje obrasca D e c o r a to r ......... 568
Klasa s t e k a ..............................................490
M iksini s d inam ikim
N a s u in ic n a L is ta .................................. 491
p o s re d n ic im a ..........................................570
Generiki interfejsi..................... 492
L a t e n t n i t i p o v i .................................. 572
Generike m etode........................496
K o m p e n z a c ija za n e p o s t o ja n je
Korienje zakljuivanja o tipu
a r g u m e n ta ..............................................497 l a t e n t n i h t i p o v a ............................... 576
A rgum enti prom enljive d u /in e R elleksija........ ................................... 576
i generike m e to d e ................................499 Priinena m etode na se k v e n c u ........ 577
G enerika m etoda za u p o treh u Kada sltiajno neinate
s G e n e r a to r im a .....................................500 odgovarajuc'i interfejs....................... 580
G e n e ra to r opte n a m e n e .................. 501 Simuliranje latentnih tipova
Pojednostavljenje u p o tre h e n - to r k i. 502 pom ocu a d a p te r a ............................. 582
U sluna m eto da za S e t .......................504 U p o t r e b a f u n k c ijs k ih
A n o n i m n e u n u t r a n j e k la s e . . . .5 0 8 o b j e k a t a k a o s t r a t e g i j a .................. 585
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 S a e ta k : d a li je e k s p l ic it n a
T a j a n s t v e n o b r i s a n j e ..........................5 1 2 k o n v e r z ija t ip o v a z a is ta
P ristup u G + + - u .................................. 513 ta k o lo a ? ............................................... 590
M igracijska k o m p a tih iln o s t..............516 Proitajte i o v o ....................................... 592
x 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
ta to nizove ini p o se b n im .. . .593 p e rfo rm a n s i............................................684
Nizovi su prvorazredni objekti. .595 Perform anse razliitih L is ta ..............688
Vraanje niza v re n o sti............. 597 O p asn o sti od m ik roporedenja
p e rfo r m a n s i............................................694
Viedimenzionalni n iz o v i.........599
Iz b o rsk u p a
Nizovi i generiki tipovi............. 603 (realizacije interfejsa S e t ) .................. 696
Pravljenje podataka Izb or m apa
za testiran je...................................605 (realizacija interfejsa M a p )................ 698
Arrays.fill()................................. 605 U s l u n e m e t o d e .................................. 7 01
Generatori podataka..................... 606 U redivanje i p retraivanje lista
Pravljenje nizova od Generatora... 612 (realizacija interfejsa L i s t ) ................ 705
M etode klase A rrays....................616 Kako kolekciju ili m a p u uiniti
Kopiranje niza..............................617 n e p ro m e n ljiv o m .................................. 706
Poreenje nizova.......................... 618 Sinhronizovanje kolekcije ili m a p e . .708
Poreenje elemenata niza..............619 uvanje referen ci ............................... 7 0 9
Ureivanje niza............................ 623 K ontejner W e a k H a s h M a p ................ 712
Pretraivanje ureenog niza.......... 624 Kontejneri Jave 1 . 0 / 1 . 1 ....................7 1 3
Saetak........................................... 626 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
17: D e t a l j n o r a z m a t r a n j e Klasa S ta c k ..............................................714
Klasa B itS e t........................................... 716
k o n te jn e ra 628
Saetak .........................................................7 1 8
Potpuna taksonomija
kontejnera.....................................628 18: J a v in u la z n o - iz la z n i

Popunjavanje k ontejnera...........629 s is te m 719


G en erato rsk o reen je............................630
G en erato ri m a p a .................................. 632 Klasa File ................................................... 7 1 9
K orienje a p strak tn ih k la sa ..............635 Listanje d ir e k to r iju m a ....................... 719
Funkcije interfejsa C ollection. .643 U slune m eto d e za d ire k to riju m e . . 723
lrovera postojanja i pravljenje
O pcione o p e ra c ije ......................646
d ire k to riju m a ......................................... 72S
N ep o d rzan e o p e ra c ije ......................... 647
U la z i i / . I a z ................................................ 7 3 0
Funkcije liste.................................649
Vrste ukr/nih to k o v a ............................730
Skupovi i redosled skladitenja. .652 Vrste iz la/nih tokova
S o r te d S e t................................................ 656 (O u tp u tS tre a m .................................. 732
R e d o v i z a e k a n j e ................................6 5 7 D o d a v a n je a tr ib u ta i k o ris n ih
Redovi za ekanje s p rio rite to m . . . . 658
i n t e r f e j s a ................................................... 7 3 2
D vostrani redovi za e k a n je ..............660
I iltriranje u la /n o g t<.kd.....................733
I s c r p n i j e o M a p a m a ..........................6 6 1
I iltriranje i/la /n o g t o k a .....................734
F e rfo rm a n se ............................................663
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
S o r te d M a p .............................................. 666
I/vori i p o n o ri p o d a ta k a .....................735
L in k e d H a sh M a p ...................................667
M enjanje ponaanja t o k a .................. 736
T r a n s f o r m i s a n j e k lj u e v a
Klase koje nisu p ro m e n je n e ..............736
i k l j u e v i z a h e i r a n j e .......................6 6 8 P o s e b a n s lu a j: k la s a
N ain rada m eto d e h a sh C o d e ( ) . . . 671
R a n d o m A c c e s s F i l e ..........................7 3 7
Transform isanje kljueva zbog
T i p i n e p r i m e n e U / I t o k o v a . . .7 3 7
h r z i n e ....................................................... 674
Kedefinisanje m etode h a s h C o d e ( ) . . 678 Baferisana u la /n a d a to te k a ................ 738
('ita n je i/ m e m o rije .............................. 739
Sadraj xi

F o rm atiran ulaz iz m e m o r ije ............739 Misterija m etode v a lu e s().........810


O snove pisanja u d a to te k u ................ 740 Realizuje, ne nasleuje............... 813
uvanje i rek on stru isan je p o d atak a. .742
Nasumian iz b o r ........................814
C itan je i upisivanje d ato tek a
s n a su m in im p r is tu p o m ...................744 U potreba interfejsa
C ev o v o d i................................................... 745 za organizovanje..........................815
U s lu n e k la s e z a ita n je Skup Enum Set urnesto
i p i s a n j e ...................................................... 7 4 5 in d ik a to ra .....................................819
itan je b in a rn ih d a to te k a ...................748 Korienje m ape E num M ap . . .821
S t a n d a r d n i U / I t o k o v i .................... 7 4 9 M etode koje se menjaju u
itan je s ta n d a rd n o g u lazn o g toka . . 749 zavisnosti od k o n sta n te ............. 823
O m o ta v a n je to k a S y ste m .o u t Stvaranje lanca ogovornosti
u P r in tW rite r..........................................750 pomou nabrojanih tipova............ 826
P reu sm erav an je stan d a rd n o g Maine stanja s nabrojanim
u laza/izlaza.............................................. 751 tipovima...................................... 830
U p r a v l j a n j e p r o c e s i m a .................... 7 5 2 Viekratno otkrivanje tip a .........836
N o v e U / I k l a s e ..................................... 7 5 3 Otkrivanje tipa pomou
Konverzija p o d a ta k a ............................757 nabrojanih tipova........................ 838
Pribavljanje p ro stih tip o v a ................ 759 Korienje metoda koje se menjaju
Baferi p rik a z a ..........................................761 u zavisnosti od konstante
Rad s p o d acim a p o m o u b a f e r a .. . . 765 nabrojanog tipa............................840
D etaljno o b a te rim a .............................. 766 Otkrivanje tipa pomou mapa
D atoteke preslikane u m e m o riju . . . 769 EnumMap...................................842
Z akljuavanje d a to te k a ....................... 772 Korienje 2-D niza.......................843
K o m p r i m o v a n j e .................................. 7 7 5 Saetak........................................... 844
Ied n o slav n o k o m p i im ovanje
u lo rm a tu G Z I P ..................................... 776
20: A n o ta c ije 845
K om prim ovanje veeg broja
dato tek a u ib rm atu Z i p ....................... 777 Osnovna sin tak sa........................846
lava arbive ( IA IO ...................................779 Detinisanje anotacije......................... 846
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 M etaano tacije....................................848
Pronalaenje k la s e ................................ 78 1 P is a n je p r o c e s o r a a n o t a c ij a . . . .848
U pravljanje s e rija liz o v a n je m ............785 Elementi anotacija............................. 849
Korienje tr a jn o s ti .............................. 793 Ogra n ienj a pod razu meva n ih
X M L ................................................................ 7 9 9 v re d n o s ti............................................ 850
P r e f e - r e n c e s ..............................................8 0 2 Generisanje spoljnih d ato te k a ........ 850
S a e t a k ......................................................... 8 0 4 Anotacije ne podravaju
nasleivanje........................................ 854
Realizacija procesora......................... 854
19: N a b ro ja n i tip o v i 805
apt za obradu an o tacija............. 857
O snovne mogunosti Upotreba obrasca Visitor
nabrojanih tip o v a........................805 sa alatkom a p t.............................. 861
Uvoz slalinih lanovii Jedinino testiranje pom ou
u nabrojani tip............................ . H06 a n o ta c ija .......................................864
Dodavanje metoda Testiranje generikih tipova
nabrojanom t i p u ........................807 alatkom @Unit............................873
Kedolinisanie t'iuini mcloda.......... S0S ,,Svite nisu potrebne................... 874
Nabrojani tipovi u naredbam a Realizaciia interfejsa (Unit...........875
sv v itch ............................................809 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
Uzajam na b lo k a d a ......................978
Nove kom ponente biblioteke . .983
Svi aspekti paralelnog C o u n tD ovvnL atch - brava
izvravanja.....................................886 sa o d b ro ja v a n je m ................................ 983
Bre iz v rav a n je .....................................886 C y c lic B a rrie r..........................................986
Poboljan d izajn k o d a ......................... 889 D e Ia y Q u e u e ............................................ 988
O s n o v e v i e n itn o g P rio rity B lo c k in g Q u e u e ..................... 991
p r o g r a m i r a n j a ........................................ 8 9 0 K ontroler staklenika napravljen
Definisanje z a d a ta k a ............................890 po m o u S c h e d u le d E x e c u to ra ......... 994
Klasa T h r e a d ......................................... 891 S e m a f o r ................................................... 997
U p o treb a izvrilaca E x c h a n g e r............................................ 1001
(interfejsa E x e c u to r)............................893 S i m u l a c i j a ..........................................1003
D obijanje p o v ra tn ih v redn osti Sim ulacija alterskog slubenika . . 1003
o d z a d a ta k a ............................................896 Sim ulacija r e s to r a n a ......................... 1008
S p a v a n je ...................................................897 R aspodela p o s la .................................. 1013
P r i o r i t e t ...................................................899 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
P rep u ta n je .............................................. 901 Poreenje tehnologija u zajam no
Servisne n i t i ............................................901 iskljuivih brava ( m u t e x a ) .............. 1018
V arijante p ro g ra m ira n ja .....................906 K ontejneri bez z a k lju a v a n ja ......... 1027
T erm inologija..........................................911 O p tim istik o z a k lju a v a n je ............1034
Kako se p rid ru iti postojeoj n i t i . . . 912 R ea d W rite L o c k .................................. 1036
Korisniko o k ruenje koje A k tiv n i o b j e k t i ............................... 1039
brzo re a g u je ............................................913
S a e t a k ................................................. 1042
G ru p e n iti................................................ 914
L iteratura za dalje usavravanje . . . 1044
H vatanje iz u z e ta k a .............................. 915
Deljenje r e s u r s a .......................... 918
N epravilno p ristu p a n je resursim a . . 918
22: Grafika korisnika
R azreavanje takm ienja okruenja 1045
za deljene re s u rse ...................................921
A tom ske operacije i tre n u tn a A pleti........................................... 1047
v id ljiv o st...................................................926 Osnove Svvinga..........................1047
A tom ske k l a s e ....................................... 932 Alatka za p rik aziv an je....................... 1050
K ritini o d e ljc i....................................... 933 Pravljenje d ugm eta................... 1051
S inhronizacija s d ru g im
I Ivatanje d o g a d a ja ................... 1052
o b je k tim a ................................................ 939
Lokalno skladite n i t i ......................... 940
Vieredna polja za te k s t...........1054
G a e n j e z a d a t a k a ............................... 9 4 2
Rasporeivanje elem enata. . . .1056
U krasna b a ta ......................................... 942
R asporediva 1056
B o r d e r L a y o u t.........
R asporediva 1057
F lo w L a y o u t..............
G aenje blo kiran ih z ad a ta k a ..............945
R asporediva 1058
G r id L a y o u t..............
Prekidanje izv rsav anja......................... 946
R asporediva G ridB agL avout . . . . 1058
Provera postoji li p r e k id .....................954
A p solutno p o z ic io n ira n je ................ 1059
M e u s o b n a s a ra d n ja
R asporediva B o x I,a y o u t................ 1059
z a d a t a k a ...................................................... 9 5 6 Koji je p ristu p najbolji?..................... 1059
w ait() i n o tity A lI() ..............................957
Model dogaaja
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
grafike biblioteke Svving.........1059
T ipovi dogadaja i p rije m n ik a ......... 1060
Proizvodai, p otroai i redovi
Praenje vi.e d o g a d a ja ..................... 1066
za e k a n je ................................................ 971
Cevi za ulazno/izlazne operacije Primeri Svving kom ponenata. . 1068
izm edu z a d a ta k a .................................. 976 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 I z r a d a SVVT a p l i k a c i j a ..................1 1 5 0


Jed n o redn a polja za t e k s t ................ 1073 Instaliranje S W T - a ............................1150
Ivice..........................................................1075 Z dravo, S W T ....................................... 1150
Mali program za u redivanje te k sta . . 1076 Izbegavanje re d u n d a n tn o sti............1153
Polja za p o tv rd u ...................................1077 M e n iji..................................................... 1155
R a d io -d u g m a d ..................................... 1078 O kna s k articam a, d u g m ad
K om hinovane liste i d o g a a ji..............................................1156
(pad aju e liste)..................................... 1080 G ra fik a ...................................................1160
G rafike liste..........................................1081 Paralelno izvravanje u SW T-u . . . 1162
O k n o s je z i c im a ................................ 1083 Poredenje SW T-a i Svvinga.............. 1164
O k v iri za p o r u k e ................................ 1083 S a e t a k ...................................................... 1 1 6 4
M e n iji...................................! ................1085
R e s u rs i.................................................. 1165
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
A: Dodaci 1166
D ijalozi za rad s d a to te k am a ............1099
Dodaci koji se mogu
H T M I. u k o m p o n en tam a
preuzeti sa Interneta................. 1166
biblioteke Svving...................................1101
Klizai i linije n a p re d o v a n ja ............1102 Misliti na jeziku C:
B iranje izgleda i p o n a a n ja .............. 1103 osnova za J a v u .......................... 1166
Stabla, tabele i ip b o a r d ...................1105 Seminar Thinking in Java . . . . 1166
J N L P i J a v a W e b S t a r t ................. 1105 CD sa sem inarom
Paralelno izvravanje i Svving. .1110 H ands-O n Java.......................... 1167
D u g o trajn i z a d a c i .............................. 1111 Seminar Thinking in O bjects.. 1167
V izuelno vienitno p ro g ram iranje ..1 1 1 8
Thinking in Enterprise fav a .. .1167
Vizuelno program iranje
T hinking in Patterns
i zrna J a v e ................................. 1120
(vvith Java)...................................1168
ta je z rn o ? ............................................ 1121
Ispitivanje zrna Seminar Thinking in P atte rn s.. 1168
klasom I n t r o s p e c t o r ....................... 1123 Konsultacije i revizije dizajn a.. 1169
N apred n ije z r n o ...................................1128
Z rna J.ne i s in h ro n iz a c ija ................ 1131 B: Resursi 1170
Pakovanje z r n a ..................................... 1135
Sloenija podrka za z r n a ................ 1137 S oftver......................................... 1170
V'ie o z r n im a ....................................... 1137 Programi za ureivanje
A l t e r n a t i v e z a S v v in g .................... 1 13 7 teksta i alatke za pravljenje
P r a v l j e n j e F la s h VVeb k l i j e n a t a aplikacija.....................................1170
p o n i o u F l e x a .................................. 1138 K n jig e ......................................... 1171
Z dravo, h lc x ..........................................1139 Analiza i projektovanje................ 1171
Prevodenje M X M l.-a......................... 1 140 Jezik Python............................... 1173
M XM L i A c tio n S c rip t....................... 1141 Spisak rnojih knjiga.......... ......... 1174
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
Spisak termina korienih
Povezivanje ,s lav o m ............................I 144 u knjizi 1175
M odeli podataka i povezivanje
p o d a ta k a .................................................1147
Indeks 1177
Izgradnja i p rim e n a ............................1 148
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 o s a m je v i Se p r o u a v a o ,p o C 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 pro-
gramskih 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 or-
avanja 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 sma-
tralo 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 je-
zika 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 vreme-
nom 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 odgo-
varajueg 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 pojedno-
stavljuju te zadatke. Najzad, latila se nekih zaista veoma sloenih problem a kakvi su me-
duplattonnsko 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 lvthon najblii t o m cilju. Ioglodajte ivw w .P yth o n .o rg


2 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 iz-
m 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 po-
kazatelj 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, na-
pravili 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 pred-
nosti 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 3

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 fa-
scinantnih 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 ku-
pe. 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 prezen-
tacija Thinking in C upravo njima pomae da dostignu potreban nivo.
Poglavlje Paralelno izvravanje(ranijeVienitno izvravanje11) 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 para-
lelan 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 obra-
sce, 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 raz-
borito 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 je-
dan obrazac i odm ah uradim odgovarajuu vebu, ak i ako zbog toga tek nakratko go-
vorim . (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 te-
stiranje koji prikazuje i vrednuje rezultate svakog programa. (Sistem je napisan na Pvtho-
nu; 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 pro-
gram eri trebalo da vladaju.
4 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 iz-
menjeni. 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 ja-
vljala 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: Pre-
vie nota!, iako se ja ni na koji nain ne poredim s M ocartom ). M ogu sam o da pretpo-
stavim 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 uklo-
nim 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. Ve-
rovali 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 iz-
meu 1900. i 1920. Nastao je u Engleskoj kao reakcija na mainsku proizvodnju, indu-
strijsku 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 mo-
dernih alata. Postoje velike slinosti s dananjom situacijom: prelazak u novi vek, napre-
dak 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 si-
stema, u pravcu softverskog majstora".
I autor i dizajner korica (koji su prijatelji od detinjstva) nalaze inspiraciju u ovom po-
kretu 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 ori-
jentisanom 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 5

ovde uhvaene i, po svoj prilici, ubijene u tegli za uzorke i smetene u male izlobene ku-
tije: 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 ost-
varivanju 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 sme-
stio 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 ostali-
ma 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 ura-
dim ). Jonathan Wilcox, Esq. proeljao je struk turu mog preduzea, prevrnuo svaki ka-
men 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 na-
pravila od sebe strunjaka za obradu zvuka i postala je bitan deo tim a za pravljenje mul-
timedijskih 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 re-
cenzent 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, poseb-
no 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.
6 Misliti na Javi

Posebno hvala m om novom prijatelju, kafi, koja mi je ulila gotovo neogranien entu-
zijazam 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 . Cyg-
win (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 dopri-
nos 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.) Jet-
Brains 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 konfigu-
risao ga prem a mojim specifinim potrebam a. Katkada je bio koristan i JEdit Slava Pesto-
va ( 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 rea-
vanje 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 Pe-
tersu, 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 ne-
verovatan), (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, Da-
vid 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, Mal-
colm 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 7

Kad god pomislim da sam najzad sve nauio o program iranju za paraleleno izvra-
vanje, 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 Pa-
ralelno 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 dopri-
nosi sklonosti ka program skim jezicima?), Neil Rubenking (koji se bavio jogom, vegeta-
rijanstvom 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 briljant-
nom 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 Will-
Harris, 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 fo-
toslog 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 ma-
serka), Andrew Birnstock, Steve Sinofskv, (D Flildebrandt, Tom Keffer, Brian McElhin-
ney, 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 Mo-
elter (i McMillan), Michael Wilk, Dave Stoner, porodica Cranston, Larry Fogg, Mike Se-
queira, 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 Pro-
metej, eli

Ljudska bia... sasvim su pod vlau odreenog jezika koji je postao sredstvo izraavanja u nji-
hovotn 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 ne-
maju 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 po-
glavlju 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 pro-
gram skup naredaba, poznajete princip potprogram a/tunkcije/m akroa, kontrolnih struk-
tura 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 po-
reenja budu jednostavna i objasniu sveto mislim da ne zna neko ko ne koristi C/C++.
Uvod 9

Uenje Jave
O tprilike u isto vreme kada se pojavila moja prva knjiga, Using C++ (Osborne/McGravv-
Hill, 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 i-
rom 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 (ka-
snije i na odseku za Javu), i prim etio sam kako sm o i ja i drugi govornici sldoni da pro-
senoj 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 na-
pravio 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 se-
m inare. O tim sem inarim a moete saznati vie na adresi www.MindView.net. (Uvodni se-
m 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 pre-
raujem materijal sve dok nc zakliuim da je podesan kao sredstvo pouavanja. Ovu knji-
gu 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 knji-
ga 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 na-
inom na koji Ijudi ue jezik Java. Poglavlje u knjizi zamiljao sam kao jednu dobru lek-
ciju 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 pre-
am biciozan i uvrstio previe novih pojmova odjednom , spoznao sam - kroz proces pri-
kaza 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 obja-
njavam, 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 uhva-
tim 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. Ve-
rujem 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 je-
zika. Da uzm em prim er iz C-a: ako zapam tite tabelu prioriteta operatora (ja ni-
kada 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 za-
boravite na prioritete i koristite zagrade svuda gde stvari nisu potpuno jasne.
4. Da svaki odeljak bude ovoljno usredsreen, tako da vreme izlaganja i pauza izme-
u iziaganja i vebe budu kratki. To sluaoca dri budnim i aktivnim tokom semi-
nara, 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 na-
stavite 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 ispre-
ajem za sedmicu dana. U jednom trenutku, neki naruilac je od mene zatraio da pre-
dajem 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 ispre-
davati 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 jez-
gro CD-a Hands-On Javn. O statak knjige obuhvata kurs srednjeg nivoa i nalazi se na CD-
u Intermedinte Thinking in Javu. O ba CD-a moete kupiti na w w w .M ind\ricw.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 We-
ba; gotovo svaka druga nezavisna distribucija Jave ima takav ili slian sistem dokum enta-
cije. 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). Jednostav-
no 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 Thin-
king iu java Annotatcd Solution Guide, koji se moe kupiti na lokaciji www.Mind-
Vicw.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 do-
voljnog 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 Inter-
netu, 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 Ope-
ralori i Kontrolisanjc izvravanja obuhvataju osnove Jave koje potiu iz C-a, mreni semi-
nar 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 pravi-
ma (obino na engleskom):

//:! A u t o r s k a P r a v a . t x t
Ovaj 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,
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 , kopiranje, prepravljanje i distribuiranje


ovog r a u n a r s k o g i z v o r n o g koda (Izvorni kod) i njegove dokumentacije, u dole
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 , ukoliko gornju poruku o autorskim
p r a v i m a , ovaj 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 iz j e d n e
n j e g o v e kopije.

1. D o z v o l j a v a se p r e v o d e n j e Izvornog koda. Preveden Izvorni kod s m e t e da


ukljuite u privatne i komercijalne softverske programe, ali samo u izvrnom
formatu.

2. Neizmenjen Izvorni 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 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, 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 iz k n j i g e
,,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 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

4. Mi n d V i e w , Inc. j e z a t i t i o a u t o r s k a p ra v a Izvornog koda i dokumentacije.


Izvorni 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 i uz n j e g a se ne d o b i j a
nikakva eksplicitna ili implicitna garancija, ukljuujui g a r a n c i j u o pro-
daji, pogo d n o s t i za o d r e e n u u p o t r e b u ili n e k r e n j u n e i j i h prava. MindView,
Inc. ne jami da e b i l o koji p r o g r a m koji sadri Izvorni kod raditi bez
prekida ili gre a k a . MindView, Inc. ne tvrdi da j e Izvorni kod ili b i l o koji
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 . Celokupan riz i k , u vezi
s kvalitetom i performansama ovog softvera, snosi sam korisnik I z v o r n o g koda.
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 kod n a p r a v l j e n radi istraivanja i
nasta v e , pa mu se s a v e t u j e da se ni u koju s vr h u niti iz b i l o ko j e g r a z l o g a ne
o s l a n j a i s k l j u i v o na Izvorni kod, niti na b i l o koji p r o g r a m koji ga s a d r i .
A k o se ispostavi da j e Izvorni kod n e i s p r a v a n , s a m k o r i s n i k snosi s v e tro-
k o v e s e r v i s i r a n j a , o p r a v k e ili isp ra vk i.
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 , INC. NIT I NJE60V I Z D A V A N E E BITI


ODGOVORNI BILO KOME, B EZ O B Z I R A N A B I L O K A K V U PRAVNU TEORIJU, ZA D I R E KT NU ,
INDIREKTNU, POSE B N U , POSLEDINU ILI S L U A J N U TE T U, ZA B I L O KA K A V P R E K I D
POSLOVANJA, G U B I T A K P R O F I T A ILI POSLOVNIH PODATAKA, ILI B I L O KA K A V DRUGI
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 IZVO R-
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 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
KO J E G R E Z U L T U J U E G PR O G R A M A , A K I K A D A BI M I N D V I E W , INC. ILI N J E G O V I Z D A V A
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 . MINDVIEW, INC. P O S E B N O NE D A J E
NIKAKVU GARANCIJU, UKLJUUJUI 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 I POGODNOSTI
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 SE NA TO. IZ V O R N I KOD JE D O S T U P A N U
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 , INC,
KOJI N E M A NITI 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 , PO DRKE,
AURIRANJA, POBOLJANJA ILI M O D I F I K A C I J A .
M o lim, i m a j t e u vidu da M i n d V i e w , Inc. odrava Web lokaciju http://www.Mind-
View.net (i n j e n e z v a n i n e d u p l i k a t e ) . J e d i n o se o d a t l e m o g u pr eu ze ti e le k -
tronske kopije Izvornog koda, 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
preu z e t i besp l a t n o .

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 g r e k u , m o l i m v as da i s p r a v k u
poaljete preko sistema povratnih informacija koji m o e t e pr onai na lo ka ci ji
www.MindView.com.

///:-
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 sva-
koj izvornoj datoteci.

Nain pisanja korien u knjizi


U ovoj knjizi identifikatori (metode, prom enljive i im ena klasa) ispisani su polucrno. Ve-
ina 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 (po-
gledajte 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 (ko-
liko znam ) nisam imao nikakve veze. Pitanje stila moe biti predm et vieasovnih raspra-
va, 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.trie-
m 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 tre-
balo 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 iz-
danje 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 klasifika-
cije 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 raz-
voja, 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 speci-
finosti 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 slo-
enost problema koje ste u stanju da reite direktno povezana s vrstom i kvalitetom ap-
strakcije. Pod vrstom" mislim na to ta apstrahujete. Mainski jezik je mala apstrakcija
maine na kojoj se programi izvravaju. Mnogi takozvani ,,proceduralni jezici koji su sle-
dili 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 za-
hteva od vas da razmiljate iz ugla strukture raunara um esto iz ugla probiem a koji rea-
vate. 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 po-
stoji, recimo poslovanje). Napor koji iziskuje ovo preslikavanje i injenica da je ono ne-
bitno 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 i-
ranju 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 progra-
m er predstavlja elemente u prostoru problem a. Ovo predstavljanje u principu ne ogra-
niava 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 ne-
maju svoj par u prostoru problem a.) Ideja je da se program u dozvoli da se prilagodi nera-
zumljivom 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 flek-
sibilnija 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 unu-
tranje stanje i operacije koje m oete zahtevati da izvri. Meutim, ovo i nije tako loa ana-
logija 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 orijen-
tisanog 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 po-
dacima. Teoretski, moete uzeti bilo koju idejnu kom ponentu problem a koji rea-
vate (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, mo-
ete 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 po-
stojee objekte. Stoga moete da uslonjavate program koji e biti skriven iza jed-
nostavnih 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 poru-
ke 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 ri-
s 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, Ti-
m 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, vie-
znana 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 oso-
bina 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, ra-
una, 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 ide-
ja u objektno orijentisanom program iranju. Apstraktni tipovi podataka rade gotovo isto
kao ugraeni tipovi: moete stvarati promenljive datog tipa (koje se nazivaju objektiili in-
stance u term inologiji objektno orijentisanog program iranja) i raditi s tim promenljiva-
ma (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 ra-
un ima saldo, svaki blagajnik moe da prim i depozit itd. Istovremeno, svaki lan ima vla-
stito stanje: svaki raun ima drugaiji salo, svaki blagajnik ima ime. Stoga blagajnici,
klijenti, rauni, transakcije itd., pojedinano mogu biti predstavljeni jedinstvenim entite-
tom 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 poda-
taka, 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 po-
dacima) 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 prinu-
en da koristi postojei tip koji predstavlja m em orijsku jedinicu unutar raunara. Pro-
gramski 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 ob-
zira na to da li se slaete sa stavom da je svaki program simulacija sistema koji projektu-
jete, 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 poda-
cima, 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 meto-
da. 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, na-
vedite 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 progra-
m irali sa objektima.
Gornja slika je napravljena u skladu s form atom koji koristi unifikovani jezik za mode-
lovanje (engl. Unified Modeling Languagc, UML). Svaka klasa je predstavljena pravouga-
onikom , 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 za-
nim 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 ob-
jekte sm atrate davaocima usluga. I va program e pruati usluge korisniku, i to po-
m 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 bu-
dete radili, doi ete do take kada moete rei: Ovaj objekat izgleda dovoljno jednostav-
no 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 soft-
vera: 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 pro-
jektovanja 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 ka-
talog 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) in-
terfejs 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 uslu-
ga 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 pokua-
vali 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 poda-
taka) i programere klijente4 (korisnike klasa koji te tipove upotrebljavaju u svojim aplika-
cijama). 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 pred-
stavlja 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 bi-
blioteku, vi stvarate odnos sa kJijentom koji je takoe program er, ali koji sastavlja aplika-
ciju 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 kli-
jenta 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 progra-
mere 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 pro-
tected. Ovi specifikatoripristupaodreuju 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 nasled-
nice 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 ko-
rienje 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 pravlje-
nje objekta lana (engl. member object). Vaa nova klasa m oe biti sastavljena od m a ko-
iiko 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 postoje-
ih 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 po-
stoji jedan auto. Kad oznaavam spajanje, obino u koristiti jednostavniji oblik: sam o li-
niju, 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 iz-
menite 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 ogra-
nienja 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 stek-
nete 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 funkcional-
nost 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 pro-
gram skom jeziku.
Bila bi teta da se nam uite i napravite neku klasu a da onda budete prim orani da pra-
vite p otpuno novu klasu koja im a slinu funkcionalnost. Bilo bi jednostavnije da uzme-
m 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 tipovi-
ma. 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 svo-
jih 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 ,,ot-
pad a svaki pojedini otpadak ima teinu, vrednost itd. i moe biti iseen, istopljen ili ra-
stavljen. 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 nje-
gove vrste i stanja). Koristei nasledivanje moete izgracliti hijerarhiju tipova. Ta hijerar-
hija 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. Compu-
ter-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. Odav-
de 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 po-
vrinu 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 ve-
liki broj m eum odela da biste od opisa problem a doli do opisa reenja. Kod objekata, hi-
jerarhija 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 i-
ranjem 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 iz-
vedena klasa istog tipa kao i osnovna klasa. U prethodnom prim eru krug je oblik. Ekvi-
valencija 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 rea-
lizacija 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 po-
trebne 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 in-
terfejsu, 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 kla-
si)? To bi znailo da je izvedena klasa potpimo istog tipa kao osnovna klasa, jer ima isto-
vetan 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 svoje-
vrstan 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 sa-
vrena, 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. Pretpo-
stavim 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 do-
voljno opta i da treba da se preim enuje u sistem za kontrolu temperature", kako bi m o-
gla 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 sup-
stitucija) 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 pret-
hodnom 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 odra-
vanja 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 me-
toda naredi generikom obliku da se iscrta ili generikom vozilu da sm ota volan ili gene-
rikoj 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 objek-
tim 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 orijen-
tisan 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 gene-
rie poziv funkcije odreenog im ena, a izvrni sistem kasnije razrei ovaj poziv ubaciva-
njem 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 konkre-
tan 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 podra-
zum 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 orfi-
zam, napisaemo naredbe koje ignoriu specifine detalje tipova i obraaju se samo
osnovnoj klasi. Te naredbe nisii povezane sa inform acijam a specifinim za pojedine tipo-
ve, 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 b-
lika 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 is-
crtava i brie. Ako u nekom drugom delu program a pozovemo funkciju radiN esto():

K rug krug = n e w Krug();


T r o u g a o t r o u g a o = n ew T r o u g a o ( ) ;
Linija linija = n ew L i n ija();
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 radiNe-
sto() m oe da poalje Obliku, Krug moe da prihvati. Zato je gornji poziv p o tp u n o sigu-
ran 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. ca-
sting 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 mo-
rate 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 slu-
aju, 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 poziva-
mo m etodu iscrtajSe() za Kvadrat ili Liniju; ali kada poruku iscrtajSe() poaljemo ne-
poznatom 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 ra-
diN 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 izvrava-
nje vode rauna o svim detaljima. Zasa je dovoljno da znate da polim orfizam funkcio-
nie 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 osta-
lim 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 to-
me, 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 ori-
jentisanim program iranjem u celini, m orate napraviti svoju hijerarhiju da biste obezbe-
dili 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 osnov-
ne 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 o-
ete da utvrdite. To je naroito vano kod operacija na sistemskom nivou, kao to je ob-
rada 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 fco-
lekcija, 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 pri-
m 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 jed-
nog prim erka raznih tipova objekata i druge kom ponente m eu kojima su redovi e-
kanja, stabla, stekovi itd.
Sa stanovita program a, za vas je vaan kontejner s kojim moete da radite i reite pro-
blem. 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 ti-
pove 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 osta-
lih. 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 se-
kvence koje mogu imati identine interfejse i, spolja gledano, naine rada. No, postoji bit-
na razlika u trajanju i zahtevima odredenih operacija nad njima. Prim era radi, nasum ino
pristupanje elementima kontejnera ArrayList je operacija s konstantnim vremenom izvr-
avanja; bez obzira na to koji element izaberemo, potrebno je isto vreme. Meutim, pom e-
ranje 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 Lin-
kedList, nego u ArrayList. Ove i druge operacije nisu jednako efikasne, to zavisi od struk-
ture 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 kon-
tejnera, 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 od-
reenijeg tipa. Ovaj nain eksplicitne konverzije naziva se svoenje nanie (engl. dovvnca-
sting). 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 svo-
enje 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 iz-
vravanju - izuzetak (engl. exception), o kome uskoro govorimo. Kada iz kontejnera uzi-
m 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 pro-
gram a i dodatni napor za program era. Zar ne bi bilo logino da nekako napravim o kon-
tejner 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 ponen-


te 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 o-
gao 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 do-
stupnim 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 na-
pravite novi avion i stavite ga u kontejner svaki put kada avion ue u zonu kontrole va-
zdunog 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 na-
putaju aerodrom . Znai, trebao bi vam drugi kontejner za male avione i kad god napra-
vite 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 za-
vrite 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 si-
stemim 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. sco-
ped -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 si-
tuacijama. 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 va-
zdunog 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 tre-
ba, koliko e trajati, niti kog su tipa. Sve se to odreuie kada program ve radi (ovakav na-
in 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 upra-
vlja 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 sloe-
ni, tako da dodatni reijski trokovi za nalaenje prostora i njegovo oslobaanje nee bit-
no 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 stva-
raju na steku, prevodilac odreuje koliko dugo objekti traju i moe automatski da ih uniti.
Meutim, kada objekat stvorite dinamiki, prevodilac nema informacije o njegovom i-
votnom 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 pro-
blem u program im a pisanim na C + + -u). Java obezbeuje tzv. sakuplja smea (engl. gar-
bage 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 ko-
risti 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 pre-
preka 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 bi-
blioteka 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 etnu-
te 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 od-
govarajui 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 indi-
katora 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 progra-
nia, 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 grea-
ka. Ako ne napiete kod tako da pravilno obrauje izuzetke, javie se greka pri prevoe-
nju. 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 isto-
vremeno. Mnogi programski poslovi zahtevaju da program moe da zaustavi rad, poza-
bavi 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 najsit-
nijih pojedinosti, pisali su prekidne servisne rutine, a suspenzija glavnog procesa je inici-
rana 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 za-
sebno 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 ope-
rativni sistem podrava vieprocesorski rad, svaka nit moe biti dodeljena razliitim pro-
cesorima, 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 pro-
cesora. 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 za-
koljica: 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 po-
datke 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 er-
ske 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 pro-
bleme 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 cen-
tralizovano tako da informacije m ogu da se m enjaju i da se te prom ene prenose svim ko-
risnicima 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 prika-
zuje 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 ko-
risti sistem za upravljanje bazam a podataka, tako da program er ,,uravnoteava raspored
podataka u tabelama da bi postigao optim alno korienje. Sistemi esto dozvoljavaju kli-
jentim 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 mi-
slite. Posebno je problem atino podrati vie tipova raunara i operativnih sistema. Ko-
nano, 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 proce-
sne zadatke, prebacujui deo obrade na klijentski raunar, a ponekad i na druge raunare
na serverskoj strani, koristei takozvane posrednike (engl. middleware). (Posrednici se ta-
koe koriste da bi se olakalo odravanje.)
Realizacija tako jednostavnog postupka - slanja informacija - toliko je slojevita i slo-
ena 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 ra-
znih 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 mo-
da 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 mo-
gunosti 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 interak-
tivni; 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 i-
ta bio predvien samo za pregled, nije m ogao da izvri ni najjednostavnije zadatke ob-
rade. (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 kli-
jentskoj 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 jed-
nostavne 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 sla-
nje 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 pake-
tom da uradi. Najea akcija je da se pokrene program koji se nalazi na serveru, u direk-
torijum 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 jezici-
ma. 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. Da-
nas 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/.i-
va. Odziv CGI program a zavisi od toga koliko podataka se alje, kao i od optereenja ser-
vera 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 za-
tim 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 i-
tai Weba sposobni su da urade ogrom an posao, a s prvobitnim statikim HTM L pristu-
p 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 progra-
m 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 pro-
gram iranje s klijentske strane povlai vrtoglav niz problem a i reenja. U daljem tekstu da-
jem o pregled pitanja i pristupa pri program iranju klijentske strane.

Dodaci
Razvoj dodataka (engl. plug-ins) predstavlja jedan od najduih koraka napred u progra-
m iranju s klijentske strane. Program er dodaje novu funkcionalnost itau tako to kori-
snik 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 ra-
dite u okviru izgradnje odredene stranice. Vrednost odataka za program iranje s klijent-
ske 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 je-
zici 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 tu-
mai 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 sofistici-
rane 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 Java-
Scripta 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 Java-
Script). 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 ne-
davno 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 procena-
ta, 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 elegant-
no da ree problem e teke i u tradicionalnim program skim jezicima, kao to su istovre-
meni 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 najnovi-
ju 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 ika-
kve 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 za-
hteva 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 is-
crta 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 po-
javila, 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 aple-
te 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 tehno-
logija, 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 tehno-
logiju 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 usvo-
jen 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 pro-
gram 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 in-
dovvs 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 vir-
tuelna maina (Java Virtual Machine, JVM) plus Java biblioteke. JVM je softverska plat-
form 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 pro-
blema, 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/ser-
ver 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 pre-
duzee, 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 iz-
gledima 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 In-
ternetu, ne znate na kojoj platform i e on raditi i elite da budete sasvim sigurni da ne i-
rite 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 neuobia-
jeno 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 pree-
m 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 najkra-
im putem koji vam omoguava da koristite postojeu bazu koda, um esto da ponovo pi-
ete program e na novom jeziku.
Kada se suoite sa ovim zbunjujuim nizom reenia za program iranje s klijentske stra-
ne, 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 progra-
miranje, uvek je dobro odabrati pristup koji omoguava najbri razvoj u odreenoj situa-
ciji. 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 stra-
nicu 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 ser-
verske 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 zasno-
vani 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. Progra-
miranje sa scrverske strane razm otreno je u knjizi Thitikingin Enterprise Java, koju moe-
te 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 funk-
cija i koncepte niskog nivoa kako biste stvorili m odel u glavi. Zbog toga nam treba po-
sredno 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 pro-
gram. Biete prijatno iznenaeni: dobro napisan Java program je uglavnom daleko jed-
nostavniji 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 stra-
na 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 pro-
bleme 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 odlu-
ite da li e ih lava optim alno zadovoljiti ili bi bilo bolje da radite s nekim drugim pro-
gramskim 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. Kon-
kretno, preporuujem da razm otrite Python; posetite www.Python.org. Ako ipak izabere-
te 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 kompli-
kuju neke aspekte C++-a.
Jezik Java je napravljen za one koji ele da se bave samo objektno orijentisanim pro-
gram iranjem . Pre nego to se uopte upustite u program iranje, m orate poeti da raz-
miljate 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 jedno-
stavnije 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 e-
sto ne sme da gubi iz vida nain rada koji se primenjuje. Da li sa elem entom radite direkt-
no 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 sintak-
som 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 (re-
ferenca). Sve dok drite tu referencu, imate vezu s televizorom, ali kada neko kae da pro-
menite 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 i11je 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 trenut-
ku 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 organizova-
na 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 regi-
stri 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 poka-
zivaa 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 si-
stem 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 me-
m 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. Narav-
no, 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 pro-
gram 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 obez-
beuju sofisticiraniju podrku za skladitenje i preuzim anje inform acija o objekti-
ma 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 opera-
tora 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 jed-
nog do drugog raunara, kao to je sluaj u veini jezika. Nepromenljivost veliina je je-
dan 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 (stati-


k 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 - - - Boolean
char 16 Unicode 0 Unicode 2'6-l Character
byte 8 -128 + 127 Byte
short 16 -2'5 +2I5-I Short
int 32 -23' +23'-l Integer
long 64 -263 +263-l Long
float 32 IEEE754 IEEE754 Float
double 64 IEEE754 IEEE754 Double
void - - - 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, ni-
jedna 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 zaa-
tak 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 iz-
van njegovog m em orijskog bloka ili koristi m em oriju pre inicijalizacije (to su este gre-
ke u program iranju), doi e do nepredvidljivih situacija.
Jedan od osnovnih ciljeva Jave je sigurnost; u njoj se uopte ne pojavljuju m nogi pro-
blemi 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 obje-
kat. 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 izvr-
avanju. Na taj nain su tipine greke s nizovima predupreene 11 Javi.
Moete da napravite i niz prostih tipova. Ponovo, prevodilac garantuje ispravnu inici-
jalizaciju 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 vidlji-
vost 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 ko-
ristei 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 ("neki znakov ni niz");
} // kraj oblasti 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 no-
avaju celim tokom programa.
Poto se objekti napravljeni pom ou operatora new zadravaju dokle god su vam po-
trebni, 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 poja-
vila 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 ru-
gim 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 iz-
gleda. Tii rezervisanu re (koja je toliko esta da nee biti pisana polucrno dalje kroz knji-
gu) 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 po-
ruke) 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 pu-
tem 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 obje-
kata 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;
I
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 ,,na-
dovezujete 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 me-
tode. Da biste shvatili metode, prvo m orate da nauite ta su argumenti i povratne vred-
nosti (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 talse
char "\u0000" (nullj
byte |byte)0
short (short)O
mt 0
long 0L
float O.Of
double 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 pro-
stog 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. Li-
sta a rg u m en ata zadaje tipove i im ena inform acija koje elite da p rosledite m etodi. Im e m e-
tode 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 e-
tod 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 argu-
m 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 prevodi-
lac 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 e-
n 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 vrecJ-
n 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 a-
ite 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 e-


toda 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 e-
sto 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 de-
ava, 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.

1 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 predsta-
vljaju 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 ve-
likim 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 pro-
sto 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 pozi-
vaju. 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 o-
stavkom da su razliite). Ili jo gore, zam islite da piete p ro g ram i u svoju biblioteku do-
dajete 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 predsta-
vlja 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 zajed-


n 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 a-
ket 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 kla-
se. 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 defi-
nicije. 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 kori-
stiti, 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 ne-
statinim 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 ine-
m 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 prevo-
d 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 ImeKla-
se.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, pre-
ko 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 obje-
kat), 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 pokre-
tan 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 da-
tu 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 razvoj-
n 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 za-
jed 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 ja-
va.Iang i dob iete listu svih klasa koje su deo te biblioteke. Poto je paket java.lang im pli-
citn 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 Print-
Stream. 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 d-
reeno je njegovim tip o m : PrintStreani. P rak tin o sti radi, PrintStream je u opisu p rika-
zan 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 tre-
n 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 Sy-
ste 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, korisni-
ko 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 si-
stem 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 i-
ju . 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 ko-
m 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 za-
h 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 istoi-
m en u hipervezu.

Sintaksa
Sve k o m and e p rog ram a Javadocnalaze se iskljuivo u n u ta r k o m entara /**. K om entar se za-
vrava 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 entacio-
n 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. (Zane-
m 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 en-
ta 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 jav-


ne 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 for-
m 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 pregle-
d 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 a-
tira n je koda, kao to je:

// : object/Documentation2.java
j
* <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 a-


tirate 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 zve-


zdice 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 generi-
sati 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 in-
d 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 ko-
m 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 izu-
zetka 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 de-
finisana, a o p is (koji m oe da se p ro teg n e na nekoliko redova) pokazuje zbog ega odre-
eni 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. Prevodi-
lac e vas upozoriti ako koristite m e to d u oznaenu sa @deprecated. U Javi SE5, @depreca-
ted 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 MDT 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 da-
toteke (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 obli-
ku, (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 prikaza-
nim 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 vi-
tiastih 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 o-
glavlja 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 sta-
vljene 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 SamoPoda-
ci 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 taci-
ja2.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. Iz-
dvojite 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 do-
dajte 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 zap-
nete 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 na-
vedena 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. Ko-
ristei 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 lo-
kacije 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 na-
redaba 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 pera-
to 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 ope-
rato 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 po-
drava 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 ope-
rato 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 iz-
riito 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 ni-
zova. 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 iz-
raun 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 t-
n 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 ko-
pirate 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 : 11 + r l.n iv o +
", r2 .n iv o : " + r2 .n iv o );
r l.n iv o = 27;
p r in t("3 : r l.n iv o : " + r l.n iv o +
" , r2 .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 odelji-
vali 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 obje-
kata 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 re-
nih 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 celobroj-
n 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);
i = k / j;
p r in t (" k / j + i);
i = k * j;
p r in t (" k * j + i);
i = k % j;
p r in t (" k % j + 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 : + u );

) /* Is p is :
j : 59
k : 56
j + k : 115
j - k : 3
k / j : 0
k * j : 3304
k % j : 56
j %= k : 3
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 njego-


vom 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 ri-
m 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 iz-
laz 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 a-
enje 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 jedi-
n 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 pri-
m 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. Pre-
fiksno 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 ope-
racija. 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 : 11 + ++i); // Prefiksn o uveanje
p rin t("i+ + : " + i++); // Sufiksno uveanje
p r i n t ( " i : 11 + i ) ;
p r i n t ( " i : 11 + - - 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 sufik-


sni oblik dobijate vred n o st p ro m en ljiv e pre nego to se o p eracija izvri. O vo su jedini ope-
rato 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 e-
d 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 re-
lacija 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, refe-
rence 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 o-
rate 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 equ-
als() do poglavlja Detaljno razm atranje kontejnera, ali u m e u v re m e n u sam o pazite na na-
in 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 Flekije-
vom 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 i n t ( " i >= j je " + (i >= j ) ) ;
p r i n t ( " i <= j je " + (i <= j ) ) ;
p r i n t ( " i == j j e " + ( 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 I I j je " + ( i | ( j ) ) ;
//! p r i n t ( " ! i je " + ! i ) ;
p r in t("(i < 10) && (j < 10) je "
+ ( (i < 10) && (j < 10)) ) ;
p r in t (" (i < 10) j | (j < 10) je "
+ ( (i <10) || ( j < 10)) ) ;
}
} /* Is p is :
i = 58
j = 55
i > j je true
i < j je fa ls e
i >= j je true
i <= j je fa ls e
i == j je fa ls e
i != j je true
(i < 10) && (j < 10) je fa ls e
(i < 10) || (j < 10) je fa ls e
* ///:-

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 jezici-
m 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 govara-
jui 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 iz-
rau 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 ne-
ta 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 ispi-
suje 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 re-
zu ltat false. Poto to znai da e i rezultat celog izraza sigurno biti false, zato nastavljati iz-
raunavanje 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 pri-
javiti greku. U g o rn jem p rim e ru o b ra tite p an ju na m aksim alne m ogue heksadecim al-
ne 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 o-
delu. (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 pri-
kazivanje 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 FOR-
T 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 eksponenci-
jalno 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 pre-
vie 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 ai-
ni 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 Bau-
d o to v kod koji nije p o d r/av ao m ala slova. Slovo E je u eksponencijalnoj notaciji uvek pisano kao ve-
liko 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 nota-
ciji, 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 ple-
m 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 vred-
nosti 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 isklju-
ivo 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 na-
kon 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 neo-
znaen 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 era-
to 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 era-
ciju 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 11 = 9223372036854775807L;
pri ntBi naryLong( " n a jv e i", 11);
long 11n = -9223372036854775808L;
p rin tB i naryLong("najmanj i " , 11 n ) ;
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 5", 1 5 );


p rintBin aryLo ng (" (-1) 5" , (-1) 5 );
p ri ntBi naryLong(" 1 > 5", 1 > 5 );
p rin tB in aryLo n g (" (-1) > 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 al-
n 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. Rezul-
tate 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 vred-


nost operacije. Ako je v red n o st logikog-izraza false, izraunava se vrcdnostl i njen rezul-
tat 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 ko-
rienju 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 r-
nog 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 prc-
klapnnje 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 reali-
zovalo na Javi nego na C ++-U (to je pokazano u jeziku C# koji inui jedn ostav n o prekla-
panje 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 zna-
kovne ekvivalente i nadovezati ih, u m esto da ih p rv o sabere. D ruga n a re d b a p rin t pretvo-
rila 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 pozi-
vam 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 oe-
kuje 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 pro-
gram . 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 ve-
rovatno 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 do-
delite 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 stra-
ne 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 ri-
m 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, su-
vina 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 iz-
riito zahtevati jer novi tip m oe da uva z n a tn o vie in fo rm acija nego stari, pa se infor-
m 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 e-
ra 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 nave-
d 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 : 0
(in t)is p o d : 0
(in t)fiz n a d : 0
(in t)fis p o d : 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 ): 11 + 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 a-
p 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 ci-
p 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 flo-
at 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. Pro-
gram 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 a-
m 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 veli-
ine, na svim ra u n arim a. Na ovom nivou ne razm iljajte o prenosivosti - o n a je u g ra e-
na 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 o-
reni 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 = ~y;
x = x & y;
x = x | y;
x = 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 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 o p e ra to ri:
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 + y );
x = (c h a r)(x - y );
x++;
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 = 1 ;

X > = 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 += 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 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 /= y;
x %= y;
x = 1;
X = 1;
x >= 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 * 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 M 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 /= y ;
x %= y ;
x = 1;
x = 1;
x >= 1;
x &= y ;
x "= y ;
x 1= 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 / 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 M y ) ;
/'/ Operatori nad bitovim a:
//! x = -y;
//! x = x & y;
//! x = x | y;
//! x = 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 -= 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 = -y;
//! x = x & y;
//! x = x | y;
//! x = 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 vred-


n 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). Ekspli-
citn 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 = 11 + v e l i k 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 kon-
vertovani 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 sua-
vanja 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(). Po-
zovite 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 Ja-
vini 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, do-
while, 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 vr-
ste 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 izvra-
vanja. 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 ra-
tite 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 pretvo-
riti 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 vred-
n 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 rezervi-
sana 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 za-
vrava.

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 vred-
n 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 iz-


vravanja 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 vred-
no 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 vred-
n 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 iz-
vri 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 ne-
taan 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 pr-
vog 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 ci-
klusa (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 de-
finisane 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. Pro-
gram 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 i-
nica 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.to-
BinaryString().

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 na-
vesti 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 kon-
tro 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 kontej-
nere (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 ro-
m 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. Fo-


reach 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 ele-


m 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 ob-
je 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 e-
to 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 e-
tode 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 je-
zicim 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 iskori-
stim 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 oe-
tak 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++) {
i f ( 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 + 11 " ) ;
)
S y s te m .o u t.p rin tln ();
// Upotreba foreach sin tak se:
fo r ( in t i : range(lOO)) {
i f ( 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;
i f ( 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 re-
kidno. 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. Po-
red 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 ge-
nerie 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 o-


sred 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 );
i f ( i = 2) {
p r i n t ( " n a s t a v i" ) ;
conti nue;
}
i f ( 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++) {
i f ( 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 = 11 + 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 na-
lazi 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 (izraz) {
case vrednostl : naredba; break;
case vrednost2 : naredba; break;
case vrednost3 : naredba; break;
case vrednost4 : naredba; break;
case vrednost5 : naredba; 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 izra-
za 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 na-
redbe 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 izvra-
vanja, ali je za nju p o treb an izraz koji im a v rednost tipova kao to su int ili char. Sa n ared-
b 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 + " , 11 + 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 o-
lu 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 kori-
ste 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 a-
red 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 iz-
vrilo 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 ekspli-
c 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 ar-
g 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 i-
zvoljnim 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 pro-
gram 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 bi-
bltoteke, 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 zadr-
avaju 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 e-
a 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. con-
structor). 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 ko-
risti 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 inici-
jalizovan 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 godi-
n 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 pret-
h 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 defi-
nisanja 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 obi-
no 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 ispisi-
vanje 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: kon-
struk 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 ita-
njem 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 neop-
h 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 visoko 0 m
Drvo j e visoko 0 m
pre klo pljena metoda: Drvo j e visoko 0 m
Pravimo novo Drvo koje j e visoko 1 m
Drvo j e visoko 1 m
pre klo pljena metoda: Drvo j e visoko 1 m
Pravimo novo Drvo koje j e visoko 2 m
Drvo j e visoko 2 m
pre klo pljena metoda: Drvo j e visoko 2 m
Pravimo novo Drvo koje j e 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 ni-
ta 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 raz-
likuje 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(short x) { printnb("fl(short)"); }
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 f2 (b y te x) { p r i n t n b ( " f 2 ( b y t e ) " ) ; }


void f 2 ( s h o r t x) { p r i n t n b ( " f 2 ( s h o r t ) " ) ; }
void f 2 ( i n t x) { p r i n t n b ( " f 2 ( i n t ) " ) ; }
void f2(lo ng x) { p r i n t n b ( " f 2 ( l o n g ) " ) ; }
void f 2 ( f l o a t x) { p r i n t n b ( f 2 ( f l o a t ) " ) ; }
void f2(double x) { p r in t n b ( " f 2 ( d o u b le ) " ) ; }

void f3 ( s h o r t x) { p r i n t n b ( " f 3 ( s h o r t ) " ) ; }


void f 3 ( i n t x) { p r i n t n b ( " f 3 ( i n t ) " ) ; }
void f 3 ( 1ong x) { p r i n t n b ( " f 3 ( l o n g ) " ) ; }
void f 3 ( f 1oat x) { p r i n t n b ( " f 3 ( f l o a t ) " ) ; }
void f3(double x) { p r in t n b ( " f 3 ( d o u b le ) " ) ; }

void f4(int x) { pr in tnb("f4(int) " ) ; }


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

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 e-
todi? 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 f l( c h a r x) { p r i n t ( " f l ( c h a r ) " ); }
void fl( b y te x) { p r i n t ( " f 1(b y te )" ); }
void f l ( s h o r t x) { p r i n t ( " f l ( s h o r t ) " ); }
void f l ( i n t x) { p r i n t ( " f 1( i n t ) " ) ; }
void fl(lo n g x) { p r i n t ( " f 1(1ong)" ) ; }
void f l ( f l o a t x) { p r i n t ( " f l ( f l o a t ) " ) ; }
void fl(d o u b le x) { p r i n t( " f l ( d o u b le ) " ) ; }

void f2 (ch ar x) { p r i n t (" f2 (c h a r)" ) ; }


void f2 (b y te x) { p r in t( " f 2 ( b y te ) " ); }
void f2 (sh o rt x) { p r i n t ( " f 2 ( s h o r t) " ) ; }
void f 2 ( in t x) { p r i n t ( " f 2 ( i n t ) " ) ; }
void f2(long x) { p r in t( " f 2 ( lo n g ) " ) ; }
void f 2 ( f lo a t x) { p r i n t (" f 2 (f 1o a t) " ) ; }

void f3 (ch ar x) { p r in t( " f 3 ( c h a r ) " ) ; }


void f3 (b y te x) { p r i n t ( "f 3 (b y te )" ) ; }
void f3 (sh o rt x) { p r i n t ( " f 3 ( s h o r t) " ); }
void f 3 ( in t x) { p r i n t ( " f 3 ( i n t ) "); }
void f 3 (1ong x) { p r i n t ( "f 3 (1ong)" ); }

void f4 (ch ar x) { p r i n t ( " f4 (c h a r)" ); }


void f4 (b y te x) { p r i n t( " f 4 ( b y te ) " ); }
void f4 (sh o rt x) { p r i n t ( " f 4 ( s h o r t) " ) ; }
void 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 vred-
nost; 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 e-
to 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 nave-
den. 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 na-


pravili 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, prevo-
dilac 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 o-
zovete 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 ,,a-
ljete 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 ide-
ju 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 po-
zvana. 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, obra-
zlaui 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 po-
sejan 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 :
M1ja c
* ///:-

Jabuka zove Ijustac.ljusti(), sp o ljn u m e to d u koja obavlja o p eraciju koja, iz nekog raz-
loga, 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 e-
todi, 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. Po-
to 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 t-
ku 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 ta-
koe 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 refe-
renca 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 a-
posletku, kom e treba da isti m em o riju koju zauzim a p rom enljiva tipa int? Ali u bibliote-
kam 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 o-
trim 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 e-
ljenu 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 saku-
pljanja 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 iz-
m 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 eksplicit-
n 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 saku-
pljaa 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 i-
enja 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, celo-
k 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, e-
m 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 pro-
gram vie ne koristi. Z bog toga svaka aktivnost koja je povezana sa sakupljanjem sm ea, p o-
sebno 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 po-
sebnim sluajevim a, kada va objekat zauzim a m em o riju na neki dru g i nain, ne pravlje-
njem 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 pozo-
vete 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 lo-
kalnih 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 fi-
nalize()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": Fina-
lizatori 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 ko-
ristite 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 i-
e, 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 de-
s 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 e-
stane 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 na-
p rav ite zasebne funkcije za ienje i da ih eksplicitno pozovete. Z ato se ini da je finali-
ze() 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 oslo-
b 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 zatvo-
ri 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 fi-
n 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 (w w w.artima.com) tokom naeg sem inara.
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 predstavlja-
lo 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 izvra-
v 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.fi-
nalize(). U ovom sluaju, taj red je pretvoren u k o m e n ta r zato to zahteva o b ra d u izuze-
taka, 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 finali-
ze() 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 ra-
vljenja 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 za-
tim 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 ina-
m 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 stra-
nica - 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 erfor-
m 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 dina-
m 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 odelji-
vanje 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 - bro-
janje 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, vo-
enje 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 o-
referencirajuih 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 jed-
noj 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 refe-
rencu koja se nalazi bilo na steku ili u statin o m skladitu. Lanac veza m oe ii kroz neko-
liko 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 orefe-
rencirajuim 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 poza-
d 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 di-
nam 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 rij-
skog 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 vir-
tuelne 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 obje-
kat, 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 zau-
zim 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 frag-
m 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 i-
ni, 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 ri-
teta, ali se ispostavilo da u ran ijim verzijam a S unove JVM saku pljanje sm ea nije realizo-
vano 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 stani-
i-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. Sa-
kuplja 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 osta-
lim blok ov im a b roja p roizvodnje se uveava ako su o d n e k u d referencirani. N a ovaj n a-
in 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 sta-
ni-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 re-
vodilac 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 na-
pravite 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 o-
vijim razvojnim o k ru en jim a (JDK) p o stu p aju slino, jer o p tim iz u ju svaki deo koda sva-
ki 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 ko-
rienja. 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 a-


ravno, 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 inicijalizaci-
ju , 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");
p r i n t ( boolean " + t);
print("char [" + c + "] " ) ;
prin t("b y te " + b);
print("short " + s)
print("int " + i)
pri nt("1ong " + 1)
p rin t("flo a t + f)
print("double " + d);
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 I n i c i j a l n a vrednost
boolean fa ls e
char [ 1
byte 0
short 0
in t 0
long 0
flo a t 0.0
double 0.0
reference null
* 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 opa-
snost 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 refe-
renca 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 ri-
m 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 et-
ne 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 o-
biete 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 lano-


vi 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 t-
sku 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 sigur-
no 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 defini-
sane. 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 inici-
jalizuje 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 ini-
cijalizacija 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 na-
pravljeno. 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 ne-
statinih 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 Kre-
denac 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 inicijali-
zacija statin ih objekata.
Prvo se inicijalizuju statin i elem en ti, ako to ve nije u in jen o , a zatim nestatin i. D o-
kaz 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 poet-
ka 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 e-
n 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 inicijalizu-
ju 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 ( ) ; / / (2)
/ / s t a t i c S o ljic e s o ljic e 2 = new S o l j i c e ( ) ; / / (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 ri-
stu 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 inici-
jalizovani, 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 uklo-
njen 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 sintak-
su, 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 ne-
dostaje 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 kon-
s 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 ope-
ratora 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 a-


niju 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 ko-
rien 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, inicijali-
zacija 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 raa-
vaju 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 ze-
tak) 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 ti-
pova 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 va-
n 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 In-
te 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 = 11 + 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 ra-
vanje 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 ar-
g 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 ispi-
suju 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 re-
ferenci.

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 ne-
p 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 ra-
zum ljiv ispis, ali o bjekti ovde n ap rav ljen ih klasa tam p aju im e klase, znak @i heksadeci-
m 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 lji-
ve 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 ele-
m 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 di-
lac 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 se-


lektivno 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 izgle-
da 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 pre-


voenje 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 reklapanje-
P 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 e-
kada 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, MNOGO, 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 r-


ci 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 ri-
m 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 ordi-
nal() 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 MNOGO:
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) {
P1jeskavic 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 ja-
nih tip o v a u Javu SE5, trebalo je uloiti dosta tru d a za pravljenje ekvivalentnog n a b ro ja-
nog 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 e-
to 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 ra-
m 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 nepravil-
n 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 sa-
kuplja 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 si-
g 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 po-
boljaie 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 si-
tuacijam 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 ga-
rancija ko n stru k cije vai i kada p rav ite nove klase kom pozicijom ili nasleivanjem , p o treb-
n 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 je-
dan 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 al-
nog 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 re-
alizaciju 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 (poseb-
no 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. (Addi-
son-W esley, 1999). Povrem eno se neko pobuni protiv po n o v n ep o d ele na proste faktore, sa argum en-
tom 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. Ar-
rayList 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 je-
ste 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 sto-


rim 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 ena-
ta) 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 napi-
sao. 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 pro-
stora 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 je-
dinici 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 ri-
lian 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 na-
pravili 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 jed-
nu klasu koja je javna i proizvoljan broj n ejavnih klasa, pa za svaku izv o rn u d ato tek u p o-
stoji 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 na-
lae 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 do-
stu 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 je-
d 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 a-
cuje 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 ra-
u 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 di-
rek 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 ra-
znim 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 global-
no im e za m oje klase. (N astavci com , edu, org itd. ranije su u Javinim pak etim a pisani ve-
likim 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 ko-


m 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 pre-


traivanje.
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 zva-
la 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 sta-
vite 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 retra-


uje 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 razvoj-
no 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 bi-
ste 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 ret-
postavim 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 eu-
tim , 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 Vec-
tor, 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 ne-
to 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 za-
ista 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 Sy-
stem.out.println() koji sm o koristili da bism o sm anjili koliinu teksta koji m o ram o da pie-
m 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 ce-
lih 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 ne-
kog 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 ogu-
n 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 pre-
vodi, 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 re-
nosiv 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 ko-
risti 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 et-
o 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 CLAS-
SPATH. 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 . Stavlja-
ju 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, naui-
ete 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 (katka-
da 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 or-
ganizujete 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 za-
tienim , 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 at-
n 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 ri-
stu 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 ri-
stu p i sutinski je znaajan za zrn a Jave, kao to ete videti u poglavlju Grafika ko-
risnika 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 sta-
vim 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 olacic11) ;
}
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 CLAS-
SPATH. 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 za-
g 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 ri-
stu 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 direktori-
ju 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 priva-
te 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 lanovi-
m 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 o-
slui: 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 pre-
d 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. (Pseu-
d 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, po-
m irite se s tim da ne m o ra te da razu m ete ovaj deo da biste m ogli da nastavite d o naslei-
vanja (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. protec-
ted) 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 za-
grizi() 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 dato-
teci 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 re-
zultat 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 o-
ete 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 jav-
n 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 kori-
snik 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 void p r iv l( ) { / * . . . * / }
private void priv2( ) { / * . . . * / }
private void priv3( ) { / * . . . * / }
private 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 u-
m 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 ta-
cije 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 rb i-
blioteke 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 te-
ke 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 ne-
m 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 izvr-
ava 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, izosta-
vite 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 e-


toda 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 re-
ferencu 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 re-
ava 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 obje-
kat 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 ra-
a 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 na-
ziva 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 deklara-
ciju, 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 lano-
vim 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 Upra-


vljacVezama 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 esta-
ne 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 bibli-
o 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 la-
n 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 pri-
stu 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 la-
avanjem 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 kli-
jen 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 re-
k 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 , naj-
vaniji 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 eki-
p 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 i-
kacija, 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 uzi-
m 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. inheri-
tance) 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 ne-
koliko 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 slo-
enih 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 + " 11 +
"ventil3 = " + ventil3 + " " +
"ventil4 = " + ventil4 + "\n" +
"i = 11 + i + " " + "f = " + f + " " +
"izvor = " + izvor;
}
public static void main(String[] args) {
Prskalica prskalice = new Prskalica();
System.out.println(prskal i c e ) ;
}
} /* 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 iz-
razu 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 Sy-
stem.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 i-
ite 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 Sveje objckat. M e u tim , reference na objekte ini-
cijalizovane 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 le-


nja 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 = 11 + 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 o-
stoji g arancija da e objekat ili referenca biti inicijalizovani p re nego to im poaljete p o-
ru 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 inicijalizo-
vana 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 nasle-
dite 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 era-
to 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 ukla-
njate; 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 ko-
m 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 specifi-
k 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 pa-
keta 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 kori-
enje 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 defi-
nisan 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 rezervi-
sana re super oznaava natk lasu (engl. superclass) koju tek u a klasa nasleuje. Stoga n a-
redba 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 je-
kat 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 po-
stoji sam o jedan nain da se to i garantuje: da se inicijalizacija obavi u k o n stru k to ru p o-
zivanjem 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 kla-
sa 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 po-
zvati 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 odgova-
rajuu 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 re-


vodilac 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 iz-
vedene 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 kon-
stru 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. Re-
cim 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 klase Obicaj
Konstruktor klase Pribor
Konstruktor klase Kasika
Konstruktor klase Pribor
Konstruktor klase Viljuska
Konstruktor klase Pribor
Konstruktor klase Noz
Konstruktor klase Tanjir
Konstruktor klase TanjirZaVeceru
Konstruktor klase PostavkaStola
* ///:-

Iako vas prevodilac prisiljava da inicijalizujete osn ov ne klase i zahteva da to u in ite od-
m 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 iz-
vorni 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 aktiv-
nosti 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 na-
sleuje 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. Poje-
dine 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 e-
m 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. Rezer-
visana 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 b-
zira 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 prevo-
dilac 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 i-
enje 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 redosle-
d 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 ve-
ban 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 pre-
klopljeno, 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)11) ;
}
}

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 uvo-
di 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 ri-
m 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 o-
ete 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 ri-
stiti 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 osnov-
no 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 na-
sleivanjem 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 projek-
tim a nekad ho ete da neto sakrijete o d ire jav n o sti, a da ipak izvedenim klasam a dozvo-
lite 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 pa-
ketni 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 + 11 + 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 govara-
ju 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 pozo-
vete tu zatienu m e to d u van paketa i o bjasnite rezultate. Sada n asledite tu klasu i pozo-
vite 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 Instru-


ment. M ed u tim , u m eto d i Duvacki.main(), m eto d a melodija() se poziva uz prosleiva-
nje 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 Instru-
m 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 a-
in.) 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 nasleiva-


nja, pa se to esto zove svoenje navie. Svoenje navie je uvek bezb ed n o , je r se sa o d re-
enijeg 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 iz-
gra 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 nasle-
ivanje 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 dlui-
vanje 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. Sta-
vite 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 Za-
ba, 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 rezervi-
sana 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 ,,kon-
sta 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 eli-
m 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 mogu 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); // OK -- 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 do-


bijaju vrednost prilik o m prevodenja, obe m o gu da se u p o treb e kao k o nstan te p ri prevo-
enju 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 nagla-
silo 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 pre-
voenja. 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 ra-
zlika izm eu statin ih i nestatin ih finalnih vrednosti. Razlika se javlja sarno kada se vred-
nosti 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 re-
zultata 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 vi-
deti 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 raz-
like 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 slua-
jevim 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 ri-
mer, 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 argu-
m 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 refe-
renca 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 poda-
taka 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 projekt-
n ih razloga, kada elim o da b u d e m o sigurni kako e p on aan je m etod e biti neprom enje-
n 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 pre-
th o d n im realizacijam a Jave, kada biste m eto d u proglasili finalnom , dozvoljavali ste pre-
vodiocu 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 fi-
n 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 (ko-
ja 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 ()" ) ;
}
}

1 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 privat-
na, o n a nije deo interfejsa o snovne klase. O na je sam o neki kod koji je sakriven u n u ta r Ida-
se 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 pri-
v 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 e-
to d u redefiniete.

Finalne klase
Kada ozn aite da je cela klasa finalna (stavljajui rezervisanu re final pre njene definici-
je), 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 bezbed-
n 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 final-
na 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 final-
n o m . M oda p om islite da ionako n em a anse da iko ikada reefinie vae m etode. Pone-
kad 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 kori-
stiti, 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 ne-
kako 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 gle-
dita. 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 Paralel-
no 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 ere-
nije. 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. (Vi-
deete 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 e-
n 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 a-
kon toga sledi inicijalizacija, a zatim o tp o in je p ro g ra m . P roces inicijalizacije u tak v im je-
zicim 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 Bu-
ba.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 ini-
cijalizacija 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 sve-
dena 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 delegi-
ranje), 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 flek-
sibilnija; 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 objekt-
no 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 pogla-
vlju 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 (izvede-
nih 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 ri-
m 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 naslei-
vanja 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 ra-
vim 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 In-
strument, 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 ); // Nema 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 Instru-
ment 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 ar-
g 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. Rezul-
tat e b iti poziv m eto d e Duvacki.sviraj(). D o b ijen je eljeni rezultat, ali ne izgleda logi-
n 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 re-


ferenca 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, kori-
sno 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 vezi-
vanje 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 prevo-
dilac 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 izvra-
vanja (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 a-
nizam 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 ne-
kakva 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 i-
no 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 vezi-
vanje 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 vei-
ni 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 vezi-
vanja, 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 isprav-
no 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

Identifikator nacrtajd nacrtaj()


objekta Krug 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 izvede-
n 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 e-
tod 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 na-
red bi return obavlja se svoenje navie, p ri em u se referenca n a Krug, Kvadrat ili Tro-
ugao 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 pozi-
v 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, sva-
ki 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 o-
ru 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 c-
to 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 e-
nite 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 nk-
c 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 pri-
hvatile 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 re-
alizacije 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 " + n ); }
String s ta () { return "DrveniDuvacki"; }
}

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 kla-
se, 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. Po-
lim 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 to-
StringO 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 iz-
vedenim 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 e-
tode. N asledite tu klasu i redefiniite d ru g u m eto d u . N apravite objekat izvedene klase, sve-
dite 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 r-
fno. 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 i-
jaju 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 o-


ru 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 po-
ziva 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 is-
pravni. 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 izve-
d en e klase, svi lanovi o snovne kiase kojim a m oete da p ristu p ite prav iln o su inicijalizo-
vani. 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 te-
kueg 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 bri-
n 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 ci-
scenje() (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 it-
no 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 ta-
kode 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 tre-
ba 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 t-
po 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 11 + 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 0
Pravljenje Kompozicija 1
Pravljenje Kompozicija 2
Pravljenje Kompozicija 3
Pravljenje Kompozicija 4
ienje Kompozicija 0
ienje Kornpozicija 1
ienje Kompozicija 2
ienje Kompozicija 3
ienje Kompozicija 4
ienje Deljena 0
* ///.-

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 oeku-
jem 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 dodaj-
Ref(), 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 ro-
verili 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 ro-
jan 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 na-
lazite 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 kon-
stru 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 redefi-
nisana verzija te m etode. Ejekat m oe da b u d e p rilin o neoekivan, zato to se redefinisa-
na 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 spoljanjosti11h ijerarh ije nasleivanja. O n poziva m e to d u izvede-
ne 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 = 11 + 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 Okru-


gliGIif. 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 sle-
dee: 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 a-
vajui 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, ja-
vie 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 obje-
kat 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 izve-
dene 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 {
Zito obradi() { return new Zito(); }
}

class PsenicaMlin extends Mlin {


Psenica obradi() { return new Psenica(); }
}

public class KovarijantnoVracanje {


public static void main(String[] args) {
M1 in m = new M1 i n ( ) ;
Zito z = m. o b r a d i ();
Sy stem.out.println(z);
in = new PenicaMl i n ( ) ;
z = m. o b r a d i ();
System.out.println(z);
}
} /* 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 po-
lim 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 prili-
kom 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 enja-
te 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 razli-
ite klase su nasleene kako bi iskazale razliku u m etod i glum i(), a u klasi Pozornica upo-
trebljava 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 Svemirski-
Brod 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 Kvadrat Trougao

nacrtaj(] nacrtajO nacrtajf)


obrisi(] obrisif 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 savr-
eno 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 nika-
kve d o d a tn e inform acije:

Obrati se Obliku
Poruka Krug, Kvadrat, Linija
relacija ,.je" 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 orfi-
zam 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 na-
in 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) sa-
v 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 svo-
denja navie ne m oete da p ozivate nove m etode:

Obrati se objektu
Korisni deo
klase Koristan 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 kre-
tan 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 in-
terfejs 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 naiz-
gled 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 izvra-
vanja (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 v () {}
public void w() {}
}

public class RTTI {


public static void main(String[] args) {
Koristan[] 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 Kori-


stan. 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 izu-
zetak 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 o-
sveeno 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 klasa-
m 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 re-
zultate. 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 ri-
jen 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
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,
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.

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 gre-
ka, 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 ne-
m 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 re-
ko 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 o-
kua 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 ap-
stra 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 ri-
siliti 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 redefi-
nisati u izvedenoj klasi. R eefinisana verzija te m eto d e treba da ispisuje v red n o st celo-
b 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() poka-
ite 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 obezbedu-
jete 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, li-


ste 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 sva-
ki 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 rezervisa-
n 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 inter-
fejsom , 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 in-


terfejs, 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 in-
terfejsa 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);
}
public String t o S t r i n g O { return "Duvacki"; }
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);
}
public String t o S t r i n g O { return "Udaraljke"; }
public void nastimuj() { print(this + ". nastimuj ()"); }
}
class Zicani implements Instrument {
public void sviraj(Nota n) {
print(this + " . s v i r a j O " + n);
}
public String t o S t r i n g O { return "Zicani"; }
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) {
// Svoenje navie prilikom dodavanja u niz:
Instrument[] orkestar = new {
new Duvacki ();
new Udaralj k e ( ) ;
new Zicani ();
new LimeniDuvacki ();
new DrveniDuvacki ();
};
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 Ob-
ject, 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 obi-
noj 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 Instru-
m 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 Br-
zaHrana (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 zajed-
nike 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 eklara-
ciju m eto d e sviraj( ) prebacite iz interfejsa Instrument u MozeDaSvira. Izvedenim klasa-
m 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 pie-
te 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 odifi-
kuju 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) {
// Argument metode split() je graninik po kojem se znakovni niz deli
// na sastavne delove:
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 ar-
g 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 ob-
jekata 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 iz-
m ed u Primeni.obradi() i Procesora jaa je nego to je p o tre b n o , a to spreava k o d iz Pri-
meni.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 elek-
tron 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 a-


te - 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 ri-
sno, 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 sva-
ka 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 inter-
fejsa ne javljaju p ro b lem i koje sm o susretali u C + + -u :

Apstraktna ili konkretna i Interfejs I


osnovna klasa A

! ..........A ........
i i Interfejs n i
: : ............A .......

Funkcije osnovne klase Interfejs 1 Interfejs 2 ... 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" (kla-


sa koja n em a a p strak tn e m eto d e). Ako n asleujete neto to nije interfejs, m oete da na-
sledite 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 w(AkcioniJunak x) { x.boriSe(); }
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
}
} ///:-

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 interfej-


sim 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 in-
terfejse. (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 e-
toda 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 kla-
se. 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 kli-
je 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 r-
fejse 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 u n is ti() {}
public void ubij() {}
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 rea-
lizovan u klasi Zmaj.
Sintaksa u p o tre b ljen a za interfejs Vampir m oe da se koristi sa n io kada nasledujete in-
terfejse. Uz rezervisanu re extends m o ete da u p o treb ite sam o jed n u klasu, ali poto in-
terfejs 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 in-
terfejs iz ta tri uz dod av an je jo je d n e nove m etode. N apravite klasu realizujui novofor-
m 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 t-
h 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 ti-
n 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 rea-
lizacija 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 ri-
hvata 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 ko-
jim o b jek to m koji je usaglaen s m o jim interfejsom ". T im e vaa m e to d a po staje fleksibil-
nija, 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 kla-
som 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 char[] samoglasnici =
"aei o u " .to Ch arArray();
private int brojac;
public SlucajneReci(int brojac) { this.brojac = 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)]);
}
cb.append(" ");
return 10; // Broj dodatih znakova
}
public static void m a i n ( S t r i n g [] args) {
Scanner s = new Scanner(new S1 uc a j n e R e c i (10));
w h i 1e ( s .ha sN ex t())
System .o ut.prin tl n( s.n e x t ());
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(), argu-
m 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 ret-
n 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) {
S1ucajniDoubles sc = new S1uc aj ni Do ub le ();
for(int i = 0; i < 7 ; i ++)
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()) + 11
cb.append(rezultat);
return rezultat.length();
}
public static void main(String[] args) {
Scanner s = new Scanner(new Pr il ag odjeniSlucajniDouble(7));
while(s.hasNextDouble())
System.out.print(s.nextDouble() + " ");
}
} /* 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 ova-
kav 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 t-


ski 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 ini-
cijalizovana 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 t-
k 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() { return new DImp2(); }
private D dRef;
public void primiD(D d) {
dRef = d;
dRef.f();
}

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[] args) {
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( ));
}
} ///:-
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 et-
nete 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 (od-
nosno , 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. Pra-
vila 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 in-
terfejse 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 proizvod-
n 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
public void metodal() {print("Realizacijal me to da l1');}
public void metoda2() {print("Realizacijal metoda2");}
}

class RealizacijalProizvodjac implements ProizvodjacUsluga {


public Usluga uzmiUslugu() {
return new Realizacijal();
}
}
class Rea1izacija2 implements Usluga {
Realizacija2() {} // Paketni pristup
public void metodal() {print("Realizacija2 metodal");}
public void metoda2() {print("Realizacija2 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) {
korisnikUsluge(new RealizacijalProizvodjac ());
// Realizacije su potpuno zamenljive:
korisnikUsluge(new Realizacija2Proizvodjac ());
}
} /* 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 in-
terfejsa 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 pri-
m 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() {
print("Dama potez " + p o t e z ) ;
return ++potezi != 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() {
print("ah potez " + potezi);
return ++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 ret-
p 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 , in-
terfejse 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 o-
d 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, na-
ro ito ne isprva. Z ato e vam p o etn i delovi poglavlja zasa verovatno biti dovoljni, a de-
taljnije 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 Spolj-
na 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 ini-
cijalizujte 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 a-
pravite 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 na-
pravila, 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;
public Sekvenca(int velicina) {obs = new O b j e ct [v el ic in a]; }
public void dodaj(0bject x) {
if(sledeci < obs.length) {
obs[sledeci++] = x;
}
private class SelektorSekvence implements Selektor {
private int i = 0;
public boolean kraj() {return i == obs.length;}
public Object tekuci () {return obs[i];}
public void sledeci() {i f (i < obs.length) 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 sakri-
vanje 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()) {
Sy st em.out.println(selektor.tekuci() + " ");
sele kt or .s le de ci();
}
}
} /* 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 obje-
k 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 na-
in, 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 o-
Ijima 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 za-
je 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 inici-
jalizuje 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 (); }
public static void main(String[] args) {
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[] args) {
TackaNova dn = new TackaNova();
TackaNova.Unutrasnja dni = dn.new Unutrasnja();
}
} ///= -

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 o-
v 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 in-
stan 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 osnov-
noj 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 tra-
nja 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[] args) {
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();
}
} 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 oba-
vite 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, proiriva-
nje 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 njego-
vom 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 o-
n 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 POd-
rediste 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 Odredi-
ste. 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 poddi-
rek 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 proiz-
voljne 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 in-
terfejsu. 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 sve-
dete 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() { // Umetanje definicije klase
private int i = 11;
public int vrednost() { return i; }
}; // U ovom sluaju je potrebna taka i zarez.
}
public static void main(String[] args) {
Posiljka7 p = new Posiljka7();
Sadrzina c = p . sa dr ();
}
1 ///:-

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 Sa-


drzina 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; }
}
public Sadrzina sadrzina() { return new Mo ja Sa dr zi na (); }
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 a-


n 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[] args) {
Posiljka8 p = new Posiljka8();
Omotac w = p . om ot (l O);
}
} ///:-

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 gor-


njem 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 za-
nim 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() {
private String oznaka = odr;
public String procitajOznaku() { return oznaka; }
};
}
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) {
{ print("Unutar inicij a l izatora instance"); }
publ ic void f () {
print("U anonimnoj f()");
}
};
}
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 osnov-
n 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 rte 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 efek-
tiv 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 je-
d 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 e-
vani. 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() {print("Realizacijal metodal");}
public void metoda2() {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() {print("Realizacija2 metodal");}
public void metoda2() {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 je-
d 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;
}
public static Proizvodjaclgara proizvodjac ;< I n d jacIg araO {
public Igra uzmilgru() { return new Danu: (
};
}
class Sah implements Igra {
private Sah() {}
private int potezi = 0;
private static final int POTEZI = 4;
public boolean potez() {
print("Sah potez " + potezi);
return ++potezi != POTEZI;
1
public static Proizvodjaclgara proizvodjac = nt'V I /(djacIgaraO {
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! pr r"ite klase, a ne interfejse.


U k oliko projekat zahteva interfejs, to e v am biti i.jte interfejse ako ba ne
m o rate.
V eba 16: (1) P repravite reenje vebe 18 iz po^lavlj; ristei a n o n im n u u n u -
tra n ju klasu.
V eba 17: (1) P repravite reenje vebe i 'j iz poj-ia ;oristei a n o n im n u u n u -
tra n ju klasu.
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 obi-
ne 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() { return oznaka; }
// Statina unutranja klasa moe da sadri druge statine elemente:
public static void f() {}
static int x = 10;
static class DrugiNivo {
public static void f() {}
static int x = 10;
}
I
public static Odrediste odr(String s) {
return new POdred is te (s );
}
public static Sadrzina sadr() {
return new PSadrzina();
}

2 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 la-
n ov im a kao u Javi.
Poglavjje 10: Unutranje klase 283

public static void main(String[] args) {


Sadrzina c = s a d r ( ) ;
Odrediste d = od r( "T an za ni ja ");
}
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 refe-
rence 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[] args) {
new Te st () . z d r a v o ( ) ;
}
}
} /* 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 sta-
ti 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[] args) {
ZaTestiranje t = new Z a Te st ir an je ();
t.f 0 ;
}
}
} /* 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 go-
tov 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() {}

1 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 f()


m o g u d a b u d u po zv an e b ez ika-
kvih 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.

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 rea-
lizacijam 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, efek-
tiv 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 na-


in 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 realizaci-
je, 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[] args) {
Z z = new Z ( ) ;
uz i m a D ( z ) ;
uzimaE(z.napraviE());
}
} ///:-
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 rea-
lizuje 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 spo-
ljanje 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 vi-
deli, 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 {
public void povecaj() { print("Druga operacija"); }
static void f(MojaPovecaj mp) { m p . p o v e c a j (); }
}

// 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, inae ete dobiti
// beskonanu rekurziju:
Pozv an a2 .t hi s. po ve caj();
}
}
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) { referencaZaPovratniPoziv = cbh; }
void kreni() { referencaZaPovratniPoziv.povecaj (); }
}

public class PovratniPozivi {


public static void main(String[] args) {
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 oe-
m 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 ra-
vljenjem 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 Mo-
zeDaSePoveca. 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 re-
ferencu 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 ka-
snije, 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 ri-
likom 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 projek-
to 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 ri-
lagoava 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 Pat-
terns (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 si-
stem 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 t-
p 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 ko-
risn 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 sa-
dri 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 naslei-
vanja, 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) {
this.vremeKasnjenja = vremeKasnjenja;
st a r t ( ) ;
}
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 e-
lite 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 spre-
man( ), n arav n o , m oe da b u d e redefinisana u izvedenoj klasi, kako bi izvravanje d o-
gaaja 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, dovolj-
no 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 Doga-
jaj 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 listu
// dok birate elemente iz nje:
for(Dogadjaj d : new Arra yL is t< Do ga dj aj >(lis t aD og ad ja ja ))
292 Misliti na Javi

if(d.spreman()) {
S y s t e m . o ut .p ri nt ln (d);
d.akcija();
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 spre-
man( ) za p o k re tan je . Z a svaki Dogadjaj koji je spreman( ), isp isuju se p o d aci objek-
tov 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 is-
k o ristim svoj te rm in , vektor p ro m e n e p red stav lja razliite akcije razliitih v rsta objeka-
ta 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 kla-
se 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) { s u p e r( vr em eK as nj en ja); }
public void akcija() {

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 pali svetlo.
svetlo = true;
}
public String t o S t r i n g O { return "Svetlo je ukljueno"; }
}
public class IskljuciSvetlo extends Dogadjaj {
public IskljuciSvetlo(long vremeKasnjenja) { super(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) (super(vremeKasnjenja);}
public void akcija() {
// Ovde treba da bude kod za upravljanje hardverom.
voda = true;
}
public String toString() { return "Voda u stakleniku je putena"; }
}
public class IskljuciVodu extends Dogadjaj {
public IskljuciVodu(long vremeKasnjenja) { su pe r(vremeKasnjenja); }
public void akcija() {
// Ovde treba da bude kod za upravljanje hardverom.
voda = false;
}
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() {
// Ovde treba da bude kod za upravljanje hardverom.
termostat = "No";
}
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 "; }
}
1 / / / =-

P rom enljive svetlo, voda i term ostat p rip a d a ju sp o ljn o j klasi UpravljanjeStaklenom-
Bastom, 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 go-
tovo kao v iestruko nasleivanje: klasa Zvono im a sve m eto d e kao i klasa Dogadjaj, a ta-
koe 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 UpravljanjeStaklenom-
Bastom 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 doga-


aje. 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 di-
rek 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 ko-
riste 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 Upravljac-
StakleneBaste.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 Upravlja-
njeStaklenomBastom 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 UpravljacStakle-
neBaste.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 spolj-
ne 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 re-
ferenca 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 spolja-


nju. 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 i-
ete 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 oe-
kivati 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 redefini-


e 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 Velikoja-
je2.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 kla-
se 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 ti-


fikatore. 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, gene-
risan 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 na-
in 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 ori-
stite 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 nji-
m a, sve vie ete ih prih v atati.
Reenja odabranih vebi data su u elektronskom dokum entu Thin 1ln Java A n n o ta ted S olu-
tions G uide, koji se moe kupiti na lokaciji www.MindView.net.

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 sno-
vu 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 ri-
stu 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 nekoli-
ko 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 ti-
je 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 ol-
le 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, ko-
ristiu 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 ele-
m 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 asocira-
n 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 objeka-
ta; 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 jed-
na 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 ra-
m 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, Array-
List. 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 tre-
n 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 ,,izu-
zetak 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 Na-
randza. 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 t-
klase 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 objek-
te 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 , iza-
zvaete 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 Javi-
n 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 u-
vanje 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 sta-
vite 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 bez-
b e d a n ra d s tip o v im a .
Poglavlje 11: ujvanje objekata 305

} /* Is p is :
0
1
2
0
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 GreniSm it extends Jabuka {}


c la s s Gala extends Jabuka {}
c la s s Fudzi extends Jabuka {}
c la s s 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 kla-


se 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 inici-
jalizuje 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 nje-
govog 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 pra-
vljenja 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 in-


terfejs 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 e-
tode koje interfejs Map ne sadri. A ko v am tre b a ju te m eto d e, neete m oi d a svedete na-
vie 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 In-
teger 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 + " , 11) ;
}
} /* 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 dgova-
rao bi svaki ob jek at klase nasle en e o d to g in terfejsa, ali ArrayList predstavlja najosno v-
niji 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 ele-
m 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 ele-
m 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 li-
stu 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 ko-
je 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 st-
venu 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 li-
ste 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 Sneg {}
c la s s Prsac extends Sneg {}
c lass Slab extends Prsac {}
c la s s Krupan extends Prsac { }
c la s s Krpe extends Sneg {}
c lass 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 e k s p lic itn im


// zadavanjem tip a argumenta:
List<Sneg> sneg4 = Arrays.< Sneg> asList(
new S 1 a b (), new 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 u-
m 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 sta-
viti ,,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 nji-
ho 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 m a in (S trin g [] args) {
print(popuni(new A rrayList< Strin g> ( ) ) ) ;
print(popuni(new L in k e d L is t< S tring> ( ) ) ) ;
p rin t(p o p u n i(new HashSet<String> ( ) ) ) ;
print(popuni(new TreeSet<String> ( ) ) ) ;
print(popuni(new LinkedHashSet<String> ( ) ) ) ;
print(popuni(new HashMap<String, String> ( ) ) ) ;
p rin t(p o p u n i(new TreeMap<String, String> ( ) ) ) ;
print(popuni(new 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 rea-
lizuje 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 er-
fo 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 a-
ta 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 nje-
govog 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 Linked-
HashMap. 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 e-
tan 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 o-
sled 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 ulda-
n jan je elem enata iz sred in e Liste ne kota m n o g o . LinkedList je relativ no sp o r p ri-
likom 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: [Mouse, Pug]
14: [R a t, Mouse, Mutt, Pug, Cymric, Pug]
15: [R a t, Mutt, Cymric, Pug]
16: [R a t, Mouse, Cymric, Pug]
17: [R a t, Mouse, Mouse, Pug, Cymric, Pug]
18: fa ls e
19: []
20: true
21: [Manx, Cymric, R at, EgyptianMau]
22: EgyptianMau
23: 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 osnov-


n 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 b-
jek 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 e-
to 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 o-
kuaji 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 ak-
le, 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 za-
v 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 na-
su 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 Ar-
rayListe 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 (tak-
va 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 bavi-
ti 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 rezul-
tat 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 con-
tainsAll() ne utie po zivanje m eto d a C ollections.sort() (za so rtira n je ) i Collec-
tions.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 al-
noj, 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 in-
deksa, 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 it-
ne 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 i-
zo 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 je-
d 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 razli-
itim 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 re-
sursa. 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, Ja-
vin 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 stan-
d 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 + 11 " ) ;
}
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 po-
kazuje 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 Ar-
ray 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 (ob-
jekat 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 t-
h 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 listltera-
tor(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(): 11 + 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(): 11 + 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 bi-


sm 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 rea-
Iizacija 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 Inte-
gere 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 era-
cija 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 po-
seb 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 li-
sto 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 e-
taljno 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 spe-
cificiratc 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 eks-
p 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:
,,+ U + n + c t e + r + t + a -+ i-+ n + t+ y ---- 1- - + r + u + l+ e + s

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 po-
n 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 r-
m 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 pro-
nalaenja. 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 ledp 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 dija-
g 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: 11 + 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 na-
stavku 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 iz-
razi 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 sta-
vlja 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 JedinstveneRe-
c 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 iz-
b 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 te-
g 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. obje-
k 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 () ispi-
tu 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 i-
n 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 te-
ku 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 e-
sto 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 re-
d 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 aci-
vanje 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 bez-
b 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 disci-
p 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 Priori-
tyQueue 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 pozi-
van 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 Priority-
Q 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 (Charac-
ter), 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 brojevi-
m 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 o-
kaite 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 se-
kvenci. 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 ra-
viti 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 obje-
kata.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 5:Cymric 6:Pug 7:Manx
4:Pug 6:Pug 3:Mutt l:Manx 5:Cymric 7:Manx 2:Cymric 0:Rat
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 : 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 Collec-


tion. 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 ri-
m 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 AbstractCol-
Iection 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 itera-
t 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 naslei-
van 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 ple-
m e n tira klasa Collection.
Veba 30: (5) Izm en ite p ro g ra m ColIectionSequence.java tako d a ne nasle uje Abstract-
Collection, 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 e-
to 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 Itera-
tor<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 opera-
tivnim 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 tre-
b 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 ite-
ra 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, 10]
Nakon meanja: [4, 6, 3, 1, 8, 7, 2, b, 10, 9]
n iz : [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Pre meanja: [1 , 2, 3, 4, 5, 6, '/, 8, 9, 10]
Nakon meanja: [9, 1, 6, 3, 7, 2, 5, 10, 4, 8j
n iz : [9, 1, 6, 3, 7, 2, 5, 10, 4, 8]
* ///:-

U p rv o m sluaju, izlaz m eto d e A rrays.asList( ) p re d air >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 t-
no, 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 o-
p 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; irogramu-SekvencaBez-
Kolekcije.java d o d a jte m e to d e o b rn u to ( ) i slu ca jn o ( ), a i i SekvencaBezKolekcije
realizujte Iterable, i p o k a ite da u fo reach n ared b am a funkc 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 u. * o b jek tim a; nizovi i liste
m o g u se sm a tra ti u re e n im k o n te jn erim a .
4. U p o treb ite listu ArrayList ako esto k o risfite a ristu p , a LinkedList ako
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 'L ist.
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 ele-
m 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


Proizvodi Proizvodi
... J ... ; 1.......'A........J ""S '"
Listlterator i 1...... 1 ir f
L J 51
/i
C *
J C l Q ueue
i
HashMap TreeMap
Proizvodi

i 1 f
LinkedHashMap
ArrayList LinkedList PriorityQueue
Alatke

HashSet TreeSet Collections

i Comparator ! Arrays
IjnkedHashSet

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.con-
current 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 ra-
vljenje 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 ra-
zmatranje 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 na-
viknete 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 zna-
ti 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 t-
sk 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 savla-
d 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 sti-
lu: ,,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 izne-
n 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 isi-
vanje, 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 te-
kue 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 iz-
b 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 de-
lilac 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 le-
m 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 izu-
zetka 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 refe-
renca 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 izu-
zetaka (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 ri-
stu 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 izu-
zetka (engl. throw ing an exception). Evo k ako izgleda:

i f ( 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 r-


n 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 jezi-
cim 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 ri-
m 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 ideal-
n 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 rezervi-
sanoj 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 su-
vie 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.

J 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 e-
lite 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 e-
to 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 rezer-
visana 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 izuze-


taka, 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 ko-
risti 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 rezervisa-
n 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 zet-
ka. 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 izvrava-
nje 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 po-
vezanost 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 i-
sanje 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 izu-
zetaka 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 bi-
blioteka 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 o-
b 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 izu-
zetak 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 knji-
ge 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 iz-
lazni 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 Sy-
stem .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 izu-
zetka 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, eks-
p 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: printStack-
Trace( ). 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 izu-
zetak 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 ArrayIndexOutOf-
BoundsException (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 ettcr-
Java, 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 Log-
ger 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 in-
terven 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 sopstve-


ne 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 ( ) 11) ;
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 o-
bila 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 kla-
se 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 vi-
e. (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 izu-
zetke. 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 i-
san 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 is-
p 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 izu-
zetka, 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 Ex-
c 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 geo-
grafsk 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 izvrava-
nja 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 vr-
ste 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 ko-
risn 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 Throwa-
ble, 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 e-
to 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 od-
re 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 nje-
gov 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
9
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 aktuel-
n 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 sle-


deem 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 fillln-
StackTrace( ) 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 izu-
zetka. 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). Predvide-
n 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 si-
stem 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 Dinamicka-
Polja 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 \ ") : 11 + 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 NoSuch-


FieldException, 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 re-
zu 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 razvo-
jn 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 sopstve-
ne 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 izu-
zeci 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 ge-
n 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 ta-
v 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 ex-
ceptions). 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 izu-
zetke 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 ge-
neriete 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 specifika-
ciju 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) speci-


jalan sluaj, p oto prevodilac ne zahteva specifikaciju ovakvih vrsta izuzetaka. Izlaz u si-
stem 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 Runtime-
Exception, p o to za o stale izuzetke prevo dilac zahteva o b ra d u . Izuzetak tip a RuntimeEx-
ception 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 e-
to 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 izvr-
avanja 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 le-
tan 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 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-
c n ja o b a v lja ti d e s tr u k to ri.
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 izu-
zeci u Javi n e dozvoljavaju vraan je n a m esto n astan k a izuzetka, kao to je ran ije p o m e-
n 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 izuze-
taka. 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.is-
k 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 fi-
nally 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 ");
i f ( i == 1) retu rn ;
p rintC 'T aka 2 " );
i f ( i == 2) retu rn ;
372 Misliti na Javi

p rin t("T a k a 3 " ) ;


i f ( 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 zahteva i e n je
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 zahteva i e n je
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 zahteva i e n je
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 zahteva i e n je
Taka 1
Taka 2
Taka 3
Kraj
O b avljan je i e n ja
* ///:-

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 e-
njen 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 rezervisa-
ne 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 je-


d 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 Tri-
vijalanlzuzetak 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 e-
to 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 osnov-
n 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 izu-
zetak, 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, Rai-
nedO 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 izu-
zetak 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 osnov-
ne 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 e-
to 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 ge-
nerie izuzetak tip a PopFouI izveden iz izuzetka tip a Foul koji g enerie verzija m e to d e at-
Bat( ) 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 ar-
g 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 UmpireArgu-
m 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 izu-
zetak, 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 kori-
sn 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 izu-
zetak 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 Ja-
vine 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 File-
NotFoundException. 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 citaj-
R 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 de-
lim 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 ra-
dite 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 i-
je 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 fi-
nally 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 izu-
zetke. 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 + 11 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 1 poiena
T r e b a Je P o c is titi 3 poiena
T r e b a Je P o c is titi 2 poiena
T r e b a Je P o c is titi 5 poiena
T r e b a Je P o c is titi 4 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 try-
finally. 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 zah-
tevaju 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 ra-
vljenje 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 ge-
nerie 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 pravil-
n 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 redosle-
d 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 tra-
enje 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 odgo-
v 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 r-
va. 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 kli-
je 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 redefini-
ite 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 iz-
vedite 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 na-
p 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 izu-
zetaka 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 sle-
ili. 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 stavlja-


te 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 ajjed-


n 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 izu-
zetaka, 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 a-
p 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 ko-
risna, 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 ilje-
n 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 # na-
stalim 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 ka-
sno 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 spe-
cifikacije 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 e-


d 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 obe-
shrabruju 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, ko-
liin 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 izu-
zetaka, 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 eri-
sati 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 e-
to 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 in-
spirisani 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 trenut-
ka 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 situaci-
jam a kada se ne m oepreduzeti nita smisleno.9
Kada objanjava zato deklaracija funkcije bez specifikacije znai da ona moe generi-
sati 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 izu-
zecima 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 igno-
risanja 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 11 s jezici-
m 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 re-
vie 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 re-
p 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 il-
nosti - 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 izu-
zetke, 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

'' 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).
12 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 Filelnput-
Stream 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 rosle-
dite 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 aa-
lost, 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 Runti-
meException. 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 ulana-
vanja 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 e-


to 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?11) ;
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: 11 + 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 izu-


zetke 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 throwRuntimeExcep-
tio 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 NekiDrugi-
Izuzetak. N a k raju , hvatate RuntimeException i g en eriete (rezervisan a re throw) re-
zu 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 RuntimeEx-
ception 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 ko-
jim 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 gre-
k 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 ri-
javljiva 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 ra-
d 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 b-


jek at s k o jim je ta referenca povezana ostaje na istom fizikom m estu . Reference se kopi-
raju 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 naz-
vana 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 e-
to 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 o-
vim 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 , ar-


g 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 je-
no. 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 i-
m 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: ldc #2; //String mango
2: astore 1
3: new #3; //class StringBuilder
6: dup
7: invokespecial #4; / / St ri ng Bu il de r. "< ini t> ":()
10: ldc #5; //String abc
12: invokevirtual #6; //StringBuilder.append:(String)
15: aload_l
16: invokevirtual #6; //StringBuilder.append:(String)
19: ldc #7; //String def
21: invokevirtual #6; //StringBuilder.append:(String)
24: bipush 47
26: invokevirtual #8; //StringBuilder.append:(I)
29: invokevirtual #9; / / S t r i ng Bu il de r. to Str in g:()
32: astore_2
33: getstatic #10; //Field System.out:PrintStream;
36: aload_2
37: invokevirtual #11; // PrintStream.println:(String)
40: return

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 ja-
va.lang.StringBuilder. U izv o rn o m k o d u se StringBuilder nije sp o m in jao , ali je prevo-
dilac 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 i-
ran 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[] polja) {
String rezultat =
for(int i = 0; i < pol j a . 1ength; i++)
rezultat += pol j a [ i ] ;
return rezultat;
1
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
7 arraylength
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: iconst_0
9: isto re _3
10: iload_3
11: alo ad _l
12: arraylength
13: if_icm pge 30
16: aload_2
17: alo ad _l
18: iload_3
19: aaload
20: in v o k e virtu a l #5; // S trin g B u ild e r.a p p e n d :()
23: pop
24: iin c 3, 1
27: goto 10
30: aload_2
31: 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 :()
34: 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 nje-
govu 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 ekspli-
citn 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: 11
+ 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 bes-
k 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 e-
to 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 Argumenti, preklapanje Upotreba


Konstruktor Preklopljene. podrazumevani, String. Pravljenje String objekata.
StringBuilder, StringBuffer
char nizovi, byte nizovi.
lengthf) Broj znakova u objektu tipa String
charAtf) int Indeks Znak (char) na zadatom mestu
znakovnog niza.
getCharsf), Poetak i kraj podniza koji treba kopirati, Kopiranje objekata tipa char
getBytes() niz u koji treba kopirati, indeks mesta u ili byte u spoljni niz.
odredinom nizu.
toCharArray() Pravi char(] sa znakovima koje
sadri String
equalsf), String za poredenje. Ispitivanjejednakosti sadraja dva
equalslgnoreCase[) znakovna niza.
Poglavlje 13: Znakovni nizovi 399

Metoda Argumenti, preklapanje Upotreba


compareTo|) String za poreenje. Rezultat je negativan, nula, ili poziti-
van, u zavisnosti od leksikografskog
uredenja Stringa i argumenta. Veli-
ka i mala slova nisu jednaka!
contains)) Objekat tipa CharSequence koji se trai. Rezultat je true ako String sadri
argument.
contentEquals() Objekat tipa CharSequence ili String- Rezultat je true ako se argument
Buffer za poreenje. tano podudara.
equalslgnoreCase() String za poredenje Rezultat je true ako su sadraji jed-
naki; zanemaruje se veliina slova.
regionMatches() Pomeraj od poetka ovog Stringa, drugi Rezultat tipa boolean pokazuje
String i pomeraj od njegovog poetka, i da li se dato podruje podudara.
duina koja se poredi. Preklapanjem se
dodaje zanemarenje veliine slova.
startsWith() String s kojim eventualno poinje. Rezultat tipa boolean pokazuje
Preklapanje dodaje pomeraj (ofset) da li String poinje argumentom.
u argument.
endsVMthJ) String koji bi mogao biti sufiks od Rezultat tipa boolean pokazuje
String da li je argument neki sufiks.
indexOf(), Preklopljen: char char i poetni indeks, Vraa -1 ako se argument ne
lastlndexOf() String. String i poetni indeks. pronae u ovom Stringu;
u protivnom, vraa indeks poetka
argumenta. lastlndexOf( )
pretrauje unazad, od kraja.
substring() (takode Preklopljen: poetni indeks; poetni in- Vraa nov objekat tipa String koji
i subSequence()) deks + zavrni indeks. sadri dati skup znakova.
concat( 1 String koji treba nadovezati. Vraa nov objekat tipa String koji
sadri znakove originalnog Stringa
i zatim znakove argumenta.
replace() Stari znak koji se trai. novi znak s kojim Vraa nov objekat tipa String sa
ga treba zameniti. Moe zameniti ijedan sprovedenim zamenama. Vraa
CharSequence drugim stari String ako ne nade ono to
treba da zameni.
toLowerCase() Vraa nov objekat tipa String
toUpperCase() sa svim malim, odnosno velikim slo-
vima. Vraa stari String ako nema
ta da izmeni.
trimf) Vraa nov objekat tipa String bez
belina na oba kraja. Vraa stari
String ako nema ta da izmeni.
valueOf|) Preklopljen: Object, char[], char[J i po- Vraa String koji sadri tekstualni
meraj od poetka i broj znakova, bool- prikaz argumenta.
ean, char, int. long, float, double
internf ) Pravi tanojednu String referencu
za svakujedinstvenu sekvencu
znakova.
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 a-
tira 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 ti-
ranje 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 ri-
m 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 t-
W 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;

2 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 + " 11 + 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 zna-


kovni 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 ja-
va.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 je na (0,0)
Kornjaa Teo je na (4,8)
Kornjaa Tom je na (3,4)
Kornjaa Teo je na (2,5)
Kornjaa Tom je na (3,3)
Kornjaa Teo je na (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 Sy-


stem.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 sloe-
niji 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 , pre-
ciznost 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 Koi Cena

Dekov arobni 4 4.25


Prin cezin graa 3 5.10
Kaa t r i medved 1 14.29
Porez 1.42

Ukupno 25.06
*///:-

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 je-
d an broj na je d n o m m estu.

Konverzije klase Formatter


Najee ete sretati sledee konverzije:

Znakovi konverzije
d Celi broj (napisan decimalno)
c Unicode znak
b Logika vrednost (tipa Boolean)
40 4 Misliti na Javi

Znakovi konverzije
s Znakovni niz
f Broj s pokretnim zarezom (napisan decimalno)
e Broj s pokretnim zarezom (napisan u naunoj notaciji)
X Celi broj (napisan heksadecimalno)
h 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 : (prim er)
u = 'a '
s: a
c: a
b: tru e
h : 61
v = 121
d: 121
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 izvra-
v 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 argu-
m 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 ) sino-
n 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 00 00 31 00 52 0A 00 05 00 22 07
00010: 00 23 OA 00 02 00 22 08 00 24 07 00 25 OA 00 26
00020: 00 27 OA 00 28 00 29 OA 00 02 00 2A 08 00 2B OA
00030: 00 2C 00 2D 08 00 2E OA 00 02 00 2F 09 00 30 00
00040: 31 08 00 32 OA 00 33 00 34 OA 00 15 00 35 OA 00
00050: 36 00 37 07 00 38 OA 00 12 00 39 OA 00 33 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-iz-
lazni 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 Py-
th 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 StringToke-
nizer. 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 znakov-
n 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 znakov-
n 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, ci-
fra (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 13: 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 al-
na 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 ( 11\\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 - re-


g 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 ozvolje-
n 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 za-
m 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 lja-


vaju 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 ular-
n 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 sa-
m 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 t-
p 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 Znak B
\xhh Znak iji je heksadecimalni kod oxhh
Nuhhhh Unicode znak iji je heksadecimalni kod oxhhhh
\t Tabulator
\n Prelazak u novi red
V Vraanje na poetak tekueg reda
\f Prelazak na novi list
\e Znak Esc (izlaz)

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] Bilo koji od znakova a. b ili c (isto to i alblc)
["abc] Bilo koji znak osim a, b ili c (negacija)
[a-zA-Z] Bilo koji znak od a do z ili od A do Z (opseg)
[abcfhijj] Bilo koji od znakova a,b,c, h,i.j (isto to i alblclhlilj) (unija)
[a-z&&[hij]J Bilo koji od znakova h, i ili j (presek)
\s Bilo koji od znakova za belinu (razmak, tabulator, prelazak u novi red, prelazak
na novi list, vraanje na poetak tekueg reda)
\S Bilo koji znak sem znakova za belinu (fA\s])
412 Misliti na Javi

Znakovi
\d Numerika cifra [0-9J
\D Bilo koji znak sem znakova za cifre [A0-9]
\w Bilo koji znak koji moe biti deo rei [a-zA-Z_0-9]
\W Bilo koji znak sem znakova koji mogu biti deo rei [A\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 X, a potom Y
XIY X ili Y

(X) Grupa koju treba pronai. U nastavku izraza, /tu grupu za hvatanje oznaavate sa \i.

Izrazi za granice
A
Poetak reda

$ Kraj reda
\b Granica rei
\B Granica koja ne moe biti deo rei
\G Kraj prethodno pronadene podudarnosti

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 zna-
kovni 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.

Pohlepni Rezervisani Posesivni Podudara se sa


X? X?? X?+ X, jednom ili nula puta
X* X*? X*+ X, nula ili vie puta
x+ X+? X++ X, jednom ili vie puta

X(r>} X{n}? X{n}+ X, tano n puta


X(n.) X(n.}? X{n,}+ X, najmanje n puta
X{n,mj X{n,m}? X(n,m}+ X, najmanje n puta, ali ne vie od m puta

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 za-
pravo 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 pre-
vedete 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 e-
to 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 na-
vodnike. 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 regular-
nih 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 CharSe-
quence), 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 re-
g 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 zna-
kovni 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 re-
z 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 matches()
boolean loo k in g A t()
boolean fin d ()
boolean 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 zna-


k 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 iz-
razi 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() + 11 " ) ;
p r in t();
in t i = 0;
whi 1e (m .f in d (i) ) {
printnb(m .group() + 11 " ) ;
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 11 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 celobroj-
ni 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 pro-
nalaenja 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 pro-
nalaenja 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 mome 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 znako-
va 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 po-
naanje je da se $ poredi s krajem celog ulaznog niza, pa regularnom izrazu m orate ekspli-
citno 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!";

1 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 = 11
+ 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 regu-
larnim 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 Uticaj


Pattern.CAIMON _EQ Smatra se da se dva znaka podudaraju ako i samo ako se podudar-
aju 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.
Pattern.CASE INSENSITIVE Podrazumevano ponaanje poredenja bez uzimanja u obzir velii-
(?') ne slova |engl. case-insensitive matching) jeste da se porede samo
znakovi iz skupa US-ASCII. Ovaj indikator slui da se ispita podu-
daranje 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.

4 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 Uticaj


P attern.CO M M EN TS U ovom reimu zanemaruju se bo'ine. f .'o kraja reda zanemaruju
(?x) se ugradeni komentari koji po .ju y kom #. Ugradivanjem indi-
katora (?x) u izraz moe se ukjjuru /im Unix lines.
P attern.DO TALL U reimu dotall, izraz . se podudctr~ - bilo kojim znakom, ak i s
l?sj graninikom reda. Izraz . se poctr r /ano ne podudara s gra-
ninicima redova.
Pattern.M ULTILINE U reimu multiline, izrazi A i $ , .. .uju se s poetkom odnos-
|?m) .no krajem reda. A se podudar is p 'f 'kom ulaznog znakovnog
niza, a $ s krajem ulaznog znak< 'n : nza. Ovi izrazi se podrazu-
mevano podudaraju samo s poi - i krajem celog ulaznog
znakovnog niza.
P attern.U N IC O D E CASE Poreenje bez uzimanja u obzir vtw slova, kada je ukljueno
(?u) indikatorom CASE_INSENSITIVE, snrovodi se u skladu sa stan-
dardom Unicode. Podrazumevano i .l aanje poreenja bez uzi-
manja u obzir veliine slova jeste ... ~>orede samo znakovi iz
skupa US-ASCII. , .
Pattern.UNIX_LINES U ovom reimu se u ponaanju i?\:,: A i $ kao graninik reda
(?d) priznaje samo \n

O d ovih indikatora naroito su korisni Pattern.CASE_IK IIV E, Pattem.M UL


TILINE i Pattern.COMMENTS (koji poveava jasnou i/ili sava dokumentaciju).
Im ajte u vidu da se ponaanje veine indikatora m oe postu L-tanjem znakova u za-
gradam a, napisanih ispod indikatora u tabeli, u regularan ^pred mesta na kojem
hoete da aktivirate taj reim.
Uticaj navedenih i drugih indikatora m oete da kom bu, peracijom OR (I):

//: 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( ) ) ;
}
) /* 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 re-
gularnim 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 ko-


rak 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 pozi-
vanje 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 ko-
m entaru na poetku program a biva izvaen i obraen regularnim izrazim a da bi se upo-
trebio 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 ce-
lim 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 ula-
za). 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 obje-
kat 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 za-
m 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 ni-
zove. U narednom prim eru prikazan je jedan nain prim ene regularnih izraza za pro-
nalaenje 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: S s ct: 26
4: c la s s : 7
5: s ta tic : 9
6: S trin g : 26
7: throws: 41
8: System: 6
9: System: 6
10: compile: 24
jll: through: 15
12: the: 23
13: the: 36
14: S trin g : 8
15: System: 8
16: 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 me-
todom 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 Expres-
sions, 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. Pat-
tern.CASEJNSENSITIVE, Pattern.MULTILINE).
Veba 16: (5) Prepravite JGrep.java tako da kao argum ent prim a ime datoteke ili direk-
torijum a (ako se zada direktorijum , pretraivanje treba da obuhvati sve datoteke u nje-
m 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 ni-
zove u kodu.
Veba 19: (8) Na osnovu preth od ne dve vebe, napiite program koji pretrauje Javin iz-
vorni 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 re-
lativno 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 ob-
jekat 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 ana-
liziranja 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?11);
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 zna-
kovnog niza, a postoje i ,,next m etode za sve proste tipove (sem char), kao i za BigDeci-
m 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 ana-
lizira 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
sli zarezi (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.
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 02/10/2005 od 58.27.82.161
Pre tn ja dana 02/11/2005 od 204.45.234.40
Pre tn ja dana 02/11/2005 od 58.27.82.161
Pre tn ja dana 02/12/2005 od 58.27.82.161
Pre tn ja dana 02/12/2005 od 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 ni-
kada 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 jed-
nostavno 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 iz-
danjim 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) omogu-
uje 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 orijen-
tisanom 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 vre-
m e izvravanja. To se obavlja na dva naina: tradicionalnim otkrivanjem tipa tokom iz-
vravanja, 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. Ge-
neriki 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 iz-
vedene 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 di-
namiki 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 po-
ziva 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 Sy-
stem .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 to-
StringO 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>. Pri-
likom 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 pre-
poznavanja tipa u vreme izvravanja, poto se u Javi u vrem e izvravanja proverava pra-
vilnost 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<Ob-
Iik> p u n objekata tipa Oblik. Tokom prevoenja to osigurava kontejner i Javin sistem ge-
nerikih 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 najlak-
e 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 o-
rate 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. Za-
pravo, 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 napi-
ete 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 pri-
mordijalni uitava klasa koji je deo realizacije JVM-a. Prim ordijalni uitava klasa, obi-
no 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 po-
sebne 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 na-
pravi 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 opera-
torom 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 tra-
dicionalnih jezika. D inam iko uitavanje om oguuje ponaanje koje je teko ili nem o-
gue 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, po-
drazum 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 pr-
vom 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 kla-
sa). Jedan od naina za dobijanje reference na objekat tipa Class jeste statina m etoda for-
Name() 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 spored-
nog 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, naj-
pre 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 za-
nim 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 objek-
ta. 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: 11 + 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 a-
Vodu i Puca. U m etodi m a in ( ), pravi se referenca klase Class i inicijalizuje na objekat Le-
palgracka 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 get-
Sim 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 obje-
kata 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 kon-
stru 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 eto-
dom new ln stance( ) m ora im ati podrazum evani konstruktor. U nastavku poglavlja vi-
deete 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 konstruk-
to 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, od-
nosno postavlja indikator u njim a. M etoda to S trin g ( ) svake izvedene potklase klase Ob-
Iik 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.getDeclared-
F 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(), isto-
vrem eno je i efikasnije.
Literali klase rade sa obinim klasama, ali i sa interfejsima, nizovim a i prostim tipovi-
ma. 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 Boolean TYPE
char.class Character.TYPE
byte.class Byte.TYPE
short.class Short.TYPE

int.class lnteger.TYPE
long.class Long.TYPE
float.class Float.TYPE
double.class Double.TYPE
void.class Void.TYPE
440 Misliti na Javi

Preporuujem da koristite verzije class ako je to mogue, poto se tako pristupa i obi-
nim 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 obu-
hvata tri koraka:
1. Uitavanje, koje obavlja uitava klasa. O na pronalazi bajtkod (obino na disku na-
vedenom 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 sta-
tinim 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 inicijali-
zovana. 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 refe-
renca klase m oe dodeliti bilo kojem drugom objektu tipa Class, d o k se generika refe-
renca 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 mo-
da previe tanana; razm otriem o je poblie u poglavlju Generiki tipovi).
Da bih ublaio ogranienja prilikom korienja generikih Class referenci, upotreblja-
vam 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 dobie-
mo 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 refe-
rencam 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 natkla-
se 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 pre-
voenja 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. Narav-
no, 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 upo-
trebite 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 e-
ravate da upotrebite za konverziju. Ispostavlja se da je to retko p o treb n o u celoj biblio-
teci 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 izvra-
vanja, ve sam o saoptava prevodiocu da objekat posm atra kao novi tip. U Javi, koja pro-
verava 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 na-
vie (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 kon-
verziju 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 na-
sleuju 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 neop-
hodno 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 izve-
denoj klasi - ovo je varijanta projektnog obrasca Template Method (ablonska metoda).
Poto je zadato da tip klase bude neto izvedeno iz klase Ljubixnac, m etoda new-
Instance( ) 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 re-
lativno 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 etoda-
ma. M etoda getTypes( ) obino vraa sam o referencu neke statine Liste. Evo jedne rea-
lizacije 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 pro-
sleujete 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 re-
zervisanu 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 pri-


m ercim a klase Ljubinici. Zatim se svaki Ljubimac u nizu ispituje i broji pom ou rezervi-
sane 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 alternativ-
no 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, dobie-
m 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 pre-
voenja 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 PravljenjeLjubi-
maca. 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 Pra-
vljenjeLjubimaca, 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 do-
sadne 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, Man-
ska=7, Velska=5, Pacov=2, Mis=2, Hrcak=l}
* ///:-

Da bi se prebrojali svi razliiti tipovi prim eraka klase Ljubimac, Mapa BrojacLjubi-
maca se unapred popunjava tipovim a iz liste RucnoPravIjenjeLjubimaca.sviTipovi. Ta
lista upotrebljava klasu net.mindview.util.M apData koja prim a objekat tipa Iterable (li-
stu 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 me-
njanjem niza tipovi; ostatak program a ne m ora da se m enja (to nije bio sluaj pri kori-
enju 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 upotre-
biti m etodu Class.isAssignableFrom ( ) i napraviti alatku opte nam ene koja um e da bro-
ji 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 : 11
+ t ip + " , tre b a lo bi da bude t ip i l i podtip od 11
+ 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 e-
todu 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 kla-
se. Zatim , ako je osnovniTip dodeljiv (engl. assignable) iz natklase, countC Iass( ) se po-
ziva 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 Gene-
riki 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 hije-
rarhije dodate i u klasu rucnoPravljenjeLjubimaca.java. Ukoliko esto pravite nove kla-
se, 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 ja-
vlja problem kokoke i jajeta: kada klasa nije u listi poznatih tipova, rucnoPravljenjeLjubi-
maca 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 alat-
ku 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 pri-
m 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 oda-
vanjem 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 14: 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 pra-
vim 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 in-
terfejs Proizvodjac koristi se tako to se navede p o tp u n o ime podaciotipu.proizvod-
jac.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 ( ). Meto-
da 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 Regi-
strovaniProizvodjaci.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 Proiz-
vodiace 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 Re-
gistrovani Proizvodjaci.

Poreenje instanceof sa ekvivalencijama klase


Kada traite inform acije o tipu, postoji vana razlika izm eu oba oblika naredbe instan-
ceof (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. Me-
u tim , postoji jedno ogranienje: da biste tip mogli da otkrijete tokom izvravanja i da bi-
ste 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 re-
ferencu 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 eu-
tim , 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 apli-
kacija u integrisanom razvojnom okruenju (engl. Integrated Development Environment,
IDE). To je vizuelni pristup pravljenju program a (koji se na ekranu prikazuje kao obra-
zac), 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 ira-
nja. Pri konfigurisanju tokom projektovanja, m ora biti omogiu no da se svim kom ponen-
tam 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 obezbe-
uje 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 Ja-
vinom 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 izvra-
vanje. Ponekad ete hteti da odreenom raunaru poaljete kod koji obrauje odreene
vrste zadataka (npr. poslovna pravila" u vieslojnoj kljent/sei ver arhitekturi), pa taj ra-
unar 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 pre-
skup 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 rea-
lizuje interfejs Member). O bjekte tih tipova pravi Javina virtuelna m aina tokom izvra-
vanja, 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(), getMet-
hods(), 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 izvr-
avanja 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 otva-
ra 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 nastav-
ku knjige). M eutim , postoje prilike kada je m ogunost dinam ikog izdvajanja inform a-
cija o klasi korisna.
Jedna izuzetno korisna alatka je ita m etoda klase. Izvorni kod s definicijom klase ili do-
kumentacija na Webu prikazuju samo m etode koje su definisane ili redefinisane unutar de-
finicije 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 . * ;

1 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 Met-
hod odnosno Constructor. Svaka od ovih klasa im a dalje m etode za izdvajanje im ena, ar-
gum 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 to-
StringO, kao to je ovde uinjeno. O statak koda slui samo za izdvajanje inform acija s ko-
m andne linije, za utvrivanje da li odreeni potpis odgovara ciljnom znakovnom nizu
(korienjem funkcije indexOf()) i za odbacivanje kvalifikatora im ena p om ou regular-
nih 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 objanje-
nje refleksije u dokum entaciji na Webu, videete da im a dovoljno podrke za stvarno po-
deavanje 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 pristu-
pa 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 en-
taciji 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 (prila-
goenu 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 napra-
vljen 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 ja-
va.lang.Class. Napiite program koji im e klase p rim a kao argum ent kom andne linije, za-
tim 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 ob-
jekat 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 JednostavanPo-
srednik, 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? Tak-
vom 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 posredo-
vanih m etoda. Svipozivi dinam ikogposrednika bivaju preusm ereni na jedini blokza po-
zive (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 obje-
kat, 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( ) po-
zivate 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 argu-
mente. 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 pro-
jektnim obrascim a vie ete saznati u knjigam a Thitiking in Patterns (posetite www.Min-
dView.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, po-
kuajte da ispiete argum ent posrednik i objasnite ta se deava.
Projekat:2 Napiite sistem u kojem dinam iki posrednici realizuju transakcije, gde po-
srednik obavlja potvrivanje ako je posredovani poziv bio uspean (nije generisao izuzet-
ke), 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 pa-
zite 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 re-
ferenca nem a svoje ponaanje, sem to proizvodi NullPointerException ako ita pokua-
te 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 prove-
ravajui 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 re-
ferenca 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 NullPo-
interException. Izgleda da su NuII objekti najkorisniji kada su blie podacim a, sa ob-
jektim 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 po-
treban nain ispitivanja jednakosti s null. Najjednostavniji nain da se to uradi je isto-
im 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 napra-


vljen 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 ge-
neriki 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 rad-
na 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 najjed-
nostavnijim 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 pro-
vere; sm ete da pretpostavite kako su sve reference objekata validne.
Ako um esto s konkretnim klasama radite sa interfejsim a, m oete u potrebiti Dyna-
micProxy za autom atsko pravljenje Null objekata. Pretpostavim o da im am o interfejs Ro-
bot 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 a.
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.* ;

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() + " NullRobot11;
}
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 jn t i)
throws Throwable {
return method.invoke(imaposrednika, argum enti);
}

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 ispu-
njava 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 zavre-
nom program u. M eutim , i lani objekat i vezivna funkcija glume da su ivi objekti koji is-
poruuju 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. Veziv-
ne funkcije vraaju samo zaetke podataka, obino su teki objekti i esto se vie puta upo-
trebljavaju 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 po-
nente 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[] args) {
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();
}
}
} /* 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 jav-
ne 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 pa-
keta 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() { print("javna C . f ( ) ); }
public void g() { print("javna C .g ()"); }
void u() { print("paketni C .u ()"); }
protected void v() { print("zatiena C .v ()) ; }
private void w() { print("privatna C . 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 pa-
keta 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 'C':
/* if(a instanceof C) {
C c = (C)a;
c.g();
) */
// Pazi sad! Refleksija ipak omoguava da pozovemo g():
pozivSkriveneMetode(a, "g");
// Pa ak i metode koje su jo manje dostupne!
pozivSkriveneMetode(a, "u");
pozivSkriveneMetode(a, "v");
pozivSkriveneMetode(a, "w");
}
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 ob-
jek 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 ispo-
ru 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() { print("javna C.g()"); }
void u() { print("paketna C .u ()"); }
protected void v() { print("zatiena C.v()"); }
private void w() { print("privatna C.w()"); }
I
public static A napraviA() { return new C(); }

public class UnutranjaRealizacija {


public static void main(String[] args) throws Exception {
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, "g");
Sk rivenaRealizacija.pozivSkriveneMetode(a, "u");
Sk rivenaRealizacija.pozivSkriveneMetode(a, "v");
S k r i ve na Re al iz ac ij a.pozivSkriveneMetode(a, "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() {
public void f() { print("javna C.f()"); }
public void g() { p r in t(javna C.g()''); }
void u() { print("paketna C.u()"); }
protected void v() { print("zatiena C.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, "g");
SkrivenaRealizacija.pozivSkriveneMetode(a, "u");
SkrivenaReal izacija.pozi vSkri veneMetode(a, "v");
Skri venaReal izacija . p o z i vSkri veneMetode(a, "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.getlnt(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 );
}
} /* 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 pa-
ketn 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 va-
na 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 tisa-
n 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 osnov-
na 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 speci-
jaln 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 e-
to 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 rede-
finiu 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 iz-
vravanja 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 e-
sto. 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 osnov-
n 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 re-
p 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 t-
h 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 rofaj-
le 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 sta-
tike 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 oguujed 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 Solu-
tion 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 re-
vie 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 fleksi-
b 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 e-
to 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 in-
terfejs - 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 e-
rim 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 ogra-
nienja 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 gene-
rik 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 pa-
ra 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 tipovi-
m 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.
2 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 be-
leja 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 (}

public class Skladistel {


private Automobil a;
public S k l a d i s t e l (Automobi1 a) { this.a = a; }
Automobil get() { 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; }
public static void main(String[] args) {
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; }
public static void main(String[] args) {
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 skla-
diten 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 z-
voljava 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 za-
je 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 re-
nos 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 ri-
kazivanje 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 bez-


b 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 bezbed-
n 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 + ", 11 + 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, Integer>(
new A m f i b i j a ( ) , "zdravo", 47);
}
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);
}
public static void main(String[] args) {
D v oj ka<Strin g , Integer> iesi = f();
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 ()) ;
}
490 Misliti na Javi

} /* Ispis: (80% podudaranja)


(zdravo, 47)
(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() {
T rezultat = vrh.stavka;
if(!vrh.kraj())
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()) != null)
System.o ut .p ri nt ln (s);
}
} /* 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 z-
naka 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 rela-
zite 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 i-
no 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) { s k l a d i s t e. ad d( st av ka); }
public T select() {
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 () + 11 ");
)
} /* 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 e-


to 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 bjek-
te 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 je-
kata:

//: 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>, Iterable<Kafa> {
private Class[] 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) { velicina = v e l ; }
public Kafa sledeci() {
try {
return (Kafa)
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() { return broj > 0; }
public Kafa s l e d e c i () {
broj ;
return Gene ra to rK af e. th is .sl ed ec i();
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[] args) {
GeneratorKafe gen = new Ge n e r a t o r K a f e ( ) ;
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);
}
} /* 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 na-
redbi. 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 Fibonac-
cijevog 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 implements Ge ne rator<Integer> {


private int broj = 0;
public Integer sledeci() { return fib(broj++); }
private int fib(int n) {
if(n < 2) return 1;
return fib(n-2) + fib(n-l);
}
public static void main(String[] args) {
Fibonacci gen = new Fibonacci();
for(int i = 0; i < 18; i++)
Sy s t e m . o u t . p ri nt (g en. sl ed ec i() + " ");
}
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) { n = broj; }
public Iterator<Integer> iterator() {
return new Iterator<Integer>() {
public boolean hasNext() { return n > 0; }
public Integer sledecif) {
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) {
for(int i : new Iterabi1 n i Fi bo na cc i(18))
System.out.print(i + " ");
}
} /* 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 naslei-


vanja.
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 , ge-
nerike 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 zakljui-
vartje 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 ret-
h 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 iz-
gleda 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 lja-
vane 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[] args) {
Map<String, L i s t < S t r i n g sls = N o v a . m a p O ;
List<String> ls = Nova.list();
LinkedList<String> lls = No va .1L i s t ();
Set<String> ss = Nova.set();
Queue<String> qs = Nova.queue();
}
} /.//-
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 ar-
g 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 ri-
m 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) {
Map<0soba, List<? extends L j u b i m c i 1judiSLjubimcima = N o v a . m a p O ;
// Ostatak koda je isti...
}
} ///-

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 rezul-
tat 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, prevo-
dilac 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 enera-
tor. 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 kon-
tejn 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
// koja ima podrazumevani konstruktor (bez argumenata).
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 jav-
ni, 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.napra-
vi(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 rijavlju-
je 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() {
return n_torka(new Vozilo(), new A m f i b i j a O , "zdravo", 47);
}
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[] args) {
Dv ojka<String,Integer> iesi = f();
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 ());
}
504 Misliti n a Javi

} /* Ispis: (80% podudaranja)


(zdravo, 47)
(zdravo, 47)
(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 generi-
k 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) {
Set<T> rezultat = new Ha sh Se t< T> (a );
r e zu lt at .r et ai nA ll(b);
return rezultat;
}
// Oduzmi podskup od nadskupa:
public static <T> Set<T>
r a z l i ka(Set<T> nadskup, Set<T> podskup) {
Set<T> rezultat = new H a s h Se t< T> (n ad sk up );
r e z u l t a t . r e m o v e A U (p odskup);
return rezultat;
}
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 ele-
m 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 raz-
n 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: " + skupl);


print("skup2: " + skup2);
print("unija(skupl, skup2): " + unija(skupl, skup2));
Set<Vodeneboje> podskup = presek(skupl, skup2);
print("presek(skupl, skup2): " + podskup);
print("raz1ika(skupl, podskup): " +
razlika(skupl, podskup));
print("razlika(skup2, podskup): " +
razlika(skup2, podskup));
print("kompleinent(skupl, skup2): " +
komplement(skupl, skup2));
}
} /* Ispis: (primer)
skupl: [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,
PERMANENT_GREEN, V I R I D I A N J U E , SAP_GREEN, YELLOW_OCHRE, BURNT_SIENNA,
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): [RAW_UMBER, SAP_GREEN, VELLOW_OCHRE,
BURNT_SIENNA, BURNT_UMBER]
komplement(skupl, skup2): [ S A P G R E E N , ROSE_MADDER, YELLOW_OCHRE,
BURNTJJMBER, VIOLET, BRILLIANT_RED, RAW_UMBER, BURNT_SIENNA, CRIMSON,
MAGENTA]
* ///:-

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) {
Se t<String> rezultat = new T r ee Se t< St ri ng >( );
for(Method m : tip. ge tM et ho ds ())
rezultat .a dd (m .g et Nam e( )) ;
return rezultat;
}
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 () +
11 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 e-
to 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() {}
public String t o S t r i n g O { return "Klijent " + id; }
// 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() {}
public String t o S t r i n g O { return "Sluzbenik " + id; }
// Samo jedan objekat tipa Generator:
public static Ge ne ra to r< Sl uz be ni k> ge nerator =
new Generator<Sluzbenik>() {
public Sluzbenik sledeci() { return new Sluzbenik(); }
};
}

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 Ge-
nerator<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 e-
to 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 ri-
m 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>() {
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() * 1000.0) + 0.99);
}
};
}

class Polica extends ArrayLis t< Pr oi zv od > {


public Polica(int nProizvoda) {
G e ne r a t o r i .po pu n i ( t h i s , Proizvod.generator, nProizvoda);
}
}
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() {
StringBuilder rezultat = new StringBuilder();
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);
}
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 po-
d 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. Razli-


iti 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 Array-
List<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 ge-
n 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 re-
m 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} (Ne moe da se prevede)

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(); }
}

public class Manipulacija {


public static void main(String[] args) {
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 ();
}
} 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 poz-
vali 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 gene-
rik 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 in-
terfejso 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 e-
to 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 ri-
sanja - 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 pa-
tibilnost - 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 generi-
kim 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 bi-
b 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 r-
m 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;
1

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) { arg = element; }
public T get() { 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[] args) {
Izvedena2 d2 = new Izvedena2();
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 i-
lac 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 gene-


rie 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 sre-
d 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 snov-
n 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) { this.vrsta = vrsta; }
@SuppressWarni ngs("unchecked")
T[] napravi(int velicina) {
return (T[])Array.newInstance(vrsta, velicina);
}
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 e-
to 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 shva-
tan 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 e-
g 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; }
public Obje ct g e t () { return obj; }
public static void main(String[] args) {
J e dn os ta vn oS kl ad is te Skladiste = new JednostavnoSk1adiste();
S k la di st e. se t( "S ta vka ");
String s = (S tr in g) Sk la di st e. get ();
}
} ///:-

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 ie-


m 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: aload_0
1: aload_l
2: putfield #2; //Field obj:0bject;
5: return

public ja va .lang.Object get();


0: aload_0
1: getfield #2; //Field obj:0bject;
4: areturn

public static void mai n(java.l an g. St ri ng []);


0: new #3; //class Je dnostavnoSkladiste
3: dup
4: invokespecial #4; //Hethod "< init> : ()V
7: astore_l
8: aloadl
9: ldc #5; //String Stavka
11: invokevirtual #6; //Method set:(0bject;)V
14: aload_l
15: invokevirtual #7; //Method get:()0bject;
18: checkcast #8; //class java/lang/String
21: astore_2
22: return
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) { this.obj = obj; )
public T get() { return obj; }
public static void main(String[] args) {
Ge ne rickoSkladiste<String> Skladiste =
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( );
}
} ///= -

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: aload_0
1: aload_l
2: putfield #2; //Field obj:0bject;
5: return

public java.lang.Object get();


0: aloadO
1: getfield #2; //Field obj:0bject;
4: areturn

public static void main(java.lang.String[]);


0: new #3; //class Ge nerickoSkla d iste
3: dup
4: invokespecial #4; //Method "<init>":()V
7: astore_l
8: aload_l
9: ldc #5; //String Stavka
11: invokevirtual #6; //Method set:(0bject;)V
14: aload_l
15: inv ok ev irtual #7; //Method get:()0bject;
18: checkcast #8; //class java/lang/String
21: astore_2
22: return
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 e-
tan 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 gene-
rik 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) {} // Greka
T var = new T(); // Greka
T[] niz = new T [ V E L I C I N A ] ; // Greka
T[] niz = (T)new O b j e c t [V E L I C I N A ] ;
// Upozorenje koje nije zaustavljeno
}
} ///:-

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 in-
sta 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 na-
redb 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.ja-
v 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 ri-
ro 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 { return new X(); }
void f() {
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) { n i z. ad d( st av ka ); }
public T get(int indeks) { 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 lja-
va nizove in te rn o ). Da stv ar b u d e zanim ljivija, m oete definisati referencu tak o da prevo-
dilac 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) { return ni z[ i n d e k s ] ; )


// Metoda koja eksponira kako je niz predstavljen:
public T[] rep() { return niz; }
public static void main(String[] args) {
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: [unchecked] unchecked cast


found : java.lang.Object[]
required: T[]
niz = (T[])new Object[vel];

1 warning

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 @Sup-
pressW 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 d-
m 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 re-
voilac 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 ele-
m 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) { return (T)ni z[i n d e k s ] ; }
@S uppressWarnings("unchecked")
public T[] rep() {
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)
0 1 2 3 4 5 6 7 8 9
java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to
[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 ak-
le, 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) { return nizfindeks]; }
// Eksponiraj kako je niz interno predstavljen:
public T[] rep() { return niz; }
public static void main(String[] args) {
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 ri-
sanja 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 pri-
m 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 a-
ra 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 re-
zerv 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, ex-
te 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) { this.stavka = stavka; }
T getltem() { 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) { this.stavka = stavka; }
T getltemO { return stavka; }
ja va.awt.Color boja() { return s t av ka .g et Co lo r( ); }
int getX() { return stavka.x; }
int g e t Y () { return stavka.y; }
int getZ() { return stavka.z; }
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) { this.stavka = stavka; }
T getltem() { return stavka; }
java.awt.Color boja() { return s t a v ka .g et Co lo r( ); }
int getX() { return stavka.x; }
int getY() { return stavka.y; }
int getZ() { return stavka.z; }
int tezina() { return st av ka .t ez in a( ); }
}

class Ogranicen
extends Dimenzija implements ImaBoju, Tezinu {
public java.awt.Color getColor() { return null; }
public int tezina() { return 0; }
}

public class OsnovnoOGranicama {


public static void main(String[] args) {
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 ;
}
} ///:-
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 iz-
bei 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) { this.stavka = stavka; }
T getltemO { return stavka; }
}

class 0bojena2<T extends ImaBoju> extends DrziStavku<T> {


0bojena2(T stavka) { su pe r( st av ka ); }
ja va .a wt .C ol or boja() { return st av ka .g et Co lo r( ); }
}
class ObojenaDimenzija2<T extends Dimenzija & ImaBoju>
extends 0bojena2<T> {
0b ojenaDimenzija2(T stavka) { s u pe r( st av ka ); }
int getX() { 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) { su pe r( st av ka ); }
int tezina() { 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. Obojena-
Dimenzija2 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) { this.snaga = snaga; }
SNAGA getPower() { return snaga; }
}

class SuperUhoda<SNAGA extends RentgenskiVid>


extends SuperJunak<SNAGA> {
534 Misliti na Javi

SuperUhoda(SNAGA snaga) { su pe r( sn ag a); }


void vidi() { snaga. vi di Kr oz Zi do ve( ); }
}

class PasJunak<SNAGA extends SuperSluh & SuperNjuh>


extends SuperJunak<SNAGA> {
PasJunak(SNAGA snaga) { su pe r( sn ag a); }
void cuje() { snaga.cu je Ti he Zv uk ove (); }
void njusi() { 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 ar-
g 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 realizu-
je 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 izra-
zim 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 ri-
v 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 a-
m 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} (Ne mo e se prevesti)
import java.util

public class NeKovarijantniGenerickiTipovi {


// 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 Ja-
buka, 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 us-
p 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 Vo-
ce. 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 slua-
ju 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 re-
vodilac 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 za-
tim 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[] args) f
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'
}
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 ar-
g 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 ri-
m 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 o-
kerskih 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 generi-
ke 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 obje-


k 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 o-
bijati 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 ? Ex-


tends 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 ara-
m 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 do-
kerskim 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 li-
sti 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 Ja-
buka 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 gene-
rikog 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 li-
stu 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<Vo-
ce> , 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 iz-
ved 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 ge-
nerike 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 t-
nim 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 ko-
rienje 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. Zai-
sta, 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 NeograniceniDzo-
keril.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 e-
o 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 ra-
n 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
// u Skladiste<? extends Long >)
// Unchecked method invocation:
// divljiPodtip(Skladiste<? extends T>,T) is
// 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(ograniceno, lng); // Greska:
// divljiNadtip(Skladiste<? super T>,T) ne moe biti
// primenjena na (Skladiste<(rezultat od) ? extends Long>,Long)
}
} 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 ko-
jeg 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 ro-
v 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 Skladi-
ste<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 Skla-
diste<? 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 e-
nih 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 ar-
g 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 poz-
n 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 ge-
n 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 e-
ra 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 ui-
tavate. 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 r-
m 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 ri-


san 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 pre-
p 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 + 11 ");
}
}
} /* 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 konver-
ziju. 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 @Suppress-
W 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 KonverzijaGene-
rickih.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 Uporediv-
Ljubimac, 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 bes-


k 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 kla-
su 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 generi-
ki 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 ra-
di 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 tar-
skog 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 vred-


n 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 osnov-
na 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 izvede-
ni 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 Samo-
Ograniceni 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 na-
m 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 si-
rovi 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 lja-
vaju 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 ar-


g 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 argu-
m 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 la-
se, 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 sa-
svim 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 e-
to 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 vi-
d 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 IzvedenaZadaj-


O b.zadajO b( ) ne redefinie m e to d u O bicnaZadajO b.zadajO b( ), n ego je preklapa. Iz re-
zu 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 snov-
ni 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 , Izvedena-


ZadajOb 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 re-
klo 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 rekla-
pate. 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 ge-
nerik 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.Col-
Iections 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) kon-
te 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 n-
tejner. O vde zastareli ko d p redstavlja m etodaU Starom Stilu( ) zato to uzim a sirovu Li-
stu, 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 izu-
zetak 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 e-
to d e o b ra d a ( ) sm eta se u List<T> kontejnerRezultata (to nazivam o param etar kolekci-
je). 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 izu-
zeci 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 ri-
m 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 pre-
nose 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 ara-
m 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 pose-
dov 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 ea-
n 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() + 11 " + 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 projekt-
nim obrascem Decorator (D e k o ra to r)/ Kada uob iajeno pravljenje potklasa daje toliko kla-
sa 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 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 .
5 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.Minti-
View.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 vi-
deti, 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 deko-
ra 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 (posled-
n 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 ik-
sine 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 a-
p 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 si-
tuacija - 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 ri-
stite 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 la-
te 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 realizo-
vanje 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 zajed-
n 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 iekrat-
n 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 ko-
rienje 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 ri-
m 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 e-
to 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 La-
te 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 Ja-
vin 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 + 11 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 di-
nam 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 izvra-
vanja 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 ar-
g 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 de-
finisan 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 i-
z v od ni interfejs. N aalost, veina n a p isan ih ldasa ne zn a za va interfejs, te ga i n e realizu-
je. 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 Popunjena-
Lista 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 Gene-
riki 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 o-
b 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 tri-
zovanu 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 Ge-
neratora. 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 Col-
lection". 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 rea-
lizacijam 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 naslei-
van jem zahtevae neto m a n je k o d a, kao to m o ete v ideti u ImaAddJednostavanRed-
ZaCekanje.
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 gencri-
ka 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 ImaAddJednostavanRedZaCe-
kanje. 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 ko-
rienje 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 funkcionalnogstila 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 slua-
ju 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 ia-
jen 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 i-
ta 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 na-
m 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 sek-


venci 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 te-
k 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 e-
n 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 ja-
v 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 raz-
m 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 ri-
k 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 in-
form 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 tej-
n 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 ob-
je 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 tre-
tirate 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 Mac-
ka, d o b ili biste izuzetak. P roblem nije ostajao sakriven, ali ste ga otkriv ali u v rem e izvra-
vanja, 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 ri-
k 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 ja-
vljuju 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 ra-
vljenja 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 na-
pisati gen eriki kod koji o b ra u je svoje generike tipove zahteva d o a tn i tr u d tv o rca kla-
se ; 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 lji-
vim 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 proble-
m 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 a-
b lon e, i ak su razm atrali da li da ih ukljue u jezik, ali su iz nekog razloga (izgleda da su u-
rili) 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://ja-
pan.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 ne-
m 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-tti-
torial.pdf
Jara Gerterics FAQs A ngelike L anger veo m a je k o ristan resurs, na lokaciji www.lan-
ger.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 Pro-
gram tning Language, iji su a u to ri T orgerson, E rnst, H ansen, Von d e r A he, B racha i Gaf-
ter. 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 Soltt-
tion 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 at-
k 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 ele-
m 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 re-
me.ta sve reference iz starog niza u novi. Iako bi klasi A rra y L i :o p ravilu treb a lo da d a-
jete 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 kla-
sa svih Javinih klasa.) Z ato je niz su p eriorn iji o d predgenerik 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 izvra-
vanja. 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 t-
skim 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 na-
m 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 e-
n 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 = 11 + 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 = " + g.length);
print("h.length = + h.length);
e = h;
print("e.length = " + e.length);
e = new int[] { 1, 2 };
print("e.length = " + 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 refe-


renco 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 sa-
zn 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 vred-
n 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 inicija-
lizacije 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 o-
naaju 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. Pozo-
vite 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 slua-
ju 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 je-
zieim 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 vred-
nost to se jo ne nalazi u nizu izabran. (Naravno, moglo je da se obavi i poreenje vred-
nosti 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 pove-
ava 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 vie-
dim 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[] args) {
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() },
};
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) {
Integer[] [] a = { // Automatsko pakovanje:
{ 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 },
};
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], [21, 22, 23, 24, 25, ,''G,
27, 28, 29, 30], [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 "omotakih" objekata.
import j a v a . u t i l .*;

public class VisedimNizOmotaca {


public static void main(String[] args) {
Integer[][] al = { // Automa ts ko pakovanje
{ 1, 2, 3, },
{ 4, 5 , 6, },
};
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 \ "Fox" },
{ "Jumped", "Over" },
{ "The", "Lazy , "Brown", "Dog", "and", "friend" },
};
System.out.println("al: " + Arrays.dee pT oS tr in g(a l));
System.out.println("a2: " + A r r a y s .deepToString(a2));
System.out.println("a3: " + A r r a y s .deepToString(a3));
}
} /* Ispis:
al: [[1, 2, 3], [4, 5, 6]]
a 2 : [[[1.1, 2.2], [3.3, 4 . 4 ] ] , [[5.5, 6.6], [7.7, 8.8]], [[9.9, 1.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 ini-
cijalizuju 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 skla-
du 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[] args) {
Integer[] celib = { 1, 2, 3, 4, 5 };
Double[] bdouble = { 1.1, 2.2, 3.3, 4.4, 5.5 };
Integer[] celib2 =
new Cl as sP ar am et er<Integer>().f(celib);
Double[] bdouble2 =
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 etrizo-
vane 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 prevodi-
lac nee dozvoliti pravljenje instance niza generikog tipa. M eutim , dopustie da napra-
vite 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[] args) {
List<String>[] ls;
List[] la = new Li st [10];
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 [10] ;
for(int i = 0; i < sfere.length; i++)
sfere[i] = new ArrayList<SferaOdBerilijuma>();
}
} ///:-

im im ate referencu na L ist< S tring > [], neke provere e se obaviti u vreme pre-
voenja. 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 pre-
vodilac 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 kon-
tejneri. 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 mogu-
nost 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 referen-
cu 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[] args) {
int velicina = 6;
booleanf] al = new bo ole a n [velicin a ] ;
byte[] a2 = new byte[vel i c i n a ] ;
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, (byte)ll);
print("a2 = " + Ar ra ys .t oS tr in g( a2 ));
Arrays.fill(a3, x');
print("a3 = 11 + Ar ra ys .t oS tr in g( a3 ));
Arrays.fill(a4, (short)17);
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 = 11 + A r ra ys .t oS tr in g( a8 ));
Arra ys .f i1 1 (a9, "Zdravo");
print("a9 = 11 + 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 eleme-
nata 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 kon-
cept 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 razli-


ite strategije1)-
U ovom odeljku napraviem o nekoliko Generatora; kao to ste ve videli, lako ete de-
finisati 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() {
vrednost = Ivrednost; // Napred - nazad
return vrednost;
)
}
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() {
indeks = (indeks + 1) % znakovi.length;
return znakovi [indeks];
}
}
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() {}
public String(int duzina) { this.duzina = duzina; }
public java.lang.String next() {
char[] baf = new ch a r [ d u z i n a ] ;
for(int i = 0; i < duzina; 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 GeneratorDa-
teKoIicine.Character, radi se o velikim i m alim slovima koja se ponavljaju. Klasa Gene-
ratorD ateK olicine.String upotrebljava GeneratorDateKolicine.Character za
popunjavanje niza znakova, koje zatim pretvara u String. Veliinu niza odreuje argu-
m ent konstruktora. O bratite panju na to da GeneratorDateKolicine.String upotreblja-
va elem entarni Generator<java.lang.Character> um esto specifine reference na
GeneratorDateKolicine.Character. Kasnije taj generator moe zam eniti RandoinGene-
rator.String iz paketa RandomGenerator.java.
Poglav[je 16: Nizovi 609

Evo jedne alatke za testiranje koja upotrebljava refleksiju su agneenim Generato-


ro 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 ) {
System.out.print(tip.getSimpleName() + ": ");
try {
Generator<?> g = (Generator<?>)tip.newInstance();
for(int i = 0; i < velicina; i++)
System.out.printf (g.next() + 11 );
System .o ut .p ri nt ln ();
} catch(Exception e) {
throw new Ru nt im eE xc ep ti on (e );
}
}
}
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 Ge-
nerator, od kojih svaki ima podrazumevani konstruktor (onaj bez argum enata). Sve ug-
neene 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 kon-
stru k to r klase R andom inicijalizuje konstantom , rezultat se ponavlja svaki p u t kada po-
m 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(); } // Inicijalizator instance
public S t r i n g O {}
public String(int duzina) { su pe r ( d u z i n a ) ; }
}
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() {}
public Integer(int modulo) { mod = modulo; }
public ja va.lang.Integer next() {
return r. ne xt In t( mo d);
}
}
public static class
Long implements Ge ne ra to r< ja va .l an g.L on g> {
private int mod = 10000;
public Long() {}
public Long(int modulo) { mod = modulo; }
public java.lang.Long next() {
Poglavlje 16: 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 jed-
nostavno ukljuuje Character generator.
Za generisanje brojeva koji nisu preveliki, G eneratorSlucajnih.Integer koristi stan-
dardn 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 odgovara-
jui 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 {


// Popunjavanje postojeeg niza:
public static <T> T[] niz(T[] a, Ge nerator<T> gen) {
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 kontej-
nera. 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 Col-
lection (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 odgovara-
jueg 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 definisa-
nih 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[] args) {
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(), 15);
System.out.print ln (Ar ra ys .t oS tr in g( b));
}
} /* 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 konver-
tor 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[] p r o s t ( B o o l e a n [] ulaz) {
boolean[] rezultat = new bo ol ea n [ u l a z . d u z i n a ] ;
for(int i = 0; i < ulaz.duzina; i++)
rezultat[i] = ulaz[i]; // Automatsko raspakivanje
return rezultat;
}
public static char[] prost(Character[] ulaz) {
char[] rezultat = new ch ar [ u l a z . d u z i n a ] ;
for(int i = 0; i < ulaz.duzina; 1++)
rezultat[i] = u l a z [ i ] ;
return rezultat;
}
public static byte[] prost(Byte[] ulaz) {
byte[] rezultat = new by te [ u l a z . d u z i n a ] ;
for(int i = 0; i < ulaz.duzina; i++)
rezultat[i] = u l a z [ i ] ;
return rezultat;
}
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[] rezultat = new floa t[ ul az .d uz in a];
for(int i = 0; i < ulaz.duzina; i++)
rezultat[i] = u l a z [i];
return rezultat;
}
public static double[] prost(Double[] ulaz) {
double[] rezultat = new d o u b le [u la z. du zi na ];
for(int i = 0; i < ulaz.duzina; i++)
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 ver-
zije 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 ene-


ratorSlucajnili:

//: nizovi/TestGenerisanjaNizova.java
// Testiranje alatki koje nizove popunjavaju generatorima.
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 = 11 + Arrays .t oS tr in g( a8 ));
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 GeneratorDateKo-
licine. Ispiite rezultate.
Veba 13: (2) Popunite objekat tipa String pom ou klase GeneratorDateKolicine.Cha-
racter.
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 argu-
m 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 de-
epEquals() za viedim enzionalne nizove), fill() za popunjavanje niza odreenom vred-
nou (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 (saznae-
te 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[] args) {
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[] u = new Integer[10];
Integer[] v = new Integer[5];
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 tre-
ba 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 eu-
tim , 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 raspa-
kivanje - 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[] args) {
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 ) );
a2[3] = 11;
print(Arrays.equals(al, a 2 ) );
String[] sl = new String[4];
Arrays.fill(sl, "Cao");
String[] s2 = {new S t r i n g C C a o " ) , new St ri ng C' Ca o" ),
new S t r i n g ( " C a o " ) , new StringC'Cao") };
print(Arrays.equals(sl, s2 ) );
}
} /* 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 uka-
zuju 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 kon-
struktora. 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 jedno-
stavan 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 argu-
m 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 ;

2 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() {
String rezultat = " [i = " + i + ", j = " + j + "]";
if(broj++ % 3 == 0)
rezultat += "\n";
return rezultat;
}
}
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[] args) {
UporedivTip[] a =
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: ");
p r i n t ( Ar ra ys .t oS tr ing (a ));
Arrays.sort(a);
print("nakon ureivanja: ");
p r in t( Ar ra ys .t oS tr ing (a ));
}
} /* Ispis:
pre ureivanja:
[[i = 58, j = 55], [i = 93, j = 61], [i = 61, j = 29]
, [i = 68, j = 0], [i = 22, j = 7], [i = 88, j = 28]
, [i = 51, j = 89], [i = 9, j = 78], [i = 98, j = 61]
, [i = 20, j = 58], [i = 16, j = 40] , [i = 11, j = 22]
]
nakon ureivanja:
[[i = 9, j = 78], [i = 11, j = 22], [i = 16, j = 40]
, [i = 20, j = 58], [i = 22, j = 7], [i = 51, j = 89]
, [i = 58, j = 55], [i = 61, j = 29], [i = 68, j = 0]
, [i = 88, j = 28], [i = 93, j = 61], [i = 98, j = 61]
]
* ///:-

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 argu-
m 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 bi-
ste 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 in-
terfejs 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 eu-
tim , 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 = 93, j = 61], [i = 61, j = 29]
, [i = 68, j = 0], [i = 22, j = 7 ] , [i = 88, j = 28]
, [i = 51, j = 89], [i = 9, j = 78], [i = 98, j = 61]
, [i = 20, j = 58], [i = 16, j = 40], [i = 11, j = 22]
]
nakon ureivanja:
[ [i = 98, j = 61], [i = 93, j = 61], [i = 88, j = 28]
, [i = 68, j = 0], [i = 61, j = 29], [i = 58, j = 55]
, [i = 51, j = 89], [i = 22, j = 7], [i = 20, j = 58]
, [i = 16, j = 40], [i = 11, j = 22], [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 = 93, j = 61], [i = 61, j = 29]
, [i = 68, j = 0], [i = 22, j = 7], [i = 88, j = 28]
, [i = 51, j = 89], [i = 9, j = 78], [i = 98, j = 61]
, [i = 20, j = 58], [i = 16, j = 40], [i = 11, j = 22]
]
nakon ureivanja:
[[i = 68, j = 0], [i = 22, j = 7], [i = 11, j = 22]
, [i = 88, j = 28], [i = 61, j = 29], [i = 16, j = 40]
, [i = 58, j = 55], [i = 20, j = 58], [i = 9 3 , j = 61]
, [i = 98, j = 61], [i = 9, j = 78], [i = 51, j = 89]
]
* ///:-

V eba21: (3) P okuajteda uredite nizobjekata iz vebe 18. Prim enite C o m p arab le da bi-
ste 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, od-
nosno bilo koji niz objekata koji realizuju C o m parab le ili im aju pridrueni C o m p ara-
to 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: ", A r ra 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 ) ;
print("Ureivanje obrnutim redom: " + Arra ys .t oS tr in g( sa ));
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: [YNzbr, nyGcF, 0WZnT, cQrGs, eGZMm, JMRoE, suEcU, OneOE,
dLsmw, HLGEa, hKcxr, EqUCB, bklna, Mesbt, WHkjU, rUkZP, gwsqP, zDyCy,
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: [zDyCy, suEcU, rUkZP, nyGcF, hKcxr, gwsqP,
eGZMm, dLsmw, cQrGs, bklna, YNzbr, WHkjU, RFJQA, OneOE, 0WZnT, Mesbt,
JMRoE, HxxHv, HLGEa, EqUCB]
Ureivanje bez obzira na veliinu slova: [bklna, cQrGs, dLsmw, eGZMm,
EqUCB, gwsqP, hKcxr, HLGEa, HxxHv, JMRoE, Mesbt, nyGcF, OneOE, 0WZnT,
RFJQA, rUkZP, suEcU, WHkjU, YNzbr, 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 tele-
fonskim im enicim a.) Ako hoete da grupiete rei abecedno, tj. bez obzira na to da ii po-
inju 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 perfor-
m 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.bi-
narySearch(). 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 slede-
em 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: " + A r r a y s .t o S t r i n g ( a ) ) ;
while(true) {
int r = ge n. n e x t ( ) ;
int pozicija = Ar rays.binarySearch(a, r);
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 me-
sto 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 pre-
traujete m etodom binarySearch() (pom ou preklopljene verzije ove funkcije). Na pri-
mer, 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[] args) {
String[] 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: 10
HxxHv
* ///:-

C o m p a ra to r se m ora proslediti preklopljenoj m etodi binaryS earch() kao trei argu-


m 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 vredno-
stim 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 ve-
liine i niskog nivoa. Takvi nizovi im aju dobre perform anse, ali ne i fleksibilnost kao u je-
zicima 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 mi-
nim 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 za-
senjuju nizove po svemu sem po perform ansam a, a i perform anse kontejnera su u m eu-
vrem 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 ge-
neriki tipovi daju kontejnere iji se tipovi autom atski proveravaju.
Rekosmo u ovom poglavlju da su generiki tipovi prilino nepodesni za rad s nizovi-
ma, 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 ni-
zovi 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 po-
kazivao 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 pro-
blem (i da e ih prelazak na nizove znatno popraviti), trebalo bi da ponovo podelite pro-
gram na proste faktore i preete na nizove.
Ovo je prilino hrabra tvrdnja, ali neki jezici uopte nem aju nizove niskog nivoa i ne-
promenljive veliine. Imaju samo kontejnere prom enljive veliine koji su m nogo funk-
cionalniji 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 'lista'>
print aLista # [1, 2, 3, 4, 5]
print a L i s t a [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

4 Videti www.Python.org.
Poglavlje 16: Nizovi 627

# Definicija metode, pokaziva 'this' se pie eksplicitno:


def d a jO br nu tu (s el f):
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 ko-
m 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, nared-
be def proizvode m etode, a prvi argum ent m etode autom atski postaje ekvivalentan rezer-
visanoj 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 auto-
m 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 delo-
va program a predstavlja pisanje dodataka na jezicim a C, C + + ili pom ou Pyrexa, speci-
jalne 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 na-
prednijem 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 kon-
tejnerim a transform isanih kljueva. Nauiete zato postoje razliite verzije nekih konte-
jnera 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 bi-
blioteke 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 vi-
deli u poglavlju iivanje objekata) i njegove realizacije PriorityQ ueue i razni pod-
tipovi 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 apstrakt-
ne 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 nasle-
dili 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 re-
ferencu 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[] args) {
List<AdresaStringa> 1 ist= new ArrayList<AdresaStringa>(
Collections.nCopies(4, new AdresaStri ng a( "Z dr avo ")));
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!"));
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 Collec-
tion) 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 - ge-
nerisan m etodom h ashC ode( ). Iz rezultata vidite da sve reference upuuju na isti obje-
kat, 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 podat-
ke 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 se-
be. 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 11 +
"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 Podaci-
K 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)));

1 O vo nije stroga definicija adaptera kao ona iz knjige Projektni obrasci, ali sm a tram d a je dovoljno pre-
cizna.
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 po-
ziv Generatorove m etode n e x t( ) m o ra napraviti par objekata (jedan klju i jedna vred-
nost) 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; i++) {
Par<K,V> p = gen.next();
put(p.kljuc, p.vrednost);
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 po-
treb n u za pravljenje objekta tipa PodacilzMape.
Evo prim era korienja program a PodacilzMape. I G enerator klase Slova realizuje in-
terfejs 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() { return broj++; }
public boolean hasNext() { return broj < velicina; }
public void remove() {
throw new Unsuppor te dO pe ra ti onE xc ep ti on ();

public class TestiranjePodatakaMape {


public static void main(String[] args) {
// Ge nerator para:
print(PodaciIzMape.map(new Slova(), 11));
// 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) ));
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 Collec-
tion 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 je-
dan 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 pre-
vie 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 naslei-
vanjem klasa java.util.Abstract. Da biste napravili objekat tipa Map sam o za itanje, na-
sleujete 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 Meto-
da 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[][] PODACI = {
// Afrika
{ "ALG ER IA ", "A1 gi e r s " }, {"A N G O L A " ,11Lu 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"}, {"CAPE V E R D E " ,"P raia"},
{"CENTRAL AFRICAN RE P U B L I C " ,"B an gu i"},
{" C H A D " , " N 'dj a m e n a " } , {" C OM OR OS1', "Moroni },
{" C ON GO ","Brazzavi11e " }, {D J I B O U T I ,"Dijibouti" } ,
{" EG YP T" ,Cairo"}, {"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 " ,11Li brevi 11 e " }, {"THE G A M B I A " , "Banjul11},
{" 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 SV ' 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 N D I A " ,"New Delhi"}, {"I ND O N E S I A " ,"J akarta"},
{" 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"}, {"THE M A L D I V E S " ," M al e"},
{" M O N G O L I A " ,"U1an Bator"},
{"MYANMAR (BURMA)" ,R a n g o o n " },
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)11, "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"}, {"N IC A R A G U A " ," M an ag ua "},
{"PANAMA"."Panama City"}, {"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]; }
public String g e t V a l u e O { return PODACI [i ndeks] [1]; }
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() { return velicina; }
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;
}
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 int velicina) {
return new FlyweightMapa() {
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() { return imena; }
// Delimina lista:
public static List<String> imena(int velicina) {
return new ArrayList<String>(select(velicina).keySet());
}
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, BENIN=Porto-Novo}
{ALGERIA=Algiers, ANGOLA=Luanda, BENIN=Porto-Novo}
{ALGERIA=Algiers, ANGOLA=Luanda, 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 drug-
de. FIyw eightM apa m ora realizovati m etodu e n try S e t() koja zahteva nam ensku realiza-
ciju 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 po-
zovete m etodu getKey( ) ili getV alue( ), ona e vam vratiti odgovarajui elem ent PODA-
CI 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 sva-
ki 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 velii-
ne, 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 ini-
cijalizujemo 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:

20 ,
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,

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 li-


stu 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() { return indeks; }
public String getValue() {
return
znakovi[indeks % znakovi.length] +
Integer.toString(indeks / z n a k o v i. le ng th );
}
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() {
// LinkedHashSet zadrava poredak ustanovljen prilikom
// 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; i++)
stavke.add(new Stavka(i));
return stavke;
}
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, 8= 10 , 9 = J0 , 10=K0,
11 =L0, 12=M0, 13=N0, 14=00, 15=P0, 16=Q0, 17=R0, 18=S0, 19=T0, 20=U0,
21=V0, 22=W0, 23=X0, 24=Y0, 25=Z0, 26=A1, 27=B1, 28=C1, 29=D1, 30=E1,
31=F1, 32=G1, 33=H1, 34= 11, 35 = J1, 36=K1, 37=L1, 38=M1, 39=N1, 40= 01,
41=P1, 42=Q1, 43=R1, 44=S1, 45=T1, 46=U1, 47=V1, 48=W1, 49=X1, 50=Y1,
51=Z1, 52=A2, 53=B2, 54=C2, 55= 02, 56=E2, 57=F2, 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 eto-
dom TextFile( ) (interfejsa Collection), a zatim te rei koristi kao izvor podataka za do-
bijeni 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. (In-
terfejs 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.)
boolean addAllf Dodaje kolekciji sve elemente iz argumenta. Vraa true ako je dodat
Kolekcija<? extends T>) neki element. (Opciona metoda.)
void clear( ) Uklanja sve elemente iz kontejnera. (Opciona metoda.)
boolean contains(T) Vraa true ako kontejner sadri argument.
boolean containsAII( Vraa true ako kontejner sadri sve elemente iz argumenta.
Kolekcija<?>)
boolean isEmpty( ) Vraa true ako u kontejneru nema elemenata.
64 4 Misliti na Javi

lterator<T> ite rato rf) Vraa lterator<T> koji se moe koristiti za kretanje kroz elemente kon-
tejnera.
boolean remove(Object) Ako se argument nalazi u kontejneru, uklanja sejedna instanca tog ele-
menta. Vraa true akoje neto uklonjeno. (Opciona metoda).
boolean removeAII) Uklanja sve elemente koje sadri argument. Vraa true ako je neto
Kolekcija<?>) uklonjeno. (Opciona metoda).
boolean retainAllf Zadrava samo elemente koji se nalaze u argumentu (skupovni presek).
Kolekcija<?>) Vraa true ako se neto promenilo. (Opciona metoda).
int size( J Vraa broj elemenata u kontejneru.
ObjectfJ to A rra y () Vraa niz koji sadri sve elemente kontejnera.
<T> T[] toArray(T[J a) 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[] args) {
C o l 1ection<String> c = new Ar ra yL i s t < S t r i n g > ( ) ;
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) = " + Col lections.max(c)) ;
print("Collections.min(c) = " + C o l l ec ti on s. mi n( c) );
// 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) = true
c. co nt ai ns Al1 (c2) = true
[ANGOLA, BENIN]
c2.isEmpty() = true
[ALGERIA, ANGOLA, BENIN, B0TSWANA, BURKINA FASO, BURUNDI]
posle c . c l e a r O : []
* ///:-
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 de-
finicije tih m etoda.
To je veom a neobian nain definisanja interfejsa. Kao to ste videli, u objektno ori-
jentisanom 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 alje-
te 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 bez-
bednost tipova u vreme prevoenja.
Nije sve ba tako strano. U kolekciji, listi, skupu ili m api, prevodilac vas jo uvek ogra-
niava 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 ob-
jekat, 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 kolek-
ciju: nijedna m etoda za itanje kolekcije nije opciona.
Zato bi iko zadao da je m etoda opciona? Ovakav pristup spreava eksploziju broja in-
terfejsa. Drugaiji naini projektovanja biblioteke kontejnera uvek se zavre sa nerazm r-
sivom 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; ope-
racije 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 napravi-
te novu kolekciju, da pri tom ne obezbedite definicije svih m etoda u interfejsu
C ollection, a da je ipak uklopite u postojeu biblioteku.

4 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".
5 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 pogla-
vlju 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 pro-
date 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 sto-
ga 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 pro-
vere 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 ne-
prom enljive duine. Takav kontejner (u gornjem prim eru, listu) od niza proizvodi m eto-
da 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 nepro-
menljivih 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 (): 11 + 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 L".split(" "));
test("Promenljiva kopija", new Ar ra yL is t< St ri ng >( lis ta ));
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)));
}
} /* 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 pera-
tionException 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 sta-
tinu 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 za-


nimljivo, 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( ) proiz-
vodi listu koja se ne m oe menjati. Kao to vidite iz rezultata, m oete m o difikovati ele-
m 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 tre-
balo 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 ne-
prom 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 Col-
lection ne obuhvata. Napiite verziju program a Nepodrzane.java za testiranje tih dodat-
nih 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 itera-
t o r ( ) za dobijanje iteratora niza, postoje i druge m etode koje m ogu da budu korisne.
Svaka od m etoda u prim eru koji slei obavlja drugu grupu aktivnosti: ono to svaka li-
sta m oe da uradi (osnovniTest( )), kretanje po listi pom ou iteratora
(k retanjeIteratoroin( )) u odnosu na izm enu objekata pom ou iteratora (prom enalte-
ratorom 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 1
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
/
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 a d d ():
i t . n ex t( );
// Obrii element iza upravo napravljenog:
i t .r e m o v e ( ) ;
// Mora se pomeriti na element posle remove():
i t .n e x t ();
// Promeni element iza obrisanog:
i t .set ("47");
}
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() {
LinkedList<String> 11 = new Li nk ed List<String>();
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);
}
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 od-


govarajua sintaksa, a njihova p o v ratna vrednost se nigde ne koristi, iako se pam ti. U ne-
kim 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, Linked-
List je dvostruko ulanana lista, to znai da odrava veze u oba sm era). Napravite sopst-
veni 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. Na-
piite 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 in-
terfejsa 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 suprot-
nom, 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.
HashSet* Klasa koja se koristi za skupove gde je vano brzo pronalaenje ele-
menata. Objekti moraju da definiu i metodu hash C o d ef).
TreeSet Ureden skup u obliku stabla. Iz ovog skupa mo/cte da izdvojite niz
ureden u odreenom redosledu. Elementi moraju da definiu i interfejs
Comparable.
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 skla-
diiti jedan int i ispisuje ga m etodom to S trin g ( ). Poto sve klase uskladitene u skupovi-
ma 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 ob-
jekat 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 ko-
rien 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 ko-
ristio jednostavnu i oiglednu konstrukciju r e tu rn i-i2. Iako je to esta greka u pro-
gram 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 fal-
se za neko poreenje, onda bi co m p areT o () za isto poreenje trebalo da da rezultat raz-
liit 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 pozi-
va 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 me-
tode 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 sku-


povim a koji te operacije zahtevaju, sve se rui. Smetanje objekta tipa TipSkupa ili Tip-
Stabla 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 to-
kom 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 Com-
parable, 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 sle-
dee 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 elemen-
tim 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 sa objektom tipa TreeSet.
import java.util
import static ne t. mi nd vi ew .u ti l.Print.*;

public class PrimerZaSortedSet {


public static void main(String[] args) {
SortedSet<String> sortedSet = new T r e e Se t< St ri ng >( );
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);
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 kon-
tejnera 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 pripad-
ne 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 ++]; }
}
public static void main(String[] args) {
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 objc-
kata. Zanimljiviji problem je lista zadataka, gde svaki objekat sadri znakovni niz i po jed-
nu 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() {
return Character.toString(primarni) +
sekundarni + " + 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) {
ListaZadataka ListaZadataka = new Li st aZ ad at ak a();
Li st aZ ad at ak a. ad d( "Ba citi smee", 'C', 4);
ListaZadataka.add("Nahraniti psa", 'A', 2);
List aZ ad a t a k a . a d d ("Nahraniti ptiicu", 'B', 7);
ListaZadataka.add("Pokositi travu", 'C', 3);
ListaZadataka.add("Zaliti travnjak", 'A', 1);
L i st aZ ad at ak a. ad d("Nahraniti maku", 'B', 1);
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 travu
C4: Baciti 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 ja-
va.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 reali-
zuje 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 do-
date 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) {


Dvored<Integer> di = new Dv or e d < I n t e g e r > ( ) ;
testPopunj avan ja (d i);
pr i n t ( d i ) ;
while(di.size() != 0)
pr i n t n b ( d i .r e mo v e F i r s t () + 11 ");
print();
testPopu nj av an ja (d i);
w h i l e ( d i .size() != 0)
printnb(di.removeLast() + " ");
}
} /* 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 ni-
za ) jeste da odrava parove klju-vrednost (asocijacije, pridruivanja, m apiranja, presli-
kavanja), tako da vrednost m oete pronai pom ou kljua. Standardna Javina biblioteka
sadri razliite osnovne realizacije Mape: HashMap, TreeMap, LinkedHashMap, Weak-
HashMap, 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 e-


toda 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 ispi-
suje 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 ve-
liinu, 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 Linked-
HashMap 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 me-
todom 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 hei-
ranjc (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 i-
zovana 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 konstant-
nom vremenu. Performanse se mogu podesiti preko konstruktora koji
omoguuju zadavanje kapaciteta i faktora optereenja tabele transformisa-
nih kljueva.
LinkedHashMap 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.
TreeMap Realizacija napravljena na osnovu crveno-crnog stabla. Kada pogledate
kljueve ili parove, oni e biti u ureenom poretku (koji odreuje Compa-
rable ili Comparator). Sutina primene kontejnera TreeMap jeste da re-
zultate dobijate u ureenom poretku. TreeMap jejedina Mapa koja ima
metodu subM ap(), dakle jedina koja moe da vrati deo stabla.
WeakHashMap 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.
ConcurrentHashMap Mapa koja bezbedno radi u vienitnom radu, a da nema zakljuavanje
zbog sinhronizacije. Ovo je objanjeno u poglavlju Paralelno izvravanje.
ldentityHashMap Mapa transformisanih kljueva koja za poreenje kljueva upotrebljava ==
umesto metode equals( J. Samo za reavanje posebnih vrsta problema;
mje za optu upotrebu.

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 po-
m 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, 19,
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

* ///:-
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 kolek-
ciji 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 Sor-
tedMap 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, 2=C0, 3=D0, 4=E0, 5=F0, 6=G0, 7=H0, 8=10, 9=J0}
0
9
3
7
{3=D0, 4=E0, 5=F0, 6=G0}
{0=A0, 1=B0, 2=C0, 3=D0, 4=E0, 5=F0, 5=G0}
{3=D0, 4=E0, 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 prolas-
ka moete videti). Sem toga, realizacija LinkedHashMape moe se u konstruktoru kon-
figurisati 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 i-
ste smee cia bi se utedeo m em orijski prostor. Evo jednostavnog prim era u kojem su pri-
kazane 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 po-
trebnog koda. Na prim er, zamislite sistem za prognoziranje vrem ena koji objekte tipa
M edved pridruuje objektim a tipa Prognoza. To izgleda prilino jednostavno - napravi-
te 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 Prognozapri-
druen objektu #3 tipa Medved. Klasa Prognoza sadri objekat tipa boolean inicijalizo-
van 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 Med-
ved 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 vi-
deti da m ora biti u Mapi).
Sve izgleda veom a jednostavno, aii ne radi - ne m oe da se pronae klju za #3. Pro-
blem 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 dru-
gog 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 jedna-
kosti 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 ob-
jekata, 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 de-
taljnije 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 funk-
cionisati.
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 sle-
dee: 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 obje-
kat 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 Asoci-
jativniNiz.java, u narednom se interfejs Map p o tp u n o realizuje, to objanjava posto-
janje 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 Asocijativ-
niNiz.java). Ovo je posledica tako kasnog ubacivanja generikih tipova u J a v u - da su ge-
neriki 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 e-
lite 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 jed-
nostavno koristi m etodu h ash C od e() objekta kljuc. Iako je ovo reenje veom a jednostav-
no 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 reali-
zacija 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 pro-
veriii 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 najspo-
riji 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 kljue-
va), 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 o-
gue ako im ate neprom enljiv broj vrednosti), onda biste imali savrenu fu n k c iju za trans-
form isanje kljueva, ali to je poseban sluaj.7 U svim drugim sluajevima, sudaranjem se
bavi spoljno nadovezivanje (engl. externalchaining): niz ne pokazuje neposredno na vredn-
ost, 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 transfor-
misanje 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 upo-
rediti 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 jednostav-
nu 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 kontej-
n 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 su-
dare: 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 uba-
citi 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 po-
trae 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 pre-
vie 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 te-
stirajte 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 hei-
ranja u Javi upotrebljavaju veliinu jednaku nekom step en u b roja dva (a to je rezultat opsenog ispi-
tivanja). 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 ,,pro-
ba 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 Stavka-
Mape 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 ter-
m 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 svo-
di 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 va-
dite objekte iz m ape. Stoga ako se rezultat vae m etode h a s h C o d e () bude m enjao u za-
visnosti 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 pro-
gram 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 infor-
macije iz objekta koje ga identifikuju smisleno.
Jedan prim er vidi se u klasi String. Znakovni nizovi (Stringovi) imaju posebno obe-
leje: 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 zna-
kovnog 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 vredno-
sti 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 ravno-
m erno raspodeljene rezultate funkcije za heiranje.
U knjizi Efikasno program iranje na Javi (M ikro knjiga, 2004), Joshua Bloch daje osnov-
ni 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 Algoritam za izraunavanje


booleari c = (f ? 0 : 1)
byte, char, short ili int c = (int)f
long c = (int)(f A (f > 3 2 ))
float c = Float.floatTolntBits(f);
double long 1 = Double.doubleToLongBits(f);
c = (int)(l A (1 > 32))
Object, gde metoda equals( ) c = f.hashCode( )
poziva equals( ) za to polje
Niz 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 : " + id +
11 hashCode(): " + hashCode();
}
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. Prebroja-
vanje se obavlja u konstruktoru, iteriranjem kroz statinu listu ArrayList u kojoj su uskla-
diteni 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 Pre-
brojanZnakovniNiz, 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 za-
petljano. 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 no-
go 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 inici-
jalizuje 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 funk-
cionalnost odreenog interfejsa, kako da izaberete njegovu realizaciju?
Svaka realizacija ima svoja obeleja, prednosti i nedostatke. Na prim er, na slici s poet-
ka 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 no-
vim 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 reali-
zuju interfejs List, elem entarne List operacije su iste bez obzira na to koju od njih upo-
trebite. 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 po-
zadini skupa TreeSet nalazi se m apa TreeMap koja uvek daje ureen skup. Realizaciju bi-
rate 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 slua-
jevim 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 funk-
cije 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 po-
zivanja 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 pri-
stup Tem plate M ethod, u ovom sluaju je osnovni kod (onaj koji se ne m enja) u zasebnoj
klasi Tester.10 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 para-
m 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 sta-
tine 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 ni-
zove (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 , in t p e tlje ) {
th is .v e lic in a = v e lic in a ;
th is . p e t lje = 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 od-
govarajui 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 k o n t e jn e r a .
im p o r t j a v a . u t i l

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 rezul-
tata prilikom ispisa. Standardnu irinu za form atiranje m enjate m odifikovanjem statine
vrednosti sirinaPolja. Metoda prikaziZaglavlje() form atira i ispisuje zaglavlje s podaci-
ma 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 to-
kom 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 gra-
nularnost 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 kontej-
nera, 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
100 130 164
1000 129 165
10000 129 165
A rra y L is t
v e l. add get set ite ra d d in s e r t remove
10 121 139 191 435 3952 446
100 72 141 191 247 3934 296
1000 98 141 194 839 2202 923
10000 122 144 190 6880 14042 7333
L in k e d L i s t
v e l. add get s e t it e r a d c 1 i n s e r t remove
10 182 164 198 658 366 262
100 106 202 230 457 108 201
1000 133 1289 1353 430 136 239
10000 172 13648 13187 435 255 239
- V e c to r -
v e l. add get s e t it e r a d d 1 i n s e r t remove
10 129 145 187 290 3635 253
100 72 144 190 263 3691 292
1000 99 145 193 846 2162 927
10000 108 145 186 6871 14730 7135
-------- I s p i t i v a n j a reda za e ka n je ---------
v e l. addFi r s t addLast rmFi r s t rmLast
10 199 163 251 253
100 98 92 180 179
1000 99 93 216 212
10000 111 109 262 384
*///--

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 li-
ste. 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 slua-
ju, 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 ope-
racija 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 nasu-


m 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 re-
lativno 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 Linked-
List treba sam o da povee nov elem ent i ne m ora da m enja ost?i -h liste, p a bi reijski tro-
kovi 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 veo-
ma 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 (za-
nem 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 napra-


vljena na osnovu niza neznatno je bra od kontejnera tipa ArrayList, ali su iste te opera-
cije 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 pro-
gram iranju za paralelno izvravanje. N ju em o razm otriti u poglavlju Paralelno izvra-
van 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() - napra-
vljenu 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 nje-
gove perform anse i perform anse objekta tipa A rrayL ist< Integer> . Neka poreenje per-
form 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 Ar-
rayList za brza prolaenja kroz kontejner i operacije g e t ( ). Ispitajte ga prepravljenim pro-
gram om Perform anseL ista.java.

Opasnosti od mikroporeenja performansi


Kada piete program e za m ikroporeenjeperform ansi, uvajte se da ne pretpostavljate pre-
vie i toliko suzite testove da oni m ere vreme izvravanja sam o onih operacija koje se ispi-
tuju. 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 izvra-


vanja 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 ne-
zavisnih 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 rezul-
tati 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 1.0?
/ / {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);
}
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 pre-
variti. 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 me-
toda 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 LinkedHash-
Set. 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 = s k u p .ite ra to r();


w h ile (it.h a s N e x t())
it.n e x t();
}
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 501 264 68
1000 714 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 - -
v e l. add c o n t a in s i te ra te
10 350 65 83
100 270 74 55
1000 303 111 54
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 pot-
rebne 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 zna-
kovne nizove (String), a ne Integere. Za pravljenje ispitnih vrednosti upotrebite Genera-
to 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 [ ] a rg s) {
i f ( a r g s . l e n g t h > 0)
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 ) ;
}
} /* Is p is : (p r im e r)

v e l. put get i t e r a t e
10 748 168 100
100 506 264 76
1000 771 450 78
10000 2962 561 83
-------- HashMap
v e l. put get i t e r a t e
10 281 76 93
100 179 70 73
1000 267 102 72
10000 1305 265 97
-- LinkedHashMap
v e l. put get i t e r a t e
10 354 100 72
100 273 89 50
1000 385 222 56
10000 2787 341 56
IdentityHashMap ----------
v e l. put get i t e r a t e
10 290 144 101
100 204 287 132
1000 508 336 77
10000 767 266 56
WeakHashMap -
vel . put get i t e r a t e
10 484 146 151
100 292 126 117
1000 411 136 152
10000 2165 138 555
----- Hashtable - -
v e l. put get i t e r a t e
10 264 113 113
100 181 105 76
1000 260 201 80
10000 1245 134 77
*///:-
700 Misliti na Javi

U svim realizacijama interfejsa Map, sem u IdentityHashMap, s porastom Mape um e-


tanje 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 ehani-
zam 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 ek-
splicitno 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 upo-
trebfjavate 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 upot-
rebljava ==, 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 iznie-
nite m etodu p u t() tako da nakon unoenja svakog para poziva s o r t( ), a izm enite i me-
todu g e t ( ) tako da za pronalaenje kljua koristi ColIections.binarySearch( ). Uporedite
perform anse nove verzije i starih verzija.
Vreba37: (2) Izmenite kontejner JednostavnaHashMapa tako da upotrebljava ArrayLi-
ste 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 kon-
kretnoj 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 opti-
m alna za um etanje i pretraivanje (ali e usporiti postupak prolaska kroz kontejner
iteratorom ). HashMap i HashSet im aju konstruktore koji om oguuju zadavanje fak-
tora 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 trans-
fo 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 au-
tom atskog ponovnog transform isanja kljueva.11
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 pre-
traivanja ove mape, zatim pokuajte da poveate brzinu tako to ete napraviti nov kon-
tejner 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 po-
ziva 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 me-
toda 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 kva-
d 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( Proizvode dinamiki bezbednu (u pogledu tipova) realiza-


Kolekcija<T>, Class<T> tip) ciju Kolekcije ili nekog njenog konkretnog podtipa. Kori-
checkedList( stite ih kada nije mogue upotrebiti statiki proverenu
List<T>, Class<T> tip) verziju.
checkedM ap(M ap<K,V>,
Class<K> keyType,
Class<V> valueType) U poglavlju Generiki tipovi ove metode su bile navedene
checkedSet(Set<T>, pod naslovom Dinamika bezbednost tipova".
Class<T> tip)
checkedSortedM ap (
SortedMap<K,V>,
Class<K> keyType,
Class<V> valueType)
checkedSortedSet(
SortedSet<T>,
Class<T> tip)
max(Kolekcija) Daje najvei ili najmanji element argumenta koristei me-
m in(Kolekcija) todu prirodnog poredenja objekata u Kolekciji.
m ax(Kolekcija, Komparator) Daje najvei ili najmanji element Kolekcije koristei Ko-
m in(Kolekcija, Komparator) mparator
indexOfSubList(List izvor. Proizvodi poetni indeks prvog mesta na kojem se
List odredite) odredite pojavljuje unutar izvora ili -1 ako takvog mesta
nema.
lastlndexO fSubList(List izvor. Proizvodi poetni indeks poslednjeg mesta na kojem se
List odredite) odredite pojavljuje unutar izvora ili -1 ako takvog mesta
nema.
replaceAII(List<T>, Zamenjuje sve primerke objekta staraVrednost objektom
T staraVrednost, T novaVrednost) novaVrednost
reverse(List) Obre redosied svih eiemenata liste.
reverseOrder( ) Vraa Kom parator koji obre prirodni poredak kolekcije
reverseOrder( objekata koja realizuje interfejs Comparable<T> Druga
Komparator<T>) verzija obre redosled datog Kom paratora
rotate(List, int udaljenost) Premeta sve elemente unapred za udaljenost;
one s kraja vraa na poetak.
shuffle(List) Nasumino permutuje datu listu. Prvi obiik ima sopstveni
shufflefList, Random) izvor siuajnih brojeva. dok ga u drugom obliku sami
zadajete.
sort(List<T>) Ureduje listu List<T> po njeni>m prirodnom redosledu. U
sort(List<T>, drugom obliku sami zadajete Kom parator za uredivanje
Kom parator<? super T> c)
copy(List<? super T> odredite. Kopira elem ente iz izvora u odredite
List<? extends T> izvor)
swap(List, int i, int j) Zamenjuje elemente na mestima i i j u listi List Veiovatno
bra od one koju moete sami da napiete.
fill(List<? super T>, T x) Zamenjuje sve elemente liste objektom x
Poglavfje 17: Detal)no razmatranje kontejnera 703

nCopies{int n, T x) Vraa nepromenljivu listu List<T> veliine n ije sve refe-


rence upuuju na objekat x
disjoint(Kolekcija, Kolekcija) Vraa true ako date kolekcije nemaju zajednikih
elemenata.
frequency(Kolekcija, Object x) Vraa broj elemenata Kolekcijejednakih objektu x.
emptyList( ) Vraa nepromenljivu praznu listu, mapu ili skup Oni su
emptyMap( ) generiki, pa e rezultujua Kolekcija biti
emptySet( ) parametrizovana u eljeni tip.
singletonfT x) Vraa nepromenljivu listu, mapu ili skup (Set<T>, List<T>
singletonList(T x) odnosno Map<K,V>) sa samojednom stavkom, napra-
singletonMap(K kljuc, V vrednost) vljenom na osnovu datih argumenata.
list(Enumeration<T> e) Proizvodi ArrayList<T> koja sadri elemente u poretku
kojim ih vraa (stari) Enumeration (prethodnik
Iteratora). Za konvertovanje starog koda.
enumeration(Kolekcija <T>) Proizvodi stari Enumeration<T> za dati argument.

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
// Jednostavni primeri usiunih metoda klase Collections.
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: " + C o l 1ections.max(lis t a ) ) ;
print("min: " + Collections.min(lista));
print("mks uz komparator: " + C o l1ec ti ons.max(lis t a ,
Stri n g .C A S E I N S E N S ITI V E O R D E R ) );
print("min uz komparator: " + Col 1 e c ti on s. mi n(1 ista,
Stri ng .C ASE_INSENSITIVE_ORDER));
List<String> podlista =
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)?: false
mks: tri
min: etiri
mks uz komparator: Dva
min uz komparator: pet
i n d e ks Po dl is te: 3
poslednjilndeksPodliste: 3
replaceAll: [Da, Dva, tri, Cetiri, pet, sest, Da]
obrnuto: [Da, sest, pet, Cetiri, t r i , Dva, Da]
rotirano: [tri, Dva, Da, Da, sest, pet, Cetiri]
kopija: [u, matrici, Da, sest, pet, Cetiri]
zamena: [Cetiri, matrici, Da, sest, pet, u]
ispreturano: [sest, matrici, Cetiri, Da, pet, u]
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_ORDERCom-
parator; 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 po-
m ou nekog kom paratora, pom ou istog kom paratora m orate obaviti i b inarno pretrai-
vanje - 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 ispre-
turane (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 e-
nerator. 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 o-
guuje 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 pre-
vodenja, 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 re-
ferencu kontejnera referencom koju proizvodi poziv ,,unm odifiable m etode. Time izbe-
gavate 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 zadri-
te 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 me-


tode, kao u gornjem prim eru. Tako se ne m oe dogoiti da sluajno eksponirate ne-
sinhronizovanu 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 ite-
rato 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 t-
M 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 Re-
ference 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 ob-
jekta - 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 mo-
gunost 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 do-
stian 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 re-
ference (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 Refe-
renceQueue. 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 eak-
HashMap. 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 o-
guuje 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 po-
sebno; 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 [ ] a rg s ) {
i n t v e l i c i n a = 1000;
/ / 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 kontej-
nerim a, m ada nikada ne bi trebalo da ih koristite dok piete nov kod. M eutim , stari kon-
tejneri 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. Nje-
govi nedostaci su suvie brojni da bi se ovde naveli (potraite prvo izdanje ove knjige na lo-
kaciji 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 ter-
min 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 ko-
riste, to nije preporuljivo ali je barem bezopasno. Iako bi prilikom pisanja novih pro-
gram 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 po-
tom 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 oe-
te 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 kla-
se 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 Linked-
List) 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 ob-
jektu klase Stack. To je m ogue zato to je, zahvaljujui nasleivanju, stek jedna vrsta vek-
tora. 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 Linked-
List 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 -is-
kljueno. 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 op-
timizaciju 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 : " + it);
is p is iB i to v e (b i);

/ / 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 njego-
ve 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 Enum-
Set 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 o-
rati da znate vie o kontejnerim a kako biste mogli pravilno da ih upotrebljavate - kon-
kretno, 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 kontej-
nerskih klasa jezika C++ i ostalih slinih biblioteka kontejnera. Rezultat ponegde izgleda
udno. Za razliku od nekih drugih odluka sprovedenih u starim Java bibliotekama, te ud-
novatosti nisu nastale sluajno, nego su posledica odluka paljivo odvaganih kako bi se po-
stigao 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 Sohi-
tion 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 je-
zika. 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 re-
ne veze itd.), ve im a i raznih naina za to (sekvencijalni, nasum ian pristup, baferi, bi-
narni, znakovni, po redovim a, po reima itd.).
Projektanti Javinih biblioteka pristupili su reavanju ovog problem a tako to su napra-
vili velikog broja klasa. Zapravo, Javin U/I sistem im a toliko m nogo klasa da na prvi po-
gled 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 orijenti-
sanim, 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 po-
boljanja 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. Pri-
lino 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 predzna-
nja 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 nji-
hovog 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 meto-
dom 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 dru-
gaiji 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 ograni-
enu 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 abe-
cednom 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 inter-
fejs 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 reali-
zuje 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 direkto-
rijum, 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 vr-
sto 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 kla-
sa 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 varija-
nata) tako da FilenameFilter otvara i ita svaku datoteku (uslunom metodom net.m in-
dview.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 pri-


ma objekat tipa File i od njegovih datoteka pravi ureenu listu direktorijuma. Klasi do-
dajte 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 varija-
nata) 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 ) { // P r e k lo p lje n o
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 ) ;
}
/ / 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) {


d a to te ke .a dd Al1 ( o s t al o. da to te ke );
dirmi.addAl 1 (ostalo.di r m i ) ;
}
public String toString() {
return " d i r ek to ri ju mi: " + PPrint.pformat(dirmi) +
"\n\ndatoteke: " + 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) { // Preklopljeno
return rekurzDirme(pocetak, regiz);
}
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) { / / Sve
r e t u r n re k u r z D ir m e ( p o c e ta k ,
}
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 ) {
InfoStabla result = new InfoStabla();
for(File stavka : p o cD ir .1 i s tF i1e s ()) {
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 di-
rektorijuma, povratna vrednost je zapravo ,,n-torka objekata - jedna lista koja sadri
obine datoteke i druga koja sadri direktorijume. Polja su namerno deklarisana kao jav-
na (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 napra-
viti privatnim. Obratite panju na to da InfoStabla realizuje Iterable<FUe> koji proizvo-
di 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 jed-
nog kontejnera u istom redu. U velikim kolekcijama to postaje teko itljivo, pa bi se mo-
glo 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 do-
stupne. 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: (primer)
[.\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
* ///:-

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 ( " . 11 + s p o l j ) )
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 );
}
} /* ( 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 ) *///:-

Interfejs Strategy ugneden je u klasu O bradaD atoteka. Da biste ga realizovali, mo-


rate 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 proe-


te 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 po-
trebno), 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 re-
gularnim 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 direktoriju-
ma ako ona ne postoji. Moete da ispitujete i karakteristike datoteka (veliinu, datum po-
slednje 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 + 11 p o s t o j i " ) ;
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 kori-
stiti 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 direk-
torijuma 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 hi-
jerarhiji 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 baj-
tova. 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 do-
bijanja 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 In-
putStream , 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 de-
talje, 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 izla-
ze 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 Enter-
prise 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 do-
punske klase, tj. za dodeljivanje atributa ili korisnih interfejsa ulaznim tokovima. O ovo-
me e biti rei u nastavku.

Tabela U/l-1 Tipovi ulaznih tokova


Klasa Funkcija Argum enti konstruktora
Kako se koristi
ByteArraylnput- Omoguuje da se memorijski Bafer iz kojeg se izvlae bajtovi.
Stream blok koristi kao ulazni tok
Kao Izvor podataka. Poveite je sa objektom
klase FilterlnputStream da biste dcbili kori-
stan interfejs.
StringBufferlnput- Konvertuje String String. Stvarna realizacija zapravo koristi
Stream u InputStream StringBuffer
Kao izvor podataka. Poveiteje sa objektom
klase FilterlnputStream da biste dobili kori-
stan interfejs.
File- itanje informacija iz datoteke String koji predstavlja ime datoteke, ili objek-
InputStream ti klase File ili FileDescriptor
Kao izvor podataka. Poveite je sa objektom
klase FilterlnputStream da biste dobili kori-
stan interfejs.
Pipedlnput Daje podatke koji se upisuju PipedOutputStream
Stream u pridrueni PipedOutput-
Kao izvor podataka u vienitnom radu. Pove-
Stream. Realizuje slanje kroz
iteje sa objektom klase FilterlnputStream
cevovod. da biste dobili koristan interfejs.
Sequencelnput- Konvertuje dva ili vie objekata Dva objekta klase InputStream ili Enume-
Stream klase InputStream ujedan ula- ration za kontejner objekata klase Input-
zm tok. Stream
Kao izvor podataka. Poveite je sa objektom
klase FilterlnputStream da biste dobili kori-
stan interfejs.
Filterlnput Stream Apstraktna klasa, interfejs za do- Pogledajte tabelu U/l -3.
punske klase koje obezbeduju
Pogledajte tabelu U/l -3.
korisne funkcije za ulazne toko-
ve. 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 do-
daju 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
ByteArray- Pravi bafer u memoriji. Svi podaci Opciona poetna veliina bafera.
O utputStream koje aljete u tok smetaju se u taj
Za zadavanje odredita podataka.
bafer.
Poveite je sa objektom klase Filter-
O utputStream da biste dobili kori-
stan interfejs.
File O utput- Slanje informacija u datoteku. Znakovni niz koji predstavlja ime da-
Stream toteke, ili objekte klase File ili File-
Descriptor

Za zadavanje odredita podataka.


Poveite je sa objektom klase Filter-
OutputStream da biste dobili korr
stan interfejs.
PipedO utput- Sve informacije koje upisujete PipedlnputStream
Stream u ovaj tok automatski zavravaju
Za zadavanje odredita podataka
kao ulaz za odgovarajui Piped-
u vienitnom radu. Poveiteje sa
InputStream Realizuje prijem iz
objektom klase FilterOutputStre-
cevovoda. am da biste dobili koristan interfejs.
FilterO utput- Apstraktna klasa. osnova za do- Pogledajte tabelu U/l -4
Stream punske klase koje obezbeduju Pogledajte tabelu U/l -4.
korisne funkcije drugim izlaznim
tokovima. Pogledajte tabelu U/l -4.

Dodavanje atributa i korisnih interfejsa


Dekoratori su predstavljeni u poglavlju Generiki tipovi , na stranici 484. Javina U/I bibli-
oteka zahteva veliki broj kombinacija funkcija i zato se koristi projektni obrazac Decora-
tor (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 kla-
sa, 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 ataln-
putStream 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 kla-
sa, uz srodnu klasu D ataO utputStream , omoguuje premetanje prostih tipova podata-
ka 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 eksperi-
mentalni Javin prevodilac pisan na Javi), pa ih verovatno neete koristiti u svakodnev-
nom programiranju.
Gotovo uvek morate da baferiete ulaz, bez obzira na to s kojim U/I ureajem ste po-
vezani, pa bi bilo korisnije da U/I biblioteka ima specijalan sluaj (ili prosto poziv meto-
de) 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 InputStream
DataOutputStream , da bi se iz
Sadri kompletan interfejs koji omoguu-
toka na prenosiv nain itali prosti
je itanje prostih tipova.
tipovi (int, char, long itd.).
Bufferedlnput- Koristite je za smanjenje InputStream, uz opcionu veliinu bafe-
Stream broja fizikih operacija ra.
itanja svaki put kada vam zatreba
Ne obezbeduje interfejs sama po sebi,
jo podataka. Ovom klasom ka-
ve samo procesu dodaje bafer. Pridrui-
ete toku: Upotrebi bafer".
te jo j objekat koji realizuje interfejs.
LineNumber- Prati brojeve redova u ulaznom InputStream
InputStream toku: moete da pozovete get-
Samo dodaje numerisanje redova. pa
LineNum ber( ) i setLinelMum-
ete jo j verovatno pridruiti objekat koji
ber(int)
realizuje interfejs.
Pushbacklnput- Ima skladite za vraanje jednog InputStream
Stream bajta u koji moete da smestite
Obino se koristi u analizatoru za prevo-
poslenji proitani znak.
dioca. Verovatno je neete koristiti.
734 Misliti na Javi

Filtriranje izlaznog toka


Odgovarajua klasa za D atalnputStream je D ataO utputStream ; ona formatira sve pro-
ste tipove i objekte klase String u tok na takav nain da ih moe itati svaki tok tipa Da-
talnputStream 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 utput-
Stream, iji je cilj da elemente podataka postavi u binarni niz na takav nain da ih Data-
InputStream 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 za-
vretku tampanja.
Klasa PrintStream moe da bude problematina zato to hvata sve izuzetke tipa IOEx-
ception (metodom checkE rror() morate eksplicitno da proverite da li je nastala do gre-
ka). 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 ko-
ristiti za ispisivanje rezultata.

Tabela U/l-4 Vrste filtriranih izlaznih tokova


Klasa Funkcija A rgum enti konstruktora
Kako se koristi
DataO utput- U kombinaciji s klasom DatalnputStre- O utputStream
Stream am koristi se za upis.vanje prostih tipova Sadr, k0mpletan mterfejs koji omogu-
(int, char, long it.| u tok na prenosiv uje uplsivanje prostlh t(pova
nain.
PrintStream Koristi se za formatiranje izlaza. OutputStream, uz opcioni argument
Za razliku od klase D ataO u tp u t- tipa boolean koji odreuje da li se
Stream koja skladiti podatke, Print- bafer prazni prilikom prelaska u sleei
Stream ih ispisuje. red.
Trebalo bi da bude konano" omotava-
nje za objekat kiase OutputStream
Verovatno eteje dosta koristiti.
Buffered- Koristite je da biste izbegli flziko upi- O utputStream, uz opcionu veliinu
O utputStream sivanje pri svakom slanju podataka. bafera.
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( ) teJQJ 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 kori-
stite, 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 oigled-
no 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 e-
snaestobitnim Unicode znakovima. Poto se Unicode koristi za internacionalizaciju (a Ja-
vin 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, projekto-
vane 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 bi-
narno 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 Reader
Konvertor InputStream Reader
OutputStream Wnter
Konvertor OutputStream\X/riter
FilelnputStream FileReader
FileOutputStream FileU/riter
StringBufferlnputStream StringReader
(zastarelo)
|nema odgovarajue klase) StringW riter
ByteArraylnputStream CharArrayReader
ByteArrayOutputStream CharArrayW riter

PipedlnputStream PipedReader
PipedOutputStream PipedW riter

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 hijerar-
hiji 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 orga-
nizacije klasa: za razliku od toka BufferedO utputStream koji je potklasa klase FilterOut-
putStream , 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


FilterlnputStream FilterReader
FilterOutputStream F ilterW riter (apstraktna klasa bez potklasa]
BufferedlnputStream BufferedReader (ima i metodu re a d L in e f))
BufferedOutputStream Buffered\X/riter
DatalnputStream Koristite DatalnputStream . osirn kada morate da koristite meto-
du re a d L in e f) U tom sluaju bi trebalo da koristite klasu Buffe-
redReader
PrintStream PrintW riter
LineNum berlnputStream LineNum berReader
(zastarelo)
StreamTokenizer StreamTokenizer (koristite konstruktor koji prihvata argument
tipa Reader)
PushBacklnputStream PushBackReader

Jedno pravilo je sasvim jasno: kad god elite da koristite readL ine(), to vie ne bi tre-
balo da radite pomou klase D atalnputStream (o tome ete dobiti upozorenje tokom
prevoenja), ve pomou klase BufferedReader. U svim ostalim sluajevima, D atalnput-
Stream se i dalje preporuuje.
Da bi se olakao prelazak na korienje klase PrintW riter, njeni konstruktori prihva-
taju 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 pro-
nalaenje 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 za-
pise. 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 In-
putStream 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 Da-
taO utputStream ). Ona ak ne koristi ni funkcije postojeih klasa InputStream ili Out-
putStream - to je sasvim posebna klasa, napisana kompletno od poetka, sa sopstvenim,
veinom osnovnim, metodama. RandomAccessFile nije deo hijerarhije zato to se pona-
a sasvim drugaije od ostalih U/I tipova: omoguuje kretanje unapred i unaza po dato-
teci. U svakom sluaju, ona postoji kao samostalan, direktan naslednik klase Object.
U osnovi, RandomAccessFile radi kao D atalnputStream u kombinaciji s klasom Da-
taO utputStream , uz metode getFilePointer() za pronalaenje pozicije unutar datoteke,
seek () za prelazak na novu poziciju unutar datoteke i le n g th () za odreivanje maksimal-
ne 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 ita-
nju (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 iz-
vedena 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 na-
stavku poglavlja.

Tipine primene U/l tokova


lako klase U/I tokova moete da kombinujete na razne naine, verovatno ete upotreblja-
vati 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 izu-
zetke 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 argu-
ment 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 pri-
hvata rei koje e traiti u datoteci. Ispiite sve redove u kojima se rei pronau.
Veba 11: (2) U primeru unutrasnjeklase/UpravljanjeStaklenomBastom.java, Kontro-
lerStakleneBaste 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 Dataln-
putStreani (a ne neku znakovno orijentisanu). To znai da morate koristiti klase hijerar-
hije 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 ByteAr-


rayInputStream , koristi se metoda getB ytes() klase String. Nakon toga imate odgovara-
jui ulazni tok koji ete proslediti klasi D atalnputStream .
Ako itate znakove iz toka tipa D atalnputStream bajt po bajt pomou metode read-
B y te(), svaka vrednost moe biti vaei rezultat, pa se povratna vrednost ne moe kori-
stiti 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 blo-
kiranja. 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 izu-
zetka. 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 bi-
ste 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 uobia-
jene 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 uobiaje-
ne 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 nji-
hove 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 to-
kovi 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 koren od 2");
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" ) ) ) ;
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 po-
datke 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 platforma-
ma. 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 ataln-
putStream 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 za-
visnosti od upotrebljenog skupa znakova. Ako radite (iskljuivo ili preteno) sa ASCII
znakovima (koji zauzimaju samo sedam bitova), korienjem Unicode zauzima se ogro-
man prostor i/ili propusni opseg; pa UTF-8 kodira ASCII znakove u jednom bajtu, a ne-
ASCII 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 poseb-
nu varijantu kodiranja UTF-8 za Javu (detaljno opisanu u HTML dokumentaciji tih me-
toda 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 mo-
raju 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 atalnputSt-
ream na Webu. Na osnovu programa CuvanjelPronalazenjePodataka.java, napiite
program koji skladiti i zatim rekonstruie sve mogue tipove s kojima rade klase Data-
O utputStream i D atalnputStream . Dokaite da se svi tipovi ispravno skladite i rekon-
struiu.

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 kre-
tanje po datoteci i promenu nasumino rasporeenih vrednosti na raspolaganju je meto-
da 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. Ran-
domAccessFile ima specifne metode za itanje i pisanje prostih tipova i UTF-8 znakov-
nih 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, "r");
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 ();
}
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 po-
drava dekorisanje, pa je zbog toga ne moete kombinovati ni s jednim aspektom potkla-
sa 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; datote-
ku 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 pres-
likane u memoriju.
Veba 16: (2) Proitajte HTM LdokumentacijuklaseRandomAccessFilenaW ebu. Na os-
novu 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 cevo-
vodi upotrebljavaju za komunikaciju izmeu niti. O ovome e, uz primer, biti rei u po-
glavlju 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 je-
ste 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 upi-
sivanje tekstualnih datoteka u obliku jednog znakovnog niza, a moete napraviti i Text-
FUe 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 me-
tode 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 mo-
rati 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 ja-
va.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 pro-
gram koji prebrojava razliite bajtove u datoteci.
Veba 20: (4) Pomou metode Direktorijum .prolazak( ) i klase BinarnaDatoteka, do-
kaite 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 re-
produkovan u Windowsu i mnogim drugim operativnim sistemima, a oznaava jedin-
st\'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 znaa-
jni zato to omoguuju lako povezivanje programa, a standardni izlazni tok jednog pro-
grama 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 Sy-
stem .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 od-
mah, 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 Rea-
der 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 ( ) != 0)
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
}
> 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 Print-
W riter ima konstruktor iji je argument objekat klase OutputStream . Stoga tim kon-
struktorom 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 argu-
menta 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 to-
kova 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 proi-
tate.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 ) ;
}
1 ///:-
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 pro-
grama 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 prouzro-
kuju 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 ko-
manda se prosleduje konstruktoru klase java.lang.ProcessBuilder (kojoj je potrebno da
ga dobije u obliku sekvence String objekata), i tako nastaje rezultujui objekat tipa Pro-
cessBuilder:

/ / : 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 11 +
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 re-
dovi 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 IzuzetakOS-
Izvrsenja 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 is-


poruuje 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. Po-
kaite 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 iskori-
stilo to ubrzanje, pa od njega imate koristi ak i kada u svojim programima ne koristite ek-
splicitno 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 ne-
mate 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 memo-
rijskog prostora da rezervie, i postoje metode za umetanje i vaenje podataka, bilo u si-
rovom 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 FileChan-
nel. Kanal je prilino jednostavan: moe m u se predati objekat tipa ByteBuffer radi i-
tanja 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 tipo-
va. 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 ni-
zom.
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 - za-
pravo, ovde e vam 1 K verovatno mnogo manje znaiti nego inae (moraete da ekspe-
rimentiete s tekuom aplikacijom da biste odredili najbolju veliinu bafera).
U potrazi za jo veom brzinom, mogli biste metodom allocateD irect() umesto me-
todom allocate() napraviti direktan bafer koji je jo tenje spregnut sa operativnim siste-
mom. 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 ap-
likacijom 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 pri-
premi 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 bi-
smo da pozovemo metodu clear( ) kao pripremu. To vidite u ovom jednostavnom pro-
gramu 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 spre-
man da od metode re a d () primi nove podatke.
Prethodni program nije idealan za obavljanje te vrste operacija. Postoje posebne me-
tode 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 na-
kon obrade metodom asC harB uffer() moe smatrati baferom CharBuffer, zato ne bis-
mo 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 me-
todu 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 pro-
grama 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 vred-
nosti 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 se d o d e li memorija:
i n t i = 0;
w h i 1e ( i + + < b b . 1 i m i t ())
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 vred-
nosti 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 tre-
ba 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 ShortBuf-
fer 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 iz-
mene 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 tipo-
va 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 ) ;
}
}
} /* Is p is :
99
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 pripada-
juem 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 ne-
kim drugim prostim tipom vrednosti, onda se ByteBuffer moe upisati neposredno u ka-
nal. 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 tu-
mai 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 r i n t n b ( d b . p o s i t i o n ( ) + 11 -> " + d b . g e t ( ) + " , ");
}
} /* Is p is :
B y t e B u f f e r 0 -> 0, 1 -> 0, 2 -> 0, 3 -> 0, 4 -> 0, 5 -> 0, 6 -> 0 , 7 -> 97,
C h a rB u ffe r 0 -> , 1 -> , 2 -> , 3 -> a,
F l o a t B u f f e r 0 -> 0 . 0 , 1 -> 1.36E-43,
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, 2 -> 0 , 3 - > 97,
D o u b le B u ffe r 0 -> 4.8E-322,
* ///:-

ByteBuffer je proizveen omotavanjem osmobajtnog niza tipa byte koji se zatim pri-
kazuje 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 97 bytes

a chars

0 0 0 97 shorts

0 97 ints

0.0 1.3 6E-43 floats

97 longs

4.8E-3 22 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 plat-
formama, najznaajniji bajt se smeta na najniu memorijsku adresu, dok se na little en-
dian 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 re-
dosled bajtova. ByteBuffer skladiti podatke u big endian obliku, a tako se podaci uvek
alju i preko mree. Redosled smetanja bajtova podataka u ByteBufferu moete prome-
niti metodom order( ), uz argument ByteOrder.BIG_ENDIAN ili ByteOr-
der.LITTLE_ENDIAN.
764 Misliti na Javi

Pogledajmo ByteBuffer koji sadri ova dva bajta:

bl b2

Ako ovebajtove proitate (protumaite) kao broj tipa short (ByteBuffer.asShortBuf-


fe r()), dobiete broj 97 (00000000 01100001), ali ukoliko preete na redosled bajtova lit-
tle endian, dobiete broj 24832 (01100001 00000000).
U narednom prim eru pokazano je kako se redosled bajtova u znakovima menja u za-
visnosti 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 (charAr-
ray) 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 po-
zvanom 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 dato-
teku, 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 Uslune


klase
'' ' K anali
F ile lnp ut? jtre a m Socket
F ile O u tp u tStream D a ta g ra m S o cke t
R a nd om A c cessFile ServerSocket

g e tC h a n n e l()
,w rite (B yte B u ffe r)
F ile C h a n n e l <- 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

pojavljuje se u adresnorc prostoru procesa j


----------------------- 1
^ a rra y ()/q e t(b y te f[)
b yte []
w ra p (b y te (j)
^ a rra y ()/q e tfc h a rf|) asC harBuffer()
ch ar(| C h a rB u ffe r
w ra p (c h a r[()
^ a rra y ()/q e t(d o u b le f|) a sD o u b le B u ffe r()
d o u b lc 'd D o u b le B u fte r
w ra p (d o u b le [j)
^ a rra y f|/q e t(flo a tffl asFloatB uffer()
flo a tfj F lo a tB u ffe r
w ra p (flo a t[j)
^ a rra y f)/q e tfin t aslntB uffer()
in t j j In tB u ffe r
w ra p (in t [))
^ a rra y f)/q e t(lo n q [|) asLongB uffer()
lo n g f j L o n g B u ffe r
w r a p ( lo n g f j)
a rr a y ( )/q e t(s h o r t(|) asS hortBuffer()
short(( S h o rtB u ffe r
w r a p ( S h o r tfj)
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! n e w E n c o d e rf)
Charset-forNarnef"8859 I
C h arse tE n cod er

ema kodiranja 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 in-
deksa, za njihovo resetovanje i za ispitivanje njihovih vrednosti.

capacity() Vraa kapacitet bafera.


c le a r() Brie sadraj bafera, indeksu potoaja zadaje vrednost nula, a indeksu kraja vred-
nost indeksa kapacitet. Ovom metodom istite postojei bafer.
flipl ) Indeksu kraja zadaje vrednost indeksa poloaja. a indeksu poloaja vrednost nula.
Ovom metodom se bafer priprema za itanje nakon upisivanja podataka.
lim it| ) Vraa vrednost indeksa kraj.
lim it(int kraj) Zadaje vrednost indeksa kraj.
mark( ) Indeksu markerzaaje vrednost indeksa poloaja.
position( ) Vraa vrednost indeksa poloaja.
positionfint Zadaje vrednost indeksa poloaja.
pol)
rem ainingl ) Vraa (kraj - poloaj).
hasRemai- Vraa true ako u baferu ima elemenata izmedu mesta na koja pokazuju indeksi po
ning( ) 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 nepo-
sredno s kanalom.
Evo kako izgleda bafer na ulazu u metodu sim etricnoP returanje():

rwi
J
u p o t r e b a B a f e r 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 funk-
cije 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 me-
tode m a rk (). Tada je stanje bafera:

I m ,!r | 1 M -) 1

/ s/
U p O t r e b a B a f e r 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 p O t r e b a B a f e r a
/
1gg I I krai I

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 upo-
trebiti 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
u p o t r e b a B a f e r a

I Pol I I k ra j |

Dva poziva metode p u t( ) upisuju c2 i zatim cl:

\/ \
p u o t r e b a B a f e r a
\
T
Tokom sledee iteracije petlje, indeksu marker se zadaje tekua vrednost indeksa po-
loaja:

\/ \/
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 po-
loaja i kraja. Dakle, da biste ispisali ceo sadraj bafera, indeksu poloaja morate me-
todom 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 pre-
velike da bi cele bile smetene u memoriju. Kada je datoteka delom preslikana u memo-
riju, 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 MB
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) Ran-
domAccessFile, pribavimo kanal za tu datoteku, i zatim pozovemo metodu n ia p () koja
proizvodi objekat tipa MappedByteBuffer, to je posebna vrsta direktnog bafera. Obra-
tite 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 pre-


vazilazi 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 per-
formansi, za preslikavanje datoteka u memoriju upotrebljene uslune metode pripadnog
operativnog sistema.

Performanse
Iako su performanse ,,starog U/I sistema zasnovanog na tokovima poboljane realizaci-
jom pomou nio klasa, obino se jo mnogo bre pristupa datotekama preslikanim u me-
moriju. 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 [ ] a rg s ) {
fo r(T e ste r te s t : is p itiv a n ja )
te s t.ru n T e s t();
}
} / * 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 upo-
trebljena u projektnom obrascu Template M ethod (ablonska metoda) za pravljenje
strukture za ispitivanje raznih realizacija metode te s t( ) defmisanih u anonimnim unu-
tranjim 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 FileOut-
putStream, 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 per-
formansama, 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 re-
sursu. Medutim, dve niti koje se takmie za istu datoteku mogu biti u razliitim JVM-ovi-
ma 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 ServerSoc-
ketC 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 me-
toda. 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 () blo-
kira sve dok ne pribavi bravu (engl. lock), ili dok nit koja je pozvala lo c k () ne bude pre-
kinuta, 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 in-
deksa poloaj+veliina, a datoteka se povea preko granice poloaj+veliina, onda taj
poveani deo datoteke nee biti zakljuan. Pozivi metoda za zakljuavanje bez argumena-
ta, 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 iskljui-
va) 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 ne-
zakljuane 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 MB
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 : "+ pocetak +" do + k r a j ) ;
} 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 ) ;
}
}
}
} 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 pristu-
pom 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 po-
zovete metodu release() kao to je uraeno u prethodnom programu.

Komprimovanje
Javina U/I biblioteka sadri klase koje podravaju itanje i upisivanje tokova u kompri-
movanom formatu. Te klase treba omotati oko postojeih U/I klasa.
Ove klase nisu izvedene iz klasa Reader i Writer, ve su deo hijerarhija klasa Input-
Streain 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 In-
putStream Reader i OutputStreamReader.)

Klasa za kom prim ovanje Funkcija


CheckedlnputStream Metoda G etCheckSum f ) vraa kontrolm zbir za bilo koji objekat klase
InputStream (nesamo dekomprimovanje).
CheckedOutputStream Metoda G etCheckSum f ) vraa kontrolni zbirza bilo koji objekat klase
O utputStream (ne samo komprimovanje).
DeflaterO utputStream Osnovna klasa za kompresiju.
ZipOutputStream Potklasa klase DeflaterO utputStream koja komprimuje podatke u
formatu Zip.
GZIPOutputStream Potklasa klase DeflaterOutputStream koja komprimuie podatke u for-
matu GZIP
InflaterlnputStream Osnovna klasa za dekomprimovanje.
ZiplnputStream Potklasa klase InflaterlnputStream koja dekomprimuie podatke
snimijene u formatu Zip
GZIPInputStream Potklasa klase InflaterlnputStream koja dekomprimuje podatke sni-
mljene 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 ko-
riste. Zbog toga s komprimovanim podacima moete lako da radite pom ou brojnih alat-
ki 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 kom-
primovati jedan tok podataka (a ne kontejner meusobno neslinih podataka). Evo pri-
mera 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 iz-


lazni tok u klase G ZIPO utputStream ili ZipO utputStream , a ulazni tok u klase GZIPIn-
putStream 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 to-


kovima: objekat in koristi klase iz hijerarhije Reader, dok konstruktor klase GZIPOut-
putStream 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 kori-
enje 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 [ ] args)
thro ws IOExce ption {
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 iskljui-
va) 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 ne-
zakljuane 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 MB
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) + 1 ));


fl.re le a s e ();
S y s te m .o u t.p rin tln ("O tk lju c a n o : "+ poce tak +" do "+ k r a j ) ;
} 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 pristu-
pom 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 po-
zovete metodu release() kao to je uraeno u prethodnom programu.

Komprimovanje
Javina U/I biblioteka sadri klase koje podravaju itanje i upisivanje tokova u kompri-
movanom formatu. Te klase treba omotati oko postojeih U/I klasa.
Ove klase nisu izvedene iz klasa Reader i Writer, ve su deo hijerarhija klasa Input-
Streain 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 In-
putStream Reader i OutputStreamReader.)

Klasa za kom prim ovanje Funkcija


CheckedlnputStream Metoda G etCheckSum ( ) vraa kontrolni zbir za bilo koji objekat klase
InputStream (ne samo dekomprimovanje).
CheckedOutputStream Metoda GetCheckSum f ) vraa kontrolnl zbirza bilo koji objekat klase
OutputStream (ne samo komprimovanje).
DeflaterOutputStream Osnovna klasa za kompresiju.
ZipOutputStream Potklasa klase DeflaterO utputStream koja komprimuje podatke u
formatu Zip.
GZIPOutputStream Potklasa klase DeflaterOutputStream koja komprimuje podatke u for-
matu GZIP
InflaterlnputStream Osnovna klasa za dekomprimovanje.
ZiplnputStream Potklasa klase InflaterlnputStream koja dekomprimuje podatke
snimljene u formatu Zip
GZIPInputStream Potklasa klase InflaterlnputStream koja dekomprimuje podatke sni-
mljene 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 ko-
riste. Zbog toga s komprimovanim podam a moete lako da radite pomou brojnih alat-
ki 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 kom-
primovati jedan tok podataka (a ne kontejner meusobno neslinih podataka). Evo pri-
mera 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 GZIPIn-
putStream 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 to-


kovima: objekat in koristi klase iz hijerarhije Reader, dok konstruktor klase GZIPOut-
putStream 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 kori-
enje 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()) != null) {
print("itanje datoteke " + z e ) ;
int x;
whil e( (x = bis.read()) != -1)
System.out.wri t e ( x ) ;
}
if(args.length == 1)
printC'Kontrolni zbir: " + csumi .getChecksum() .getValue());
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
}
/* i f ( a r g s .1ength == 1) */
}
} /* (Pokrenite da biste videli rezultate) *///:

Metodi putN extE ntry( ) m orate da prosledite objekat klase ZipEntry za svaku datote-
ku 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 lieckedlnput-
Stream i CheckedOutputStream podravaju oba tipa kontrolnih zbirova (Adler32 i
CRC32), klasa ZipEntry podrava sam o interfejs za kontrolni zbir tipa CRC. To ogranie-
nje 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 meto-
da vraa sledei objekat klase ZipEntry ako on postoji. Postoji i kraa varijanta - proi-
tajte 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 odgova-
rajuem objektu klase Checksum. Pri tom se zadrava referenca na objekte tipa Checke-
dO 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 18: 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 da-
toteke - 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 sme-
stite 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 ui-
niti 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 at-
ski 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). Ko-
risnici Unixa/Linuxa prim etie slinost s alatkom tar. Opcije su:

c Pravi novu praznu arhivu.


t Lista sadraj.
X Dekomprimuje sve datoteke iz arhive.
x datoteka Dekomprimuje navedenu datoteku
f Kae Saa u ti dostaviti ime datoteke". Akoje ne koristite, ja r pretpostavlja da e ula-
zm poaci stii iz standardnog ulaznog toka ili, ako pravi datoteku, da izlazne podatke
treba da poalje na standardm izlazm tok.
m Kae da e prvi argument biti ime manifest datoteke koju je napravio korisnik.
V Detaljno ispisuje informacije tokom rada.
0 Samo snima datoteke, a ne komprimuje lh. (Koristi se za pravljenje JAR datoteke koju
moete da stavite u putanju klasa.)
M Ne pravi automatski manifest datoteku.

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 ar-
hivu 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 pu-
tanju 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 doda-
jete 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 pro-
ita 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 ob-
jekat 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 au-
tom atski pobrine za sve pojedinosti.
Serijalizovanje objekata u Javi om oguuje pretvaranje svakog objekta koji realizuje in-
terfejs Serializable u niz bajtova iz kojeg se taj objekat kasnije moe p o tp u n o rekonstru-
isati. 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 pro-
grama. 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 prepu-
stite 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. Ja-
vino daljinsko pozivanje m etoda (engl. reniote m eth o d invocation, R M I) om oguuje objek-
tim 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 Enter-
prise 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 ka-
snije, 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 do-
dato u jezik, m noge klase standardne biblioteke prom enjene su da bi ga podrale, uklju-
ujui 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 unu-
tar objekta klase ObjectOutputStream. Nakon toga treba sam o pozvati m etodu write-
Object( ) 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 Object-
InputStream i poziva se m etoda readO bject( ). Kao i obino, dobija se referenca na obje-
kat 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 jed-
nim 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: " + i);
c = x;
if(--i > 0)
sledeci = new Crv(i, (char)(x + 1));
}
publ ic Crv() {
print("Podrazumevani k o ns tr uk to r" );
}
public String t o S t r i n g O {
StringBuilder rezultat = new St ri ng Bu i1d e r ( ":");
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)
Poglavlje 18: Javin ulazno-izlazni sistem 783

rezultat.append(sledeci);
return rezultat.toString( );
}
public static void main(String[] args)
throws ClassNotFoundException, IOException {
Crv w = new Crv(6, 'a');
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 ) ;
}
} /* Ispis:
Konstruktor crva: 6
Konstruktor crva: 5
Konstruktor crva: 4
Konstruktor crva: 3
Konstruktor crva: 2
Konstruktor crva: 1
w = :a (8 53):b(119):c (802) :d (788):e(199):f(881)
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 )
* ///:-

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 auto-
matski generie tokom rekurzivnog pravljenja ulanane Iiste crva. Kada napravite objekat
klase Crv, saoptavate konstruktoru koliko treba da bude dugaak. Da bi napravio refe-
rencu 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 seri-


jalizovati runo. Postupak serijalizovanja, m eutim , prilino je jednostavan. Kada se na-
pravi 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 na-
vedeno u knjizi T hinking in Enterprisc Java.
Prim eujete li da deserijalizovan objekat zaista sadri sve veze koje su postojale u ori-
ginalnom objektu?
U postupku deserijalizovanja objekta interfejsa Serializable ne poziva se nijedan kon-
struktor, ak ni podrazum evani. Ceo objekat se rekonstruie na osnovu podataka iz ula-
znog toka.
Veba 27: (1) Napravite klasu koja realizuje interfejs Serializable i sadri referencu na ob-
jekat neke druge klase koja se m oe serijalizovati. Napravite instancu svoje klase, serijali-
zujte 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 rekonstru-
ie 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 di-
rektorijum 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 posto-
janju 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 se-
rijalizujete 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 Externaliza-
ble um esto interfejsa Serializable. Interfejs Externalizable proiruje interfejs Serializa-
ble 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 konstruk-
to 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 na-
in (ukljuujui i inicijalizovanje tokom definisanja polja), a za tim se poziva m etoda re-
ad 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 obje-
kat 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) {
p r i n t ( " B l i p 3 (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.
}
public String toString() { return s + i; }
public void writeExternal(ObjectOutput out)
788 Misliti na Javi

throws IOException {
print("Bl ip3.wri teExternal11) ;
// 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 11 , 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 to-
kom 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 pod-
razum 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 ProveraBli-
pova.java. Preimenujte i klasu Blip2 u ProveraBlipova (prepravite je u javnu, a ispred kla-
se 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 Pro-
veraBlipova. Izvrite program i objasnite zato radi. O bratite panju na to da nakon pre-
voenja 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 ta-
kva 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 re-
alizuje kao Externalizable, kao to je upravo pokazano. Na taj nain nita se ne serijali-
zuje 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 at-
ski. Da biste to kontrolisali, serijalizovanje bilo kog elem enta m oete da iskljuite rezer-
visanom 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 (reali-
zuje) 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() {
return "informacije o prijavljivanju: \n 11 + "korisniko ime: " + kori-
snickolme +
"\n datum: " + datum + "\n lozinka: " + loz;
}
public static void main(String[] args) throws Exception {
Prijavljivanje a = new Prijavljivanje("Hulk", "mojMaliPoni");
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); // Kanjenje
// 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);
}
} /* 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 rekonstrui-
e, 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 kon-
vertuje 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, re-
zervisana 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 dru-
gaiji 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 write-
O b ject( ) i readO bject( ) koje e se autom atski pozivati prilikom serijalizovanja i deseri-
jalizovanja 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 me-
tode, 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 zna-
i 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 meto-
de 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, pre-
skae 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 izve-
dete 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 rekon-
struisanjem objekta koji realizuje interfejs Serializable:

//: ui/KontrolaSerij a l izovanja.java


// Upravljanje serijalizovanjem dodavanjem sopstvenih
// metoda w r i teObject() i re adObject().
import java.io.*;

public class KontrolaSerijalizovanja implements Serializable {

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; " + aa;
b = "transient: " + bb;
}
public String t o S t r i n g O { return a + \n" + b; }
private void w r it eO bj ec t( Ob je ct Out pu tS tr ea m tok)
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 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 {
KontrolaSerijalizovanja sc = new Ko ntrolaSerijal izovanja("Testl",
"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);
}
} /* Ispis:
Pre:
Nije transient: Testl
transient: Test2
Posle:
Nije transient: Testl
transient: 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 ope-
raciju 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 prosle-


ujete 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 KontroIaSerija-
lizovanja 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 utput-
Stream.) 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 re-
avanje 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 nee-
mo 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 pro-
jekta, 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 obje-
kat pojaviti sam o jednom ? ta e se desiti ako se dva objekta serijalizuju u zasebne dato-
teke, 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[] args)
throws IOException, C1 assNotFoundException {
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", kucica));
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: " + zivotinjel);
print("ivotinje2: " + zivotinje2);
print("ivotinje3: " + zivotinje3);
}
} /* Ispis: (primer)
ivotinje: [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 na-
in 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 deserija-
lizuju i ispiu, videete rezultate prikazane za jedno izvravanje (objekti e pri svakom po-
kretanju 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 refe-
rence na objekat klase Kucica koji oba objekta sadre. S druge strane, prilikom rekonstru-
isanja 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 tre-
nutku kada ih serijalizujete, objekti e biti upisani u stanju u kom e se nalaze (i s postoje-
im vezam a do drugih objekata).
Ukoliko elite da zabeleite stanje sistema, najbezbednije je da se to obavi kao nedelji-
va operacija. Ako serijalizujete neto, pa ponete da radite neto drugo, zatim opet seri-
jalizujete 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 ope-
raciji. 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 ob-
jekat 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 pa-
m 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() +
11 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) { boja = novaBoja; |
public int citajBoju() { return boja; }
}

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) { boja = novaBoja; }
public int citajBoju() { return boja; }

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) { boja = novaBoja; }
public int citajBoju() { return boja; }
}

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 auto-
matski m oe serijalizovati. Svaki objekat klase Oblik sadri podatke, a svaka klasa izvede-
na 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 po-
vezivanje, 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 kon-
struktoru. 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 18: 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 enje-
ne. Krugovi im aju vrednost 1 (CRVENA, po definiciji), a kvadrati im aju vrednost 0 (se-
tite 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 se-
rijalizaciju.) 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 in-
form acija tako da prilikom rekonstruisanja m oete da vratite privatne prom enljive u pr-
vobitno 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 for-
m 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 naj-
jednostavniji nain da se XML proizvodi i m enja u Javi. Pored toga, XOM proizvodi veo-
ma 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 { return im + " " + prez; }
// Neka bude itljivo za ljude:
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 ();
}
public static void main(String[] args) throws Exception {
List<Osoba> Ijudi = Arrays.asList(
new Osoba("Dr. Bunsen", "Honeydew"),
new Osoba("Gonzo", "The Great"),
new O s o b a (Phi11 ip J.", "Fry"));
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 ) ;
}
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 gomi-
lu, 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[] args) throws Exception {
Ljudi p = new Ljudi ("Ljudi ,xml");
System.out.println(p);
}
} /* 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 struk-
tu 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 dis-
tribucije 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.TextFi-
le, 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 go-
vori, 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), us-
kladitene 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: (primer)
Mesto: Oz
Obua: Crvene papuice
Drugara: 4
Ima li vetica?: true
UsageCount: 53
Koliko drugara ima Doroti? 4
* ///:-
O vde je upotrebljena m etoda userNodeForPackage( ), a m ogao sam odabrati i sy-
stem N odeForPackage( ); izbor je donekle proizvoljan, ali ideja je da se ,,user upotreblja-
va 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 get-
C 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 poz-
navaoci 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. Uo-
biajeno 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 kas-
nijim pokretanjim a biti razliit od nule.
Kada pokrenete PrimerZaPreferences.java, videete da se UsageCount zaista povea-
va za jedan svaki put kada pokrenete program . Ali gde se taj broj uva? Nema lokalne da-
toteke 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 zavis-
nosti od operativnog sistema. U Windovvsu se za to upotrebljava registar (poto je on io-
nako 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 po-
traite 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 nasle-
ivanja moete da napravite nove vrste ulaznih i izlaznih objekata. Moete ak i da izmc-
nite 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 (Ja-
vina 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 za-
date otvaranje izlazne datoteke, ali sam o ako ona ve ne postoji. Izgleda da u Javi treba ko-
ristiti 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 po-
treban dodatni napor da se ona objasni i razum e. Istovrem eno, biblioteka nije zaokrue-
na: 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 bibliote-
ku 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 vred-
nosti; 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 de-
taljnije 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 ge-
neriki 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: 1
0 true true
class Odmor
PLANINA

BANJA rednibroj: 2
1 false false
class Odmor
BANJA

BANJA
PLANINA
MORE
* ///:-

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 inter-
fejs 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 me-
todom 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) { this.stepen = stepen;}
public String toString() { return "Pljeskavica je "+ stepen;}
public static void main(String[] 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 si-
gurno 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 razu-
mljiva, 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 me-
todu 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 kon-
struktor 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; }
public static void main(String[] args) {
for(VesticaIzOza vestica : V e s t i c a l z O z a . v a l u e s O )
print(vestica + 11 + 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 Doro-
tina 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[] args) {
fo r(SvemirskiBrod s : v a l u e s O ) {
System.out.pri ntln (s);
}
}
} /* 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 me-
toda 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 ; i <7; i++) {
pri nt (s);
s.changef);
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 mo-
gue sluajeve. S druge strane, ukoliko iz naredbe case pozivate return, prevodilac e se a-
liti 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: " + enumKlasa.getSuperclassO);
print("Metode: ");
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)? 11 +
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, noti-
fyAU, 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, noti-
f 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 Istrazi OVDE;
public static final Istrazi TAMO;
public static final Istrazi[] values();
public static Istrazi valueO f( ja va .1an g. S t r i n g ) ;
static {};
}
* ///:-

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 infor-
m 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 po-
m 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[] args) {
C1 as s<Integer> intKlasa = Integer.class;
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 ) ;
}
}
} /* 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 na-
sleivanja:

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, LUDA, BOBA;
private Random slucajan = new Random(47);
public LiklzCrtaca next() {
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, LEZANJE, STAJANJE, POSKAKIVANJE,


TRCANJE, 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 ukla-
njanje 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 nabro-
janog 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;
j
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[] args) {
Hrana hrana = Predjelo.SALATA;
hrana = G 1 avnoJelo.LAZANJE;
hrana = Desert.SLADOLED;
hrana = Kafa.CAPPUCCINO;
}
} 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++) {
for(VrstaJela p o j e d i n o j e l o : VrstaJela.vrednosti ()) {
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 ();
S y s t e m . o u t .pri ntl n ( h r a n a ) ;
}
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(Vrednosni-
P 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 { KRATKOROCNE, DUGOROCNE,
JEMSTVENE }
en u m Obveznica implements VrednosniPapiri { GRADSKE, RAZNE }
}
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 pogla-
vlja) tako da koristi tri nabrojana tipa: SAMOGLASNIK, PONEKAD SAMOGLASNIK i
SUGLASNIK. K onstruktor nabrojanog tipa treba da prim a sva slova koja ine odgovara-
juu 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 in-
terfejsa 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 de-
lotvoran. 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 pri-
m 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 pro-
menljive duine i za pojedinane m etode koje prim aju od dva do pet eksplicitnih argu-
m 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 pozo-
vete 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/neposto-
janje. 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) {
E n um Se t< Ve 1iki> bigEnumSet = En um Se t.al1O f (Veli k i .c la ss );
System.out .p ri nt ln (bi gE nu mS et );
}
} /* 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 an-


da). 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 klju:
em .g et (S AL A) .a ct io n();
} catch(Exception e) {
print(e);
}
}
} /* Ispis:
KUPATILO: Uzbuna u kupatilu!
KUHINJA: 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 viekrat-
no 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 ostvare-
no m etodam a koje se menjaju u zavisnosti od konstante, stie se utisak da je svaka instan-
ca 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() { pr i n t ( " P o n a s a n j e l " ) ; } },
TREPNUTI { void ponasanje() { pr in t( " P o n a s a n j e 2 " ) ; } },
KLIMNUTI { void p o n a s a n j e O { pr i n t ( " P o n a s a n j e 3 " ) ; } };
abstract void ponasanje();
}

public class NisuKlase {


// void fl(KaoKlase.NAMIGNUTI instance) {} // Ne moe
} /* 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;

* ///:-

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 po-
naaju se kao obine unutranje klase; m i nem am o pristupa nestatinim poljima ili me-
todam 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) { ci kl us i . a d d ( c i k l u s ) ; }
public void operiKola() {
for(Ciklus c : ciklusi)
c.dejstvoO ;
}
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) {
PerionicaAutomobila pranje = new Pe ri on ic aA ut om ob i1a ( ) ;
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 se zanemaruju
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 ();
}
} /* Ispis:
[PRANJE, ISPIRANJE]
Pranje
Ispi ranje
[PRANJE, VOSKIRANJE, ISPIRANJE, SUSENJE]
Pranje
Nanoenje vrueg voska
Ispiranje
Suenje ventilatorom
* ///:-
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[] args) {
for(RedefinisanjeMetUZavOdK rmuzok : values()) {
printnb(rmuzok + " : ");
rm u z o k . f ();
}
}
} /* 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 obrai-
vanje 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 er-
ke prob nih poiljki. M etoda g en erato r( ) proizvodi objekat koji realizuje interfejs Itera-
ble 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() { return "Poiljka " + id; }
public String details() {
return t o S t r i n g O +
", Opta dostava: " + opstaDostava +
", Adresa se moze skenirati: " + MozeSeSkenirati +
", itljivost adrese: " + Citljivo +
", Adresa Adresa: 11 + adresa +
", Povratna 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 int broj) {
return new Iterable<Posi1jka>() {
int n = b r o j ;
public Iterator<Posi1jka> iterator() {
828 Misliti na Javi

return new I t er at or <P os i1jka>() {


public boolean hasNext() { return n > 0; }
public Posiljka next() { return nasu mi cn aP os il jk a( ); }
public void remove() { // 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: return false;
default:
printfp + " : isporui a u to ma ts ki");
return true;
}

},
VIZUELNIPREGLED {
boolean ob ra da (Posiljka p) {
switch(p.Citljivo) {
case NECITLJIVO: return false;
default:
switch(p.adresa) {
case NETACNA: return false;
default:
print(p + " : isporui obino");
return true;
}
}
Poglavlje 19: Nabrojani tipovi 829

}
},
VRATI_POSILJAOCU {
boolean obrada{Posiljka p) {
switch(p.povratnaAdresa) {
case NEDOSTAJE: return false;
default:
print(p + " : vrati p o i 1j a o c u " ) ;
return true;
}
}
};
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[] args) {
for(Posiljka posiljka : Po si1jka.gene ra to r( lO )) {
pr in t( po si lj ka .d et ails( ) );
obrada(posilj k a ) ;
p r i n t C 1* * * * * " ) ;
}
}
} /* Ispis:
Poiljka 0, Opta dostava: NE2, Adresa se moze skenirati: N E MO ZE SE SK EN IR AT I,
Adresa itljiva: DA3, Adresa Adresa: OKl, Povratna adresa: OKl
Poiljka 0 : isporui obino
*****
Poiljka I, Opta dostava: NE5, Adresa se moze skenirati: DA3, Adresa
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
itljiva: DAl, Adresa Adresa: NETACNA, Povratna adresa: 0K4
Poiljka 3 : vrati poiljaocu
* * * * *

Poiljka 4, Opta dostava: NE4, Adresa se moze skenirati: N E M O ZE SE SK EN IR AT I,


Adresa itljiva: DAl, Adresa Adresa: NETACNA, Povratna adresa: 0K2
Poiljka 4 : vrati poiljaocu
*****
Poiljka 5, Opta dostava: NE3, Adresa se moze skenirati: DAl, Adresa
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
itljiva: NECITLJIVO, Adresa Adresa: 0K4, Povratna 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


itljiva: DA3, Adresa Adresa: NETACNA, Povratna adresa: NEDOSTAJE
Poiljka 8 se ne mo e uruiti
* * * * *

Poiljka 9, Opta dostava: NEl, Adresa se mo ze skenirati: NEMOZESESKENIRATI,


Adresa itljiva: DA2, Adresa Adresa: OKl, Povratna adresa: 0K4
Poiljka 9 : isporui obino
* * * * *

* ///:-
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 oba-
vi 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 defi-
niem 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) { t h i s .vrednost = vrednost; }
Ulaz() {}
int iznos() { return vrednost; }; // U parama
static Random slucajan = new Random(47);
public static Ulaz randomSelection() {
return v a l u e s ()[slucajan.nextlnt(values().1ength - 1)]; // STOP ne
// treba obuhvati t i :
}
} 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 ula-
za, pa oni generiu izuzetak ako je pozovete za njih. Iako je ovo neobino (definisati m e-
todu 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 Stanje stanje = Stanje.MIRUJE;
private static int iznos = 0;
private static Ulaz izbor = n u l l ;
enum TrajanjeStanja { PR0LAZN0 } // Nabrojani tip
// Kao oznaka
enum Stanje {
MIRUJE {
void next(Ulaz ulaz) {
switch(Kategorija . k a t e g o r i z u j (ulaz)) {
case NOVAC:
iznos += ul a z . i z n o s ( ) ;
stanje = PRIMANJEJIOVCA;
break;
case UGASI:
stanje = TERMINAL;
de fa ult:
}
}
},
PRIMANJE_NOVCA {
void next(Ulaz ulaz) {
s w it ch (K at eg or ij a. kat eg or iz uj (u la z)) {
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;
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() {
print("uzmite kupljeno: " + izbor);
iznos -= izbor. iz no s( );
stanje = VRACANJEJOJSURA;
}
}.
VRACAN JE _K US UR A( Tr aja nj eS ta nj a.PROLAZNO) {
void next() {
if(iznos > 0) {
print("Va kusur: " + iznos);
iznos = 0;
}
stanje = MIRUJE;
}
},
TERMINAL { void output() { p r in t( "Z au st av lj en "); } };
private boolean stanjeProlazno = false;
Stanjef) {}
Stanje(TrajanjeStanja trans) { stanjeProlazno = true; }
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[] args) {


Generator<Ulaz> gen = new R a n d o m In pu tG en er at or( );
i f (args.length == 1)
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() {
if(!ulaz.hasNext())
return n u l1;
return Enum.valueOf(Ulaz.class, ul a z . n e x t ().trim());
}
} /* 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 do-
datni trud uloen u to da se naredba sw itch moe lako upotrebljavati s nabrojanim tipovi-
ma), 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 AutomatZa-
Maloprodaju. U svakom stanju treba izabrati jednu od osnovnih kategorija ulaznog dej-
stva: 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 tipo-
ve ulaza tako da m etoda kategorizuj( ) moe u nutar naredbe switch da proizvede odgova-
rajuu 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 nabroja-
nog 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 pro-
gram AutomatZaM aloprodaju.java tako da artikle koji se prodaju predstavlja klasa um e-
sto 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 ali-
zaciju ), 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 nu-
m 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 dina-
mikog povezivanja sam o za jedan od njih. Tim e se ne bi reio p rethodno opisani pro-
blem, 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 slu-
aju 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 po-
ziva 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 nepo-
znatih 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 konfigu-
raciju 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, PORAZ, NERESENO } ///:-

//: 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 ) ;
}

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) { return st.izrc(this); }


publ ic Ishod izrc(Papir p) { return NERESENO; }
public Ishod izrc(Makaze m) { return POBEDA; }
publ ic Ishod izrc(Kamen k) { return PORAZ; }
public String toString() { return "Papir"; }
}

class M a ka ze implements Stavka {


publ ic Ishod takmicenje(Stavka st) { return st.izr c( th is ); }
public Ishod izrc(Papir p) { return PORAZ; }
publ ic Ishod izrc(Makaze m) { return NERESENO; }
publ ic Ishod izrc(Kamen k) { return POBEDA; }
public String toString() { return "Makaze"; }
}

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 Ishod izrc(Kamen k) { return NERESENO; }
public String t o S t r i n g O { return "Kamen"; }
}

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[] args) {
for(int i = 0; i < VELICINA; i++)
ma t c h ( n e w l t e m ( ) , newltem());
}
} /* Ispis:
Kamen u odnosu na Kamen: NERESENO
Papir u odnosu na Kamen: POBEDA
Papir u odnosu na Kamen: POBEDA
Papir u odnosu na Kamen: 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 otkri-
vanje tipa pozivom m etode iz rc ( ) za preostali tip. Prosleivanjem sebe (this) kao argu-
m 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 tipo-
vi nije jednostavno, jerenum instance nisu tipovi, pa preklopljene m etode i z r c ( ) nee ra-
diti - 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: PORAZ
KAMEN u odnosu na MAKAZE: 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: PORAZ
PAPIR u odnosu na MAKAZE: PORAZ
MAKAZE u odnosu na PAPIR: POBEDA
MAKAZE u odnosu na PAPIR: POBEDA
MAKAZE u odnosu na PAPIR: POBEDA
MAKAZE u odnosu na PAPIR: POBEDA
* ///:-

Nakon to m etoda takm icenje( ) otkrije oba tipa, jedino dejstvo je vraanje rezul-
tujueg 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 Tak-
m 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 + 11 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, int velicina) {
for(int i = 0; i < velicina; i++)
match(
Na br oj an iT ip ovi.random(rsbKlasa),
NabrojaniTi p o v i .ra nd om(rsbKlasa));
}
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 pra-
vljenje 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 naj-
vie 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
: ((protivnik == gubitnik) ? Ishod.POBEDA
: Ishod.PORAZ)) ;
}
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 en-
ta; 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 ne-
poznata 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 tipa EnumMap.
package nabrojani;
import j a v a . u t i l .*;
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 }, // PAPIR
{ POBEDA, NERESENO, PORAZ }, // MAKAZE
{ PORAZ, POBEDA, NERESENO }, // 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 iz-
gleda 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 o-
gunosti, 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 pakt-
nije, 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 napra-
vite funkcijski objekat. Pri odreenim vrstam a problem a, koncept koda upravijanog ta-
belom um c veoma dobro da poslui.

Saetak
Iako nabrojani tipovi sami po sebi nisu previe sloeni, ovo poglavlje sam pom erio u dru-
gi deo knjige zbog onoga to se m oe uraditi kom binacijom nabrojanih tipova i polim or-
fizma, generikih tipova i refleksije.
M ada znatno sofisticiraniji nego u jezicim a C i C + + , nabrojani tipovi i dalje pred-
stavljaju ,,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 - kat-
kada 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 doku-
m 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 infor-
macije 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 ra-
nijim izdanjim a Jave SE5 dozvoljavala se ova anotacija, ali nije bila podravana (ne-
go 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 au-
tom 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 pre-
voenja. 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 au-
tom 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 sta-
tic 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 in-
terfejsa:

//: 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 izvr-
avanja (RUNTIME).
Poglavlje 20: Anotacije 847

Anotacije najee sadre elcm enie za zadavanje vrednosti u anotacijama korisnika. Pro-
gram ili alatka m ogu upotrebljavati te param etre prilikom obrade anotacije korisnika. Ele-
m 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 ano-
tiraju 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 slua-
jeve 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 prove-


rava 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
Retention 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.
@Documented Uvrsti anotaciju u Javadoc dokumente.
Inherited Dozvoli potklasama nasledivanje roditeljske anotacije

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 etode-
ZaLozinke 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 Lozinka mora s a d r a t i barem jednu b r o jk u
Pronaden s l u a j u p o tr e b e :4 8 nema opisa
Pronaen s l u a j u p o tr e b e :4 9 Nova l o z i n k a ne moe b i t i je dnaka p re thodnim
Upozore nje : n e d o sta je s l u a j u p otrebe-5 0
* ///:-

U p retho dn om program u koriste se reflektivna m etoda getD ecIaredM ethods( ) i me-


toda 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 ano-
tiranu 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 ele-
inent 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 de-
klaraciji 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 Hiber-
nate, 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 infor-
macija 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 prevo-
diocu 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 vred-
nosti 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 ugne-
denoj 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( ). Po-
to nakon ovog tipa anotacije nem a u zagradam a specificiranih vrednosti elemenata,
podrazum evana vrednost og ranicenja( ) zapravo je anotacija @Ogranicenja sa sopstve-
nim 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 ob-
lik 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 za-
gradam 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 (@inter-
face) za svaki SQL tip, ali bism o izgubili m ogunost da kvalifikujemo tipove pom ou do-
datnih 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. tar-
get). 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 na-
sleivanje, 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 11 + 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 g la s i:
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 g la s i:
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 g la s i:
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 g la s i:
NAPRAVI TABELU CLAN(
LICNOIME VARCHAR(30),
PREZIME VARCHAR(50),
STAROST INT,
IDENTIFIKATOR VARCHAR(30) PRIMARNI KLJUC);
* ///:-

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 slua-
jeva pravi se relevantni String odlom ak sa im enom kolone tabele. Poto se interfejsi ano-
tacija 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 preslika-
vanja 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 od-
govarajue 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 AnnotationProcessorFac-
tory 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 poje-
dinosti 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 in-
terfejsa 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() + 11 ( " ) ;
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 obra-
de 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 kon-
strukcija, 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 otvo-
rili 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 pro-
putali kroz procesor. Ostale dve metode, supportedA nnotationTypes( ) i supportedO p-
tions( ), 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 direk-
torijum a kom andna Iinija ugraena u kom entar Exec na poetku program a Proce-
sorZalzdvajanjelnterfejsa.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 da-
toteke m oraju napraviti u direktorijum u anotacije. Ovako izgleda generisana datoteka If-
Mnozaca.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 jednosta-
van 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 (Pose-
tilac). Visitor je jedan od klasinih projektnih obrazaca iz knjige Design Patterns (Projekt-
ni 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 ope-
raciju 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 razdva-
jaju od sam ih objekata, to znai da m oete dodavati nove operacije, a da ne dodajete m e-
tode 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 odgovara-
jue 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 (
C1 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 me-
toda 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 upo-
trebljen 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 in-
terfejsa 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 usred-


sredite 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 prvobit-
nom 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 pret-
hodnog 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. Najko-
rienija 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 ver-
zijama JUnita pre anotacija bile su brojne ,,ceremonije neophodne u priprem i i izvra-
vanju 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, jedna-
ko lako kao javne.
Poto je struktu ra za testiranje u ovom prim eru napravljena pom ou anotacija, na-
zvao 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. Ta-
koe, @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,' od-
govarajue m etode i polja oznaite sa @ Unit (o tom e ete vie saznati u narednim prim e-
rim 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 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.
7 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 bu-
dete 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 te-
stira 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 [ ] a rg s ) thro ws Exce p tio n {
O S I z v r e n je . komanda(
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 at-
ski. 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 iz n e n a e n ja !
(fa ile d )
. 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 prove-


rava 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 konstruk-
tor. Ukoliko nem ate podrazum evani konstruktor ili vam je po treb n a sofisticiranija kon-
strukcija 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 - pro-
gram @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 je-
dinino testiranje (da biste mogli da ih uklonite pre isporuke program a klijentu). U na-
rednom 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 MUCH 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 ' b r o n t o s a u ru s e s '
ts a e bororn ussu

. reci 'a re '

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 te-
stova, 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 @ TestObject-
C 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 te-
stiranja.

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 jednostav-
no: 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 me-
todu, 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 ko-
riste jednostavnim dodavanjem @Test m etoda, bez ikakvog specijalnog koda ili pred-
znanja 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, E1 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 te-
stiranje. 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 . " ) ;
}
}
}
} ///:-
AtUnit.java ProcessFiles iz paketa net.mindview.util. Klasa AtU-
u p o t r e b lja v a a la t k u
nit ProcessFiles.Strategy k o ji o b u h v a t a m e t o d u process( ). N a taj
re a li z u je in t e rf e js
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.
Poglavlje 20: Anotacije 879

Ako ne zadate argum ent s kom andne linije, program e pretraiti stablo tekueg di-
rektorijum 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 inenjerin-
ga 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 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 ).
v 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 for-
m 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,10 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 className-
Table i tabeli offsetTable. Nakon uitavanja skladita konstanti m oe se pronai inform a-
cija 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 me-
tode 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 a-
tra 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 izu-
zetka). 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 pri-
javljene na kraju testiranja.
Veba 11: (5) Alatki @Unit dodajte anotaciju @TestNote; ona oznaava pratee napom e-
ne (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 e-
lite), 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 Javassist11 svodi inenjering bajtkoda u dom en m o-
gueg. N aredni program prim a opcioni indikator -r kao svoj prvi argum ent; ako zadate taj

111 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

indikator, on e ukloniti @Test anotacije, a ako to ne uinite, on e ih prikazati. I ovde se


za 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() + 11 " + 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 dosle-
dnost 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 me-
toda 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,12 i
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 ini-
cijalizaje. 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 etapoda-
taka kodu, zajemeno bezbedan u pogledu tipova, a pri tom kod ne ini neitljivim i za-
petljanim . 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 anotaci-


jom @Deprecated sam o je jedan od pokazatelja koliko su anotacije prikladnije od ko-
m 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 Solu-
tion 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 spo-
sobnosti 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 sup-
tilan 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 pre-
gleda 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 para-
lelnog 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 pro-
veravaju. 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 ko-
da. 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 shva-
titi 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 para-
lelnom izvravanju. Katkada su ta zakazivanja dobroudna, ali um eju da prouzrokuju
gubljenje vrednih podataka, i ako barem niste uli za problem e s paralelnim izvrava-
njem, 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 pro-
gram 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 mo-
rate da shvatite sve problem e i specijalne sluajeve da biste efikasno upotrebljavali
paralelno izvravanje.
Probleme koje reavamo paralelnim izvravanjem grubo delim o na brzinu izvra-
vanja 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 vieproce-
sorskog 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 isto-
vrem eno izvravanje), to um e znatno da pobolja protok. Tako esto rade snani vie-
procesorski 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 zau-
stavlja 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 pro-
gram 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 progra-
ma. 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 pro-
cese. 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 ra-
zliku 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 trenu-
tku pristupao sam o jedan zadatak.
888 Misliti na Javi

Evo jednostavnog prim era procesa u operativnom sistemu. D ok piem knjigu, redov-
no pravim vie redundantnih rezervnih kopija tekueg stanja knjige. Jednu kopiju napra-
vim 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 ver-
zije 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 pro-
ces 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 pri-
m 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, raz-
mislite o ideji da taj deo program a napiete na nam enskom jeziku za paralelno izvra-
vanje 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 viepro-
gramski 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 tre-
n 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 pred-
nost: dizajn program a se m oe znatno pojednostaviti. Neke vrste zadataka, kao to je si-
m ulacija, teko je reiti bez paralelnog program iranja.
Veina ljudi je videla barem jedan oblik simulacije, bilo kao raunarsku igricu ili rau-
narski generisanu anim aciju u filmovima. Simulacije po pravilu obuhvataju m nogo ele-
m 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 sop-
stveni procesor i da predstavlja nezavisan zadatak.
Potpuna simulacija esto sadri veom a veliki broj zadataka, zato to svaki elem ent si-
m ulacije m oe da dejstvuje nezavisno - to vai i za vrata i stene, ne sam o za duhove i a-
robnjake. 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. coope-
rative 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 vre-
mena 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 vr-
stu naredbe za preputanje kontrole. Prednost kooperativnog sistema je dvostruka: preba-
civanje konteksta je obino mnogo jeftinije nego u preduprednom sistemu, i teorijski
nema ogranienja broja nezavisnih zaataka koji se m ogu izvravati istovremeno. Kada si-
mulacija 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 po-
ruka 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 zadat-
ke koje obavljaju niti.
89 0 Misliti na Javi

Osnove vienitnog programiranja


Paralelno program iranje om oguuje deljenje program a na zasebne delove koji se izvr-
avaju 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 dru-
gi, 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 ko-
rienja 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. Upo-
treba vieprogram skog (engl. m ultitasking) operativnog sistema i istovrem eno izvra-
vanje 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 ! ") + " ) , ";

11 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 rezervisa-
na 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 posta-
ne 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 bes-
konane petlje, to znai da e se ona izvravati u nedogled ako je neto ne prekine. (U na-
stavku 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 preba-
cuje 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 povea-
va 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 jedno-
stavno 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 po-
zivom 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 spo-
sobnost 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 ru-
tine 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 ,,re-
gistruje 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 kon-
struktor 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 pojedno-
stavljuju paralelno program iranje tako to iniciraju izvravanje niti (objekata tipa
T h read ) i upravljaju njime. Izvrioci obezbeuju sloj indirekcije izm eu klijenta i izvra-
vanja zadatka; um esto da klijent izvrava zadatak neposredno, njega izvrava posredniki
objekat, tj. izvrilac (engl. executor). Klasa E xecutors om oguuje da upravljam o izvra-
vanjem asinhronih zadataka, a da ne m oram o eksplicitno da upravljam o ivotnim ciklu-
som 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. Te-
kua 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 vr-
stom 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), # 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 ira n je !),
# 1 ( 1 ) , # 2 (1 ) , # 3 ( 1 ) , # 4(1), # l( L a n s ir a n je !) , # 2 (L a n s ira n je !) , # 3 (L a n sira nje !),
# 4 (L a n s ira n je !),
* ///:-

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 Fixed-
ThreadPool 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 ko-
riste istu nit. U narednom prim eru, videete da svaki zadatak, po redosledu prijavljivanja,
biva dovren pre otpoinjanja sledeeg. Dakle, SingleThreadExecutor serijalizuje zadat-
ke 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(2),
#1(1), # l( L a n s ir a n je !) , # 2 (9 ), # 2 (8 ), # 2 (7 ), # 2 (6 ), # 2 (5 ), # 2 (4 ), # 2(3),
# 2 (2 ), # 2(1), # 2 ( L a n s ir a n je !) , # 3 (9 ), # 3 (8 ), # 3 (7 ), # 3 (6 ), # 3 (5 ), # 3(4),
# 3 (3 ), # 3(2), #3(1), # 3 ( L a n s ir a n je ! ) , # 4 (9 ), # 4 (8 ), # 4 (7 ), # 4 (6 ), #4(5),
# 4(4), # 4(3), # 4(2), # 4 (1 ), # 4 (L a n s ir a n je !) ,
* ///:-

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 re-
zultat. 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 po-
zvati 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 0
r e z u l t a t ZadatkaSRezultatom 1
r e z u l t a t ZadatkaSRezultatom 2
r e z u l t a t ZadatkaSRezultatom 3
r e z u l t a t ZadatkaSRezultatom 4
r e z u l t a t ZadatkaSRezultatom 5
r e z u l t a t ZadatkaSRezultatom 6
r e z u l t a t ZadatkaSRezultatom 7
r e z u l t a t ZadatkaSRezultatom 8
r e z u l t a t ZadatkaSRezultatom 9
* ///:-

M etoda s u b m it( ) proizvodi objekat tipa Future, param etrizovan za odreeni tip re-
zultata koji vraa taj objekat tipa Callable. O bjekat tipa Future moete ispitivati meto-
dom 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 prethod-
ne 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 Fi-
bonaccijeve 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(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 ira n je !) , #1( L a n s ir a n je !) , #2(L a n s ira n je !) , # 3 (L a n s ira n je !),
#4(Lansi r a n j e ! ) ,
* ///:-
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 kon-
verzije, 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 procesor-
skog 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 duga-
kog 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 do-
biti 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 pokret-
nog zareza koje obuhvata sabiranje i deljenje tipa double. Promenljiva d je dobila modifi-
kator 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 pro-
cesorskog vrem ena ee pozivao nit najveeg prioriteta (MAX_PRIORITY). (Tako se po-
naao 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 si-
stem 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 te-
kua 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 progra-
m a Lansiranje. U m etodi Lansiranje. r u n ( ) pozive Thread.yield( ) pretvorite u kom en-
tare, 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 ce-
lini 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] JednostavneServisneNiti@16caf43
Thread[Thread-6,5,main] JednostavneServisneNiti066848c
Thread[Thread-7,5,main] JednostavneServisneNiti08813f2
Thread[Thread-8,5,main] JednostavneServisneNiti@ld58aae
Thread[Thread-9,5,main] JednostavneServisneNiti@83cc67

*///:-

Pre pokretanja m etodom s ta r t( ), nit m orate m etodom setD aem on( ) najpre pretvo-
riti 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 posle-
dice 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 vi-
deli 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 pri-


ma objekat tipa ThreadFactory pom ou kojega e praviti nove niti.
M oem o to podii za jedan stepen vie i napraviti uslunog izvrioca Daemon-
ThreadPooIExecutor 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 na-
pravi, 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 Servisne-
N 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-0(4), Thread-0(3), Thread-0(2), Thread-O(l), Thread-1(5),
Thread-1(4), Thread-1(3), Thread-1(2), Thread-l(l), Thread-2(5), Thread-2(4),
Thread-2(3), Thread-2(2), Thread-2(1), Thread-3(5), Thread-3(4), Thread-3(3),
Thread-3(2), Thread-3(1), Thread-4(5), Thread-4(4), Thread-4(3), Thread-4(2),
Thread-4(1),
*///-

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 kon-
struktora 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() + ": 11 + 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 kon-
struktoru pravi instancu te unutranje klase. To ima smisla ako unutranja klasa ima po-
sebne 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 imenova-
nu 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 pre-
ko interfejsa T hread i bez poznavanja stvarnog tipa tog objekta.
Trea i etvrta klasa u prim eru ponavljaju prve dve klase, ali koriste interfejse Runna-
ble, 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 koris-
niji 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 eto-
de 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 za-
pravo 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 da-
mo. 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 izve-
dem 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 napravi-
m o zadatak koji se izvrava nezavisno od drugih zadataka, pa bi trebalo da je mogue de-
finisati 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 od-
vojiti 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 bi-
sm o ostali na visokom nivou apstrakcije, m oram o biti disciplinovani pri pisanju progra-
ma. (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 i-
nom nit - m ehanizam koji izvrava zadatak. Dakle, ako sistem razm atram o na koncep-
tualnom 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 ilisekun-
dam 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 pozi-
vajuu 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 Spo-
roK 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 izrau-
navanje 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 biblio-
teku 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 izuze-
tak 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 ko-
ristili 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 ( ) po-
kazuje 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.Uncaught-
ExceptionHandIer.uncaughtException( ). Da bism o mogli da ga upotrebimo, napravie-
m o nov tip interfejsa ThreadFactory koji svakoj niti koju napravi pridruuje Thread.Un-
caughtExceptionHandler. 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 11 + 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 = 11 + 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 po-
jedinom 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 izu-
zetaka 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 pro-
stor 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 jedin-
ki, 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 ge-
neratora s kojim a em o eksperim entisati, napraviem o apstraktnu klasu GeneratorCe-
lobroj 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 can-
celled 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 dodelji-
vanje 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 me-
todi r u n ( ). Na taj nain, zadaci koji dele zajedniki resurs (GeneratorCelobroj) osluku-
ju 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 vi-
deete 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 se-
riju 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 Pro-
v 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 ot-
kazivanje 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 vie-
nitni 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 oba-
viti 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 na-
bodete poslednje pare hrane u tanjiru; u trenu tk u kad viljuka dodirne to pare, ono iz-
nenada 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 funk-
cionisalo, 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 jed-
na nit koristi. Prva nit koja pristupi nekom resursu p rethod n o ga zakljua, im e se sprea-
va 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 ri-
stup deljenim resursima. To znai da je, u svakom trenutku, p ristup deljenom resursu doz-
voljen 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, oso-
ba 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 do-
biti 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 pro-
cesorskog 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 pozo-
vete bilo koju sinhronizovanu m etodu, objekat se zakljuava i nijedna druga sinhronizo-
vana 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 pri-
vatna; 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 jed-
na 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 zane-
m ariti bravu na objektu i biti nesm etano pozvane. Ovo je vano: sve m etode koje pri-
stupaju 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:

10 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 bra-
va 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 poda-
ci 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 ele-
gantan od ugradenih oblika. M eutim , prilagodljiviji je za reavanje odredenih vrsta pro-
blema. 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 interna-
lizujete 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 otkljua-
na. 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 pred-
stavlja i jednu od prednosti eksplicitnih Lock objekata. Kada program otkae uz rezervi-
sanu 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 vra-
tili 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 c-
sto 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 otkljua-
vanjem od ugraenih brava synchronized. O ni slue za realizaju specijalizovanih struk-
tu 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 pro-
gram iranje ili ukoliko vam takav strunjak ne pom ae. Ako mislite da ste dovoljno pa-
m etni da se igrate s vatrom , podvrgnite se ovom testu:
Gecov test1': Ako umete da napiete JVM visokih perform ansi za savrem eni m ikropro-
cesor, 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 teh-
nike, bile upotrebljene za realizaciju dela pam etnijih kom ponenata biblioteke ja-
va.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 me-
m 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 uko-
liko 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 tre-
balo 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 zakljua-
vanja 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 kva-
lifikovani n i da pokuaju da sinhronizaciju zam ene atom skim operacijama. Pokuaj
uklanjanja sinhronizacije obino je znak prerane optim izacije i prouzrokuje grdne pro-
bleme, 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 neprekidno-
sti operacija je vidljivost. (Taj problem ne postoji u jednoprocesorskim sistemima.) Izme-
ne 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 pri-
m er), pa razni zadaci dobijaju razliite uvide u stanje aplikacije. S druge strane,
m ehanizam sinhronizacije obezbeuje da izm ene koje na vieprocesorskom sistemu na-
pravi 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 pre-
pisana 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 ogra-
niene vrednostim a drugih polja, kao to su lovver (donja) i upper (gornja) granica ob-
jekta tipa Range koje m oraju da zadovolje ogranienje lovver <= upper.
U potreba m odifikatora volatile umesto rezervisane rei synchronized bezbedna je naj-
ee 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).13

!' 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: aload_0
1: dup
2: getfield #2; //Polje i:I
5: iconst_l
6: iadd
7: putfield #2; //Polje i:I
10: return

void f2();
Code:
0: aload_0
1: dup
2: getfield #2; //Polje i : I
5: iconst_3
6: iadd
7: putfield #2; //Polje i:I
10: 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 vred-
nost 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 vid-
ljivou, 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 pro-
gram 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.14 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 ope-
racija poveanja za 1 u C + + -u esto moe realizovati kao jedna m ikroprocesorska in-
strukcija (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 upi-
sivanje, 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 sle-
d 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 i-
zacije. 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 me-
m 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 sprei-
Io 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 se-
rijskih brojeva jedinstven. Kada napravite vie zadataka koji se takm ie za dobijanje se-
rijskih 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 opera-
cije. 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 pri-
dravati 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, Ato-
micLong, 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 (nepie-
Idnosti) dostupne na nekim od savrem enih procesora, pa o njim a po pravilu ne bi tre-
balo 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 eprekidno-
sti.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 synchroni-
zed. 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 ja-
va.util.concurrent pa ih u svojim program im a upotrebljavajte sam o u posebnim okolno-
stima, 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 syn-
chronized ili eksplicitnih objekata tipa Lock).
Veba 14: (4) Pokaite da java.util.Tim er daje velike brojeve tako to ete napisati pro-
gram koji generie m noge Tim er objekte to obavljaju neke jednostavne zadatke kad iste-
kne 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 kri-
tinom delu program a koristi drugaije. U sledeem prim eru re synchronized zakljua-
va 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 vre-
m 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 dru-
ga 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: 11 + 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 invarijan-
ta (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 bez-
bedne 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 Mena-
dzerPara 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 povecaj-
Z 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 do-
dati 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 te-
stirao 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 izvra-
vanja, videete da m etoda M enadzerPara).povecajZaJedan( ) nc dozvoljava klasi Prove-
raPara 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. Eksplicitan-
MenadzerPara2 prikazuje pravljenje kritinog dela pom ou objekta tipa Lock; poziv me-
tode 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 blo-
ka. 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 po-
kazano 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 eto-
de), 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 sinhroniza-
cija 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 suti-
ni, ta skladita om oguuju da svakoj niti pridruite njeno stanje.
Pravljenjem i upravljanjem lokalnim skladitem niti bavi se klasa java.lang.T hreadL o-
cal, 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 sop-
stveno skladite, poto svaka od njih odrava sopstveni broja iako postoji sam o jedan ob-
jekat 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 doda-
tan 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 bro-
ja 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 1: 1 Ukupno: 2
Ulaz 4: 1 Ukupno: 5
Ulaz 3: 1 Ukupno: 4
Ulaz 2: 2 Ukupno: 6
Ulaz 4: 2 Ukupno: 7
Ulaz 0: 2 Ukupno: 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, iz-
m 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 eto-
de 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 konstruk-
to 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 synchroni-
zed ispred m etode Broj.povecajZaJedan( ), videete da ukupan broj posetilaca nije jed-
nak 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 bu-
dite 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 prouz-
rokovano 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 si-
stemske resurse i obavlja inicijalizaciju. O d tog trenutka postaje podobna za pri-
m 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 rt-
va 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 izvra-
vanje 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.concur-
rent). 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 blo-
kiranje 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 ne-
kog drugog m esta gde je program er sprem an da napusti tu m etodu. Kada izaete iz blo-
kiranog 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 vr-
stu prekidanja koriste izuzeci.16 (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 raz-
m 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 Interrup-
tedException 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 instruk-
ciju/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 ot-
k 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 neposre-
dan rad s nitim a; ona sve pokuava da ostvari izvriocima. Ako pozovete shutdow n-
N 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 bi-
sm 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 nai-
na 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 SinhronizovanoBlo-
kiranje nije m ogue prekinuti.17 Prethodni program dokazuje da se ulazno/izlazne ope-
racije 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 za-
kljuati 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 rea-
lizovan, 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 re-
sursa. 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 predsta-
vljene u poglavlju o ulazno/izlaznim operacijam a. Blokirani nio kanali autom atski reagu-
ju 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 11 + 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( );
} /* Ispis: (primer)
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.18
Veba 18: (2) N apravite klasu bez zadataka, ali s m etodom koja poziva s le e p () za duga-
ko 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 po-
stignete 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 ob-
jekat 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 broj 9
f2() poziva f i o uz broj 8
fl() pozi va f2() uz broj 7
f2 () pozi va fl() uz broj 6
fl() pozi va f2 () uz broj 5
f2() pozi va f i o uz broj 4
fl() pozi va f2 () uz broj 3
f2() pozi va f i o uz broj 2
f i o poziva f2 () uz broj 1
f2() poziva f i o uz broj 0
*///:

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 visePuta-
Z 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 sin-
hronizovane 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 o-
gunost 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() {
print("ekamo na f() u klasi B l o k i r an Mu te x" );
blokiran.fO;
p r i n t ("Izaao iz blokiranog poziva");
}
}

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 slua-
ju 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 sinhroni-
zovanih 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 po-
zivom 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 preki-
nutosti. 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 posta-
vljen, 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 na-
pravljene 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 milisekun-
dam 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 pozva-
na 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 na-
redbe 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 blo-
kira. U tom sluaju, iste se samo objekti klase TrebaPocistiti napravljeni do trenutka ba-
canja 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 je-
dan 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 dru-
gim, 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: naj-
pre se m o ra iskopati rupa za temelje, ali se arm atura m oe poloiti paralelno sa pra-
vljenjem 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 o-
predaju 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 iz-
begavaju svi eventualni uslovi za trku. Pored mutexa, dodaem o nain da se zadatak zau-
stavi 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. Bi-
blioteka 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 e-
kanjem (engl. busy vvaiting) i obino prestavlja lo nain korienja procesorskog vrem e-
na. 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 obje-
kat 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 vreme-
na. 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 konkret-
noj klasi postoji ili ne postoji vie niti, tj. da li je izvedena iz klase Thread ili realizuje in-
terfejs 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 sinhro-
nizovanog 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 na-
noenja 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! 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! Izlazim iz niti
putem prekida
Zavravam zadatak Voska Ima
Izlazim iz niti pomou prekida
Zavravam zadatak Voska Nema
* ///:-

Kola im aju samo jednu prom enljivu tipa boolean pod im enom voskalm a koja po-
kazuje 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 voska-
Im 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 pro-
budio 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 plat-
fo 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 ope-


racija poziv m etode sleep () kojim sim ulira vreme potrebno za voskiranje. Zatim kazuje ko-
lima 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 cekajNa-
V 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 po-
navlja 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 zau-
staviti 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 orateproveriti 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 na-
kon nekoliko sekundi, da bi prvi zadatak m ogao da prikae poruku. Ispitajte klase po-
m 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 indi-
kator 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 pro-
gram gubi na zauzetost ekanjem i napravite drugu verziju program a koja um esto zauze-
tosti 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 realizo-
vane 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 pripre-
m 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 ispra-
van 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 protiv-
nom , ukoliko nit N2 prva dobije priliku da se izvrava, ui e u w a it() i kasnije e je pro-
buditi 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 progra-
m 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. Po-
red 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. Naj-
zad, 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 po-
zove 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 Zada-
tak.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.bloka-
tor 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.bloka-
tor.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 na-
putanja 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 e-
sto 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 predsta-
vlja 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 Jelo 1
Evo narudzbe! Konobar je uzeo Jelo 2
Evo narudzbe! Konobar je uzeo Jelo 3
Evo narudzbe! Konobar je uzeo Jelo 4
Evo narudzbe! Konobar je uzeo Jelo 5
Evo narudzbe! Konobar je uzeo Jelo 6
Evo narudzbe! Konobar je uzeo Jelo 7
Evo narudzbe! Konobar je uzeo Jelo 8
Evo narudzbe! Konobar je uzeo Jelo 9
Ponestalo hrane, zatvaramo
Konobar prekinut
Evo narudzbe! Kuvar prekinut
* ///:-

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 iz-
vaditi jelo iz njega. U m etodi r u n (), Konobar prelazi u reim ekanja - w a it() - i zausta-
vlja 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 Ko-
nobar 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-inyfirst-
out prvi izlazi onaj koji je prvi uao). Vie rei o takvim redovim a za ekanje bie u na-
stavku poglavlja.
Veba 24: (1) M etodam a w a it() i n o tify A ll() reite problem jednog proizvoaa i jed-
nog 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 me-
tode 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, Ko-
nobar 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 po-
m 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! Izlazim iz
niti pomou prekida
Za vr a va m zadatak Voska Nema
Izlazim iz niti pomou prekida
Za vravam zadatak Voska Ima
* ///:-

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 , obje-
kat C ondition ne sadri inform acije o stanju procesa, pa su potrebne dodatne inform a-
cije 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.Blocking-
Queue koji ima vie standardnih realizacija. Najee ete koristiti LinkedBlocking-
Queue, 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. Blo-
kiranje 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 me-
tode 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()11) ;
}
}
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 Izvr-
savaLansiranje. 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 11 + 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 syn-
chronized), 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 blo-
kirajuim 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 predsta-
vljaju 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.InterruptedEx-
ception: 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 Prijem-
nika pravljenje PipedReadera m ora biti p ridrueno odreenom PipedWriteru u kon-
struktoru. 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 stra-
ne, 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 je-
dan 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 skrive-
nu m ogunost uzajam ne blokade. U tom sluaju, uopte ne m orate dobiti nikakvu na-
znaku 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 fi-
Iozofa 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 Sta-
pic 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 e-
kaju 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 te-
zinskiFaktor razliit od nule. Dakle, F ilozof razmilja tokom nasum ino odabranog vre-
m 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 po-
kuaju da jedu svi e jedan drugom konkurisati za Stapice i uzajam na blokada e nastu-
piti 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 vaz-
dan razmiljaju, m oda uopte neete videti uzajam nu blokadu, iako je ona i dalje m o-
gua. 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 de-
sni i tako se obiao krug oko stola - naime, poslednji Filozof sedi odm ah do prvog i obo-
jica 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 verovat-
noa da e im zatrebati deljeni resursi (Stapici) m nogo m anja, i vi ete biti uvereni da pro-
gram 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 pokazu-
je 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 je-
dan 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 pro-
gram 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, dovolj-
no 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 sprea-
vanjem 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 Sta-
pic 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 sle-
dei nain: kada filozof upotrebi tapie, neka ih ne sputa na sto nego u zdelu. Kada fi-
lozof 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 ob-
jekat 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 ko-
risti 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 prika-
zuje 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 u-
lira 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 Count-
DownLatch definisanim u m etodi m a in ().
Veba 32: (7) Upotrebite CountDownLatch za korelaciju rezultata raznih Ulaza u pri-
m 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 DeoZadat-
ka 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 bi-
blioteke. 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 sli-
no 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 ula-
cija: igra s konjskim trakam a napisana u BASIC-u, nazvana (zbog ograniene duine
im ena datoteka) HOSRAC.BAS. Sledi objektno orijentisana, vienitna verzija tog progra-
ma, 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 rea-
lizuje interfejs Runnable i izvrava se autom atski kada se (na poetku zadat) broj izjed-
nai 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 procesor-
skog 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 izvra-
vanja. Ako se nijedno vrem e odlaganja nije skratilo, onda nem a eonog elem enta i meto-
da 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 skra-
tilo 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() {
return St ri ng .f or ma t("[%l$-4d]", delta) +
" Zadatak " + id;
}
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 odra-


va 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 kla-
sa 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 reali-


zovati 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 po-
trebe da pravim o zasebne niti za izvravanje odloenih zadataka (objekata tipa Odloze-
niZadatak).
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 na-
rednom prim eru, objekti u prioritetn o m redu za ekanje su zadaci koji se iz reda za e-
kanje 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 int id = brojac++;
private final 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 + 11 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 Stra-
zarN 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 o-
rate 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 stak-
leniku zadatak koji se izvrava u unapred definisano vreme. Klasa ScheduIedThread-
PoolExecutor 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 izvra-
vanje u odreeno budue vreme. U poredite naredni prim er i prim er iz poglavlja Unu-
tranje 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 float poslednjaTemp = 65.0f;
private int smerTemp = +1;
private float poslednjaVlaznost = 50.Of;
private int smerVlaznosti = +1;
private 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 pera-
ture 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 me-
todom synchronizeL ist() iz biblioteke java.util.Collections.
Veba 33: (7) Izm enite RasporedjivacStaklenika.java tako da koristi DeIayQueue um e-
sto ScheduledExecutora.

Semafor
O bina brava (eksplicitna iz concurrent.locks ili ugraena synchronized) u svakom tre-
nutku dozvoljava sam o jed no m zadatku da pristupi svakom resursu. Setnafor broja om o-
guuje 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 nikak-
vi 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 izdajObjekatNa-
Koriscenje(), a kada vam objekat vie nije potreban, prosledite ga m etodi primiObjekat-
NazadUGrupu().
Poglavlje 21: Paralelno izvravanje 999

Niz izdat tipa boolean prati objekte koji su izdati na korienje, a njim e upravljaju me-
tode uzm iStavku() i pustiStavku(). N jih pak uva sem afor (objekat tipa Semaphore)
dostupan, tako da u m etodi izdajObjekatNaKoriscenje(), dostupan blokira poziv uko-
liko 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 ZadatakaKojiUzimaju-


Resurs 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 bra-
tite 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 moe-
te 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 kori-
ste 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 part-
nerski 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 ConcurrentModificationExcepti-
on). RazmenjivacProizvodjac popunjava jednu listu, zatim zamenjuje p u n u listu praz-
nom 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 pra-
vljenje 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 a-
cije u film ovim a su simulacije, a i ranije prikazani program i TrkaKonja.java i Raspored-
jivacStaklenika.java mogu se sm atrati sim ulacijama.

Simulacija alterskog slubenika


Ova klasina simulacija predstavlja svaku situaciju u kojoj se objekti pojavljuju nasu-
m 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 nasu-
mino. 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 is-
pisuje 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 (Prio-
rityQueue) 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 prilagodiBrojSluzbe-
n 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 kli-
jenata nem a).
Veba 35: (8) Izm enite program SimuIacijaSalterskogSluzbenika.java tako da predsta-
vlja 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 mo-
gli 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
Gost 1 jede PROLECNE_ROLNICE
Konobar 3 primio PROLECNE_ROLNICE to posluuje gostu Gost 0
Gost 0 jede PROLECNE_ROLNICE
Konobar 0 primio PLJESKAVICU to posluuje gostu Gost 1
Gost 1 jede PLJESKAVICU
Konobar 3 primio PR0LECNE_R0LNICE to posluuje gostu Gost 2
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 0
Gost 0 jede LOVACKI_PIR
Konobar 0 primio VOCE to posluuje gostu Gost 1

*///:-
Poglavlje 21: Paralelno izvravanje 1013

Jedna od veom a vanih stvari na koje u ovom prim eru treba obratiti panju jeste upra-
vljanje 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 Lin-
kedBlockingQueue. 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 Linked-
BIockingQueue 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 e-
kanje 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 ne-
kim 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 Zabranje-
no 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 na-
m 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 sva-
kog 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 TestSinhronizacij 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 izri-
cati 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 i-
taju (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 : %13d\n", "Ciklusa", A k u m ul at or .c ik lu sa );


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) {
int iteracija = 5; // Podrazumevani broj
if(args.length > 0) // Opciono promeni broj iteracija
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: (primer)
Zagrevanje
OsnZ aP or : 34237033

Ciklusa : 50000
OsnZ aP or : 20966632
synchronized : 24326555
Lock : 53669950
Atomi c : 30552487
synchroni zed/OsnZaPc r 1.16
Lock/OsnZaPor 2.56
Atomi c/OsnZaPor 1.46
synchroni zed/Lock 0.45
synchronized/Atomic 0.79
Lock/Atomi c 1.76

Ciklusa : 100000
OsnZaPor : 41512818
synchronized : 43843003
1024 Misliti na Javi

Lock 87430386
Atomi c 51892350
synchroni zed/OsnZaPor : 1.06
Lock/OsnZaPor : 2.11
Atomic/OsnZaPor : 1.25
synchronized/Lock : 0.50
synchronized/Atom 'c : 0.84
Lock/Atomic : 1.68

Ciklusa 200000
OsnZaPor 80176670
synchronized 5455046661
Lock 177686829
Atomic 101789194
synchroni zed/OsnZaPor 68.04
Lock/OsnZaPor 2.22
Atomic/OsnZaPor 1.27
synchronized/Lock 30.70
synchroni zed/Atomi c 53.59
Lock/Atomi c 1.75

Ci klusa 400000
OsnZaPor 160383513
synchronized 780052493
Lock 362187652
Atomi c 202030984
synchronized/OsnZaPor 4.86
Lock/OsnZaPor 2.26
Atomi c/OsnZaPor 1.26
synchroni zed/Lock 2.15
synchroni zed/Atomi c 3.86
Lock/Atomic 1.79

Ci klusa 800000
OsnZaPor 322064955
synchroni zed 336155014
Lock 704615531
Atomi c 393231542
synchronized/OsnZaPor : 1.04
Lock/OsnZaPor 2.19
Atomi c/OsnZaPor 1.22
synchronized/Lock 0.47
synchronized/Atomic : 0.85
L o c k /Atomic 1.79

Ci klusa 1600000
OsnZaPor 650004120
synchroni zed 52235762925
Lock 1419602771
Atomi c 796950171
Poglavlje 2 1: Paralelno izvravanje 1025

sy nchronized/OsnZaPor : 80.36
Lock/OsnZaPor : 2.18
A tomic/OsnZaPor : 1.23
synchronized/Lock : 36.80
synchronized/Atomic : 65.54
Lock/Atomic : 1.78

Ciklusa : 3200000
OsnZaPor : 1285664519
synchronized : 96336767661
Lock : 2846988654
Atomic : 1590545726
synchronized/OsnZaPor : 74.93
Lock/OsnZaPor : 2.21
Atomic/OsnZaPor : 1.24
synchronized/Lock : 33.84
synchronized/Atomic : 60.57
Lock/Atomic : 1.79
*///:-

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 me-
toda ak u m u liraj() i c ita j() u izvedenim klasama. U izvedenim klasama TestRrSynchro-
nized, 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 tro-
kovi tokom testiranja. Za svaki sluaj, poetno testiranje se ponavlja i prvi rezultati od-
bacuju, zato to obuhvataju i poetno pravljenje niti.
CyclicBarrier je potrebna zato to obezbeuje da se svi zadaci zavre pre nego to sva-
ko 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 una-
predUcitani (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 dobi-
janje objekta Akumulator.
O bratite panju na to da sam u TestuKlaseAtomic napomenuo kako je situacija pre-
vie 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 (po-
drazumevanih) ponavljanja. U svakom ponavljanju, broj ciklusa testiranja se udvo-
struuje, 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 iznenau-
ju 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 syn-
chronized.
Im ajte u vidu da ovaj program daje tek naznaku razlike izmeu raznih realizacija uza-
jam 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 eksperi-
mentisali s ponaanjem, ono se znatno menja u zavisnosti od broja niti i trajanja izvra-
vanja 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 synchroni-
zed 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 po-
trebne 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 programi-
ranju 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 PoreenjeSinhroni-
zacija.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 pro-
gram iranje, a to vai i za paralelno programiranje. Zato su prvi Java kontejneri (kao Vec-
tor 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 re-
zervisane 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 nevidlji-
va tokom postupka modifikacije. Modifikovana struktura se atomski zamenjuje glav-
nom 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 i-
tanja 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 Co-
pyOnW 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 po-
naanje 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 kontej-
nera, a ne ceo kontejner. Meutim, itaoci ne vide modifikacije pre nego to budu zavre-
ne. ConcurrentHashM ap ne baca izuzetke ConcurrentM odificationException.

O performansama
Ukoliko iz kontejnera bez zakljuavanja uglavnom itate, bie to mnogo bre nego da i-
tate iz njegove sinhronizovane verzije, zato to izbegavate reijske trokove od zakljua-
vanja 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; i++) {
izvr si Te st ir an je ();
vremeCit = 0;
vremeUpis = 0;
}
}
void izvrsiTestiranje() {
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() {
long vremePocetka = S y st em .n an oT im e( );
test();
trajanje = System.nanoTime() - vremePocetka;
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.");
}
} III--
Apstraktna metoda inici;aIizatorK ontejnera() vraa inicijalizovan kontejner koji tre-
ba testirati i smeta ga u polje kontejnerZaTestiranje. Druga apstraktna metoda,
pokreniCitaoceIU pisivace(), pokree zadatke itanja i upisivanja koji e itati i modifi-
kovati testirani kontejner. Razni testovi se obavljaju s razliitini brojem italaca i upi-
sivaa 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() iteracijaTe-
stiranja 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) ko-
ristite tako to iz klase Tester izvedete konkretni tip kontejnera koji elite da testirate i na-
pravite odgovarajue klase Citalac i Upisivac:

//: paralelno/PoredjenjaListi .java


// {Args: 1 10 10} (Brza verifikaciona provera tokom builda)
// Grubo poreenje performansi lista bezbednih u vienitnom radu.
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() {
for(long i = 0; i < ciklusaTestiranja; i++)
for(int indeks = 0; indeks < v e l icinaKontejnera; indeks++)
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() {
for(long i = 0; i < ciklusaTestiranja; i++)
for(int indeks = 0; indeks < velicinaKontejnera; indeks++)
kontejnerZaTestir a n j e . s e t (indeks, upisiP od at ke [i nd ek s]);
}
void upisiRezultate() {
vremeUpis += trajanje;
}
}
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 {
for(int i = 0; i < nCitalaca; i++)
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
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[] args) {
Tester.i ni cMMai n ( a r g s ) ;
new TestSinhronizovanog0bjektaTipaArrayList(10. 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, 5);
T e s t er .e xe c. sh ut do wn( );

} /* Ispis: (primer)
Tip Vreme itanja Vreme upis.
Sinhro. ArrayList 10c Ou 232158294700 0
Sinhro. ArrayList 9c lu 198947618203 24918613399
vremeCit + vremeUpis = 223866231602
Sinhro. ArrayList 5c 5u 117367305062 132176613508
vremeCit + vremeUpis = 249543918570
CopyOnWriteArrayList 10c Ou 758386889 0
CopyOnWriteArrayList 9c lu 741305671 136145237
vremeCit + vremeUpis = 877450908
CopyOnWriteArrayList 5c 5u 212763075 67967464300
vremeCit + vremeUpis = 68180227375
*/// =-

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 Array-
List ima priblino iste performanse bez obzira na broj italaca i upisivaa - itaoci se nad-
meu za brave sa drugim itaocima, isto kao upisivai. Meutim, CopyOnW rite-
ArrayList je mnogo bri kada nema upisivaa i jo uvek znatno bri s pet upisivaa. Iz-
gleda 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 sinhroni-
zacije 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 apsolut-
nih brojeva. Vi ete gotovo sigurno dobiti drugaije brojeve. Cilj mi je bio samo da ste-
knete uvid u relativna ponaanja te dve vrste kontejnera.
Poto skup CopyOnW riteArraySet upotrebljava listu CopyOnW riteArrayList, nje-
govo ponaanje e biti slino i ovde ga ne moramo zasebno ispitivati.

Poreenje realizacija Mapa


Istu strukturu moemo da upotrebimo za dobijanje grube slike performansi sin-
hronizovane 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() {
for(long i = 0; i < ciklusaTestiranja; i++)
for(int indeks = 0; indeks < velicinaKontejnera; indeks++)
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() {
vremeUpis += trajanje;
}
}
void po kr en i C i t a o c e l U p i s i v a c e O {
for(int i = 0; i < nCitalaca; i++)
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 ) ;
}

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, int nUpisivaca) {
super("Sinhro. HashMap", nCitalaca, nUpisivaca);
}
}

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, int nUpisivaca) {
super( "C on cu rr en tH ash Ma p", nCitalaca, nUpisivaca);
}
}

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);
1034 Misliti na Javi

new TestiranjeSinhronizovaneHashMape(9, 1);


new TestiranjeSinhronizovaneHashMape(5, 5);
new TestiranjeConcurrentHashMape(10, 0);
new TestiranjeConcurrentHashMape(9, 1);
new TestiranjeConcurrentHashMape(5, 5);
Tester .e xe c. sh ut do wn( );
}
} /* Ispis: (primer)
Tip Vreme itanja Vreme upis.
Sinhro. HashMap 10c Ou 306052025049 0
Sinhro. HashMap 9c lu 428319156207 47697347568
vremeCit + vremellpis = 476016503775
Sinhro. HashMap 5c 5u 243956877760 244012003202
vremeCit + vremeUpis = 487968880962
ConcurrentHashMap 10c Ou 23352654318 0
ConcurrentHashMap 9c lu 18833089400 1541853224
vremeCit + vremeUpis = 20374942624
ConcurrentHashMap 5c 5u 12037625732 11850489099
vremeCit + vremeUpis 23888114831
*///:-

Uticaj dodavanja upisivaa na ConcurrentHashM ap manji je ak i od uticaja na Co-


pyOnW 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 decrement-
AndGet( ), neke Atomic klase omoguuju i tzv. optimistiko zakljuavanje. To znai da
prilikom izraunavanja ne koristite uzajamno iskljuivanje, ali da nakon zavretka izrau-
navanja 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 (re-
zervisanu re synchronized ili objekat tipa Lock) i tako spreavamo da vie zadataka isto-
vremeno modifikuje objekat, ali ovde se ponaamo optimistiki tako to podatke
ostavljamo nezakljuane i nadamo se da drugi zaaci nee uleteti i modifikovati ih. Narav-
no, sve to radimo samo zbog performansi - korienjem objekta tipa Atomic umesto re-
zervisane 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 va-
n 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 po-
kuati da ponovite operaciju i nee smetati ako ona (tek) drugi put uspe. Ili moda moe-
te 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 gene-
tikog algoritma potreban veoma skup proraun, pa ste odluili da upotrebite vieproce-
sorski 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 mo-
gui 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 zane-
mari 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 int N_ELEMENATA = 100000;
static final int N_GENA = 30;
static final 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[] args) throws Exception {


ExecutorService exec = Ex ec ut or s. ne wC ac he dTh re ad Po ol();
for(int i = 0; i < N_ELEMENATA; i++)
for(int j = 0; j < N_GENA; j++)
MATRICA[i][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)) ;
for(int i = 0 ; i < N_EVOLVERA; i++)
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 vred-
nost 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 ret-
ko, 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 pro-
grama; to zavisi od inilaca kao to su srazmera broja itanja i broja modifikovanja poda-
taka, 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 ReadWri-
teLock poboljava va program jeste da ga isprobate.
U narednom primeru prikazana je samo najjednostavnija upotreba klase ReadVVri-
teLock, 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) {
Lock bravaZaUpisivanje = b r av a. wr it eL oc k( );
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) {
Lock bravaZaCitanje = b r a v a . r e a d L o c k ( ) ;
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[] args) throws Exception {
new Te stListeCitalacaIUpisivaca(30, 1);
}
}

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
}
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()) {
for(int i = 0; i < VEUICINA; i++) {
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) *///:-

ListaCitalacalUpisivaca moe sadrati fiksan broj objekata proizvoljnog tipa. Nje-


nom 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 po-
zove 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 i-
talaca 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 ko-
rektno 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 re-
Iativno nepromenjen iz sveta proceduralnog programiranja. Moda postoji drugaiji mo-
del paralelnog rada, prikladniji za objektno orijentisano programiranje.
Jedan od alternativnih pristupa nazvan je aktivni objekti ili aktori.26 Objekti su proglae-
ni ,,aktivnim zato to svaki objekat odrava sopstvenu radnu nit i red za poruke; svi za-
htevi 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 ob-
jektov 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, "nevezane
// 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>() {

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 + 11 + " + 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) {
fo r( Future<?> f : rezultati)
if ( f .is D on e( )) {
try {
print(f.get());
} catch(Exception e) {
throw new R u n t im eE xc ep ti on (e );
}
re zu lt at i. re mo ve (f );
}
}
pl.shutdown();
}
} /* Ispis: (85% podudaranja)
Zavreni 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.newSingleThread-


E 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 me-
tode 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 pa-
ram etrom koji je stvarni povratni tip te metode. Na taj nain se poziv metode gotovo tre-
nutno 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 jed-
nostavniji 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 ko-
piramo 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 uni-
cira 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 jed-
nu poruku, nema takm ienja za resurse i ne moram o da sinhronizujemo metode. Sinhro-
nizaja 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 pro-
gramiranje 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 rau-
nara 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 odre-
dinu 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 alat-
ka 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 pre-
kid 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 ,,la-
kim 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 izvra-
vanju (vie niti izvrava pojedinane zadatke koje sve zajedno ne mogu da zavre).
5. Uslovi za paralelno izvravanje menjaju se u zavisnosti od platforme. (Piui pri-
mere 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 razvija-
li na jednom od raunara na kojem se Ioi delovi ne vide, doiveete neprijatno iz-
nenaenje 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 gor-
nja 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 programi-
ranje. 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. (Go-
vorim iz vlastitog iskustva, poto sam ve vie puta pomiljao kako sam ga napokon savla-
dao; 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 za-
pravo 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.
The Thinking in Java Annotated Solu-
Reenja o d ab ran ih vebi data su u elek tron sk om d o k u m en tu
tion 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. Ume-
sto 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 si-
stemima. Osim toga, ta biblioteka je jako ograniena: dozvoljeno je korienje samo etiri
fonta, a ne moe se pristupiti nijednom naprednijem elementu grafikog okruenja iz ne-
kog operativnog sistema. Programerski model AWT iz Jave 1.0 istovremeno je bio nezgra-
pan 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, projek-
tovan 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 ja-
sniji, 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 pre-
pravi) 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 ne-
kog 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. Uko-
liko 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 iznena-
ivati.
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 bibli-
oteku 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 o-
guuju razvojnom okruenju da pie kod umesto vas, dok vi razmetate kompo-
nente na obrasce pomou grafikih alatki. To u velikoj meri ubrzava projektovanje
grafikog okruenja i om oguuje slobodnije eksperimentisanje i isprobavanje di-
zajna, to posledino dovodi do izbora boljeg okruenja.
3. Jednostavnost i dobar projekat grafike biblioteke Swing znai da e ak i ako kori-
stite vizuelnu alatku, umesto da runo piete kod, dobijeni kod biti razumljiv. To
reava veliki problem s korienjem ranijih vizuelnih alatki kojima se esto generi-
sao nerazumljiv kod.
Grafika biblioteka Swing sadri sve komponente koje se oekuju u modernom korisni-
kom 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 ,,or-
togonalnost 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 stan-
dardu za imenovanje. To je, sasvim sigurno, obeleje dobre biblioteke. Pored toga, kom-
ponente se obino mogu dodavati drugim komponentama i sve e raditi ispravno.
Navigacija pom ou tastature je automatska: aplikaciju napisanu pomou Svvinga ino-
ete 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 sa-
veta (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 ponaa-
nje (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 okru-
enja. 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 pe-
skom, da ne bi nakodio klijentskom raunaru). Predvialo se da e Java aplet postati sle-
dea 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 istri-
buciju 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 si-
stemima 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 ko-
risnik 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[] args) throws Exception {
JFrame prozor = new JFrame("Zdravo, Swing");
JLabel 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 jedno-
stavnom 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 uza-
jam 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 do-
gaaja (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 .*;

4 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 in-
terfejs Runnable, a stvarno rukovanje obavlja nit za otpremu dogaaja kada u reu za e-
kanje 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 prid-
ravao 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 me-
tode 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() {
ps p. na tp is .s et Te xt ("E j! Ovo se p r o m e n i l o ! ");
}
0;
}
} ///-

Obratite panju na to da poziv metode sleep () rtije unutar konstruktora. Ako ga sta-
vite 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 po-
ziva metode setD efauItCIoseO peration().
Veba 2: (2) Izmenite ZdravoNatpis.java tako da pokaete da je doavanje natpisa di-
namiko - 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 int sirina, final int visina) {
SwingUti 1 it ie s.invokeLater(new R u n n a b l e O {
public void run() {
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 Swing-
Konzola.

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() {
setLayout(new Fl ow La yo ut () );
add (dl);
add(d2);
}
public static void main(String[] args) {
run(new D u g m e l O , 200, 100);
}
} ///:-

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 kon-
trolni 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 upra-
vljanog dogaajima, to ini veliki deo programiranja grafikih korisnikih okruenja, je-
ste 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 dug-
meta, 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 JBut-
ton, 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 me-
todom addA ctionListener(). Metoda prosledenog objekta pozivae se kad god se priti-
sne 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 me-
njati tekst. Iako postoji vie naina da se napravi objekat klase JTextFieId, najjednostav-
niji 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 JTextFieldsadri 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 action-
Performed( ) tipa je ActionEvent i sadri sve inform acije o dogaaju i mestu gde je na-
stao 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) ui-
nio 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 unutra-
nja 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[] args) {
run(new Dugme2b(), 200, 150);
}
} 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 una-
zad, 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 ure-
daju. Kao primer, razmotrimo sledei program koji popunjava vieredno polje za tekst iz-
laznim 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() {
public void actionPerformed(ActionEvent e) {
for(Map.Entry me : m.entrySet())
t.append(me.getKey() + ": "+ m e . g et Va 1u e( )+ "\ n" );
}
});
c.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
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[] args) {
run(new TextArea(), 475, 425);
}
} /// =-

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 dugme-
ta 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 kliza-
ima (JScrollPane) da bi se mogao pomerati sadraj kada se na ekranu pojavi previe tek-
sta. 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 (pri-
mene 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 kom-
ponente 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 ap-
likaciju 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 osta-
lim 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, ob-
lik 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 po-
loaj 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 posta-
vljati 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 me-
todu a d d () iji je prvi argument konstantna vrednost. Ta vrednost moe da bude neto od
sledeeg:

Borderl_ayout.NORTH fvrh)
BorderLayout. SOUTH |dno)
BorderLayout. EAST (desno)
BorderLayout.WEST (ievoj
BorderLayout.CENTER 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 ko-
risti 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 11) ) ;
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 po-
puni 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 po-
malo 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 prome-
nite 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 formati-
ranju oblasti u prozoru kada mu se promeni veliina. Meutim, to je istovremeno i naj-
sloeniji 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 kompo-
nenta 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 pik-
selima. 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 obi-
no nije najbolji nain za generisanje koda.

Rasporeiva BoxLayout
Poto korisnici nisu razumevali klasu GridBagLayout i teko su je primenjivali, u biblio-
teku Swing dodata je i klasa BoxLayout koja prua mnoge prednosti rasporedivaa Grid-
BagLayout, 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 kon-
trolisanje poloaja komponenata, a za kontrolisanje prostora izmedu komponenata kori-
ste 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. Pri-
meri u ovoj knjizi prilino su jednostavni, a radi uenja vredi ih napisati runo. Prilino
mnogo se moe postii kombinovanjem jednostavnih rasporeivaa. Meutim, u odree-
nom trenutku, runo pisanje koda za grafiko okruenje postaje besmisleno, previe sloe-
no 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 izbo-
rite 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 bi-


blioteke 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 in-
terfejsa. Zbog toga treba samo da napravite objekat prijemnika i da ga prijavite kom po-
nenti 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 do-
gaaja 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 gra-
fike 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 pri-
mer: addMojPrijemnik (M ojPrijemnik m). U sledeoj tabeli nabrojani su osnovni doga-
aji, 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 mo-
del dogaaja moe proirivati, pa ete moda naii na druge tipove prijemnika koji nisu
navedeni u ovoj tabeli.

Dogaaj, prijemniki interfejs Kom ponenta koja podrava taj dogadaj


i m etode add- i remove-
ActionEvent JB u tto n JList JTextField JM enu ltem i klase izvedene iz
ActionListener ovih, kao to su JCheckBoxM enultem , JM en u
addActionListenerf ) i JRadioButtonM enultem
removeActionListener( ]
A djustm entEvent JScrollbar i sve to napravite, a realizuje interfejs Adjusta-
Adjustm entListener ble
addAdjustm entListenerf )
rem oveAdjustm entListener| )
Com ponentEvent Klasa Com ponent i klase izvedene iz nje, medu kojima su
Com ponentListener JB u tto n JCheckBox JCom boBox, Container JP a n e l
addCom ponentListener) ) JA p p let JScrollPane, \X/indow, JDialog, JFileD ialog
rem oveCom ponentListener( ) JFram e, JLabel, JList, JScrollbar, JTextArea i JTextField
Poglavlje 22: Grafika korisnika okruenja 1061

D ogaaj, prijemniki interfejs Kom ponenta koja podrava taj dogaaj


i m etode add- i remove- (nastavak)
ContainerEvent Klasa Container i klase izvedene iz nje.
ad d C on tainerListener() JScrollPane, W indow , JDialog, JFileDialog,
rem oveC ontainerListener() i JFram e
FocusEvent Klasa Com ponent i klase izvedene iz nje*.
FocusListener
addFocusListener( )
rem oveFocusListener()
K eyEven t Klasa Com ponent i klase izvedene iz nje*.
KeyListener Klasa Com ponent i klase izvedene iz nje*.
addKeyListener( )
rem oveKeyListener
M ouseEvent |za pomeranje Klasa Com ponent i klase izvedene iz nje*.
i pritiskanje tastera mia)
MouseListener
ad d M o u seListener()
removeMouseListenerf )
M ouseEvent6 (za pomeranje Klasa Com ponent i klase izvedene iz nje*.
i pritiskanje tastera mia)
MouseMotionListener
addM ouseM otionListener
rem oveM ouseM otionListener( )
W in d o w E v e n t Klasa W in d o w i klase izvedene iz nje. meu kojima su i JDi-
W ind o w Listener alog JFileD ialog i JFram e
ad d W ind o w Listen er( )
remove\X/indowListener( )
Item Event JCheckBox, JCheckBoxM enultem ,
ItemListener JCom boBox, JList i sve to realizuje interfejs
addltem Listener( ) ItemSelectable
rem oveltem Listener( )
TextEvent Sve toje izvedeno iz klase JTextComponent, ukljuujui
TextListener i JTextArea i JTextField
addTextListener( )
removeTextListener( )

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 prika-
zuje 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 za-
data 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 o-
u 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 do-
kum 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 ko-
risnije verzije za grafiko okruenje koja trai metode tipa ,,addListener u Swing kompo-
nentama:

//: 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) {
re zultati.setText("Ne p o s t o j i ");
return;
}
Class<?> vrsta;
try {
vrsta = Class.forName("javax.swing." + im)
} catch(ClassNotFoundException ex) {
rezultati,setText("Ne p o s t o j i " ) ;
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");
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 une-
sete 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 ve-
likih i malih slova), metoda Class.forN am e() nalazi klasu i getM ethods() vraa niz obje-
kata 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 oda-
brati prvu podudarnu grupu unutar zagrada. Taj znakovni niz jo uvek sadri kvalifikato-
re, 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 do-
gadaj (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 preo-
stane. 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 meto-
du m ouseM oved() interfejsa M ouseM otionListener. (Naravno, morate da reali-
zujete 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 kompo-
nenti metodom ije ete ime dobiti ako ispred imena prijem nika dodate add, npr.
addMouseMotionListener.
Evo nekih prijemnikih interfejsa:

Prijemniki interfejs i adapter M etode u interfejsu


ActionListener actionPerform ed(ActionEuent)
AdjustmentListener adjustm entValueChanged(Adjustm ent Event)
ComponentListener com ponentH idden(Com ponentEvent)
Com ponentAdapter com ponentShow n(Com ponentEvent)
com ponentM oved(Com ponentEvent)
com ponentResized(Com ponentEvent)
ContainerListener com ponentAdded(ContainerEvent)
ContainerAdapter com ponentRem oved(ContainerEvent)
FocusListener focusGained(FocusEvent)
FocusAdapter focusLost(FocusEvent)
KeyListener keyPressed(KeyEvent)
KeyAdapter keyReleased(KeyEvent)
keyTyped(KeyEvent)
MouseListener mouseClicked(MouseEvent)
MouseAdapter m ouseEntered(M ouseEvent)
m ouseExited(M ouseEvent)
m ousePressed(MouseEvent)
m ouseReleased(M ouseEvent)
MouseMotionListener m ouseDragged(M ouseEvent)
MouseMotionAdapter mouseM oved (MouseEvent)
\X/indowListener w in d o w O p e n e d (W in d o w Eve n t)
W in dow A d apter w indow Closing (\X/indowEvent)
w m d o w C lo sed (\X /m d o w E ve n t)
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 dozvo-
ljava da pravite sopstvene tipove dogadaja i prijemnike za njih. Zato ete esto viati bi-
blioteke 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 ve-
oma lako realizuju, poto se to radi samo kada elite da napiete metodu koju sadre. Me-
utim, 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 MouseLi-
stener 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 obez-
beuje 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 Mouse-
Adapter 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 iz-
vrava, 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 po-
red 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 ugra-
enu 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 do-
datno 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 pri-
stup 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 Pra-
tiDogadjaj. 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", "focusLost", "keyPressed",
"keyReleased", "keyTyped", "m ou se C l i c k e d " ,
"mouseEntered", "mouse Ex it ed "," m o u se Pr es se d",
"mouseReleased", "m ou se Dr ag ge d", "mouseMoved"
};
private MojeDugme
dl = new MojeDugme(Color.BLUE, "testl"),
d2 = new MojeDugme(Color.RED, "test2");
class MojeDugme extends JButton {
void prijavi(String polje, String poruka) {
(h.g e t (polj e ) ) ,setText(poruka);
}
FocusListener fl = new FocusListener() {
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 ve-
likih propusta u projektovanju.
Poglavlje 22: Grafika korisnika okruenja 1067

public void keyReleased(KeyEvent e) {


prijavi ("keyReleased", e . p a r a m S t r i n g O ) ;
}
public void keyTyped(KeyEvent e) {
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) {
pri javi ("mouseDragged", e . p a r a m S t r i n g O ) ;
}
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) {
JTextField t = new JTextField();
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);
1068 Misliti na Javi

h.put(dgd, t ) ;
1
add(dl);
add(d2);
}
public static void main(String[] args) {
run(new P r at iD og ad ja j(), 700, 500);
}
1 ///=-

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 doga-
daju. 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 do-
gadjaj; 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 do-
gadajima.
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 kori-
stiti kom ponente grafike biblioteke Swing. Ovaj odeljak ukratko prikazuje Swing kom-
ponente 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 lo-
kacije www.MindView.net).
2. U H TM L dokumentaciji na adresi java.sun.com opisane su sve klase i metode bibli-
oteke 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 potvr-
du, radio-dugmad, ak i stavke menija, izvedeni su iz klase A bstractButton (kojoj bi, po-
to obuhvata i stavke menija, bolje odgovaralo ime AbstractSelector" ili neto optije).
Uskoro ete saznati kako se koriste stavke menija, a sledei primer prikazuje razliite do-
stupne 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() {
setLayout(new F l ow La yo ut () );
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[] args) {


run(new Dugmad(), 350, 200);
}
} ///:-
1070 Misliti na Javi

Program prvo prikazuje klasu BasicArrowButton iz biblioteke javax.swing. plaf.ba-


sic, a zatim razne tipove dugmadi. Kada izvrite program, videete da dugme za ukljui-
vanje 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 dugma-
di. 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 = {
" G a j a " , " R a j a " , "Vlaja", "Miki", "Silja", "Pluton",
h
static JPanel napraviPano(
Class<? extends Ab st ra ct Bu tt on > vrsta, String[] ids) {
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) {
AbstractButton ab = new J B u t t o n ( " n e u s p e n o " ) ;
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);
}
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));
add(makeBPanel(JToggleButton.class, ids));
add(makeBPanel(JCheckBox.class, ids));
add(makeBPanel(JRadioButton.class, i d s));
}
public static void main(String[] 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 ar-
gumenata 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 vi-
deti prim er). U sledeem primeru istrauju se drugi naini korienja ikonica s dugma-
dima 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.Min-
dVicw.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", likovi[3]),
setLayout(new F l ow La yo ut () );
jb .a ddActionListener(new ActionListener() {
public void ac tionPerformed(ActionEvent e){
if(besan) {
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() {
public void actionPerformed(ActionEvent e ) {
if(jb.isEnabled()) {
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[] args) {
run(new Likovi(), 250, 125);
}
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 pri-
mer 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, isklju-
i 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 setTool-
TipText(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 po-
kazivaa 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 dogaaji.
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 tekst"),
d2 = new JButton("Zadaj tekst");
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 ());
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[] args) {
run(new Po lj aZ aT ek st (), 375, 200);
}
)

class DokumentSVelikimSlovima extends PlainDocument {


boolean pretvaranje = true;
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. Vi-
deete 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 DokumentSVelikimSIo-
vima 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 pr-
vobitni raspored malih i velikih slova kako su bila unesena, umesto da se automatski pre-
tvaraju 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 teh-
nika 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[] args) {
run(new Ivice(), 500, 300);
}
} ///:-

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. Sle-
dei 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() {
public void ac ti on Pe rf or me d( Ac tio nE ve nt e ) {
for(int i = 1; i < 10; i++)
tp.setText(tp.getText() + sg.next() + "\n");
}
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);
}
} ///:-

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 JTextPane tr e b a d a


ap p en d ( ).
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
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 pre-
lamanje redova. Postoje i brojne druge funkcije o kojima moete mnogo saznati iz doku-
m entacije razvojnog programskog paketa JDK.
Veba 14: (2) Promenite program TekstualniPano.java tako da koristi polje tipa JText-
Area umesto JTextPane.

Polja za potvrdu
Polje za potvrdu om oguuje pravljenje opcije koja moe da bude potvrdena ili nepotvr-
ena; sastoji se od malog polja i oznake. Polje obino sadri malo x (ili neto drugo to po-
kazuje 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 nat-
pis. 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 ActionLi-
stener. 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, 15);
private JCheckBox
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() {
public void ac tionPerformed(ActionEvent e){
obradi("l", c b l ) ;
)
));
cb2.addActionListener(new A c t i o n L i s t e n e r O {
public void ac tionPerformed(ActionEvent e){
obradi("2", cb2);
)
});
cb3.addActionListener(new ActionListener() {
public void ac tionPerformed(ActionEvent e ) {
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) {
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 doga-
aj i umetnite drugaiji tekst u polje.

Radio-dugmad
Ime radio-ugme u programiranju grahkih korisnikih okruenja izvedeno je aso-
cijacijom 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 mo-
gla 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 do-
date u grupu tipa ButtonGroup. (U obrascu moe da postoji proizvoljan broj grupa dug-
madi.) 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 do-
gaaji 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 vred-
nosti 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 prime-
ru, polje tipa JCom boBox na poetku sadri odreen broj stavki, a zatim se nakon priti-
skanja 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",
"Zastraujui" , "Ispunjen", "Iskvaren" };
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() {
public void actionPerformed(ActionEvent e ) {
if(broj < opis.length)
c.addItem(opi s[ broj++]);
}
});
c.addActionListener(new ActionListenerf) {
public void actionPerformed(ActionEvent e){
t.setText("indeks: "+ c.getSelectedIndex() + 11 " +
((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 iza-
bran 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 po-
zovite 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 pri-
tisnut 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] ukusi= {
"okolada", "Jagoda", "Vanila", "Pepermint",
"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) {
elementiListe.add(0, ukusi [bro j+ +] );
} else {
// Iskljui, poto nema vie ukusa
// koji bi se dodali u listu
1082 Misliti na Javi

b. se tE nab1ed(false);
}
}
};
private ListSelectionListener 11 =
new ListSelectionListener() {
public void valueChanged(ListSelectionEvent e) {
if (e .g et Va lu el sA dj ust in gO ) return;
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 je-
zici. 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", "Jagoda", "Vanila", "Pepermint",
"Moka", "Rum grozice", "Praline krem", "Pita od blata"
};
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 ve-
liinu 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 biblio-
teci 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 JOpti-
onPane.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() {
setl_ayout(new FlowLayout());
for(int i = 0; i < b.length; i++) {
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 showInputDia-
lo g ( ) objekti s vrednou koju je uneo korisnik.
Veba 17: (5) Napravite aplikaciju p o m ou klase SwingKonzola. U HTML dokum enta-
ciji 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 JOpti-
onPane.
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 kla-
se 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 JMenu-
Item 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 izvor-
n 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 sva-
ku 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 napra-
viti 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 sasta-
vljena 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 i-
ja). Kada se linija menija sastavi, m oe se prikazati u tekuem prozoru m etodom setJMe-
nuBar( ). 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 dru-
gom 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 po-
reenja znakovnih nizova est je izvor greaka u programiranju.
Potvrivanje i odjavljivanje stavki m enija obavlja se autom atski. Kod kojim se obra-
uju stavke tipa JCheckBoxMenuItem prikazuje dva naina za odreivanje onoga to je
potvreno: pronalaenje istih znakovnih nizova (kao to je reeno, to nije naroito bez-
bedan pristup iako se s vrem ena na vrem e koristi) i pronalaenje istih ciljnih objekata do-
gaaja. 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 JMe-
nultem 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 e-
ru 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 upo-
trebite preklopljeni konstruktor iji je drugi argum ent identifikator tastera. M eutim , ve-
ina 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 doda-
te su preice u neke stavke menija; indikatori preica autom atski se pojavljuju na kom po-
nentam 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 dode-
ljuje kom anda akcije.
Poglavlje 22: Grafika korisnika okruenja 1091

Najvei deo posla odvija se u prijem nicim a. Klasa BL zamenjuje linije m enija (objeka-
ta tipa JMenuBar). Klasa ML prim enjuje pristup otkrij ko je zvonio" tako to ispituje iz-
vor 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 stav-
kom 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 e-
nije i podm enije kao stavke.

Iskaui meniji
Najjednostavniji nain za prim enu iskaueg menija tipa JPopupMenu jeste da se napra-
vi 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 prili-
no 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 si-
nus. Ua bi sve biio zammljivije, i da bi se jo jed n om pokazalo koliko se kom ponente bi-
blioteke 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 JPa-
nel. 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 obje-
kat 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 sa-
dri ukupan broj taaka, sinusi sadri vrednosti sinusne funkcije, a tck sadri Y koordi-
nate 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 e-
tode zadajCilduse( ) prouzrokuje se poziv m etode p ain tC om p on en t( ) u kojoj se dovr-
ava 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 bo-
jenje 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 ). Primeti-
ete 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 pri-
kazuju grafike podatke: najvei deo vrem ena provodiete smiljajui ta elite da nacrta-
te, 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 si-
nusoida 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 vred-
nost 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 addChangeLi-
stener. 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 argu-


m 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 prevazi-
laze 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 tre-
balo 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 velii-
n om prozora.
Veba 24: (7) Seate li se igrake s dva dugm eta - jednim koje kontrolie vertikalno kre-
tanje 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 o-
mera 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 sinusoida-
ma. 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 ja-
va.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 i-
ki 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 jednostav-
n 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 (napra-
vljena 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 na-
izm 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 o-
raju 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 otva-
ranje 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 kori-
ste 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 JCoIor-
C 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 a-
tirati 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 uobia-
jene HTML oznake. Obratite panju na to da nije obavezno korienje uobiajenih ozna-
ka 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 , JTooI-
Tip, 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 grafi-
ke 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 o-


dela, u redu:

pb.setModel(sb.getModel());

Ove dve kom p onente m ogli biste da kontroliete i p om ou prijemnika, ali je ovo ree-
nje prostije. ProgressM onitor nem a m odel, pa sam m orao da upotrebim prijemnik. Ve-
rovatno 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 pribli-
avanjem 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 na-
predovanja 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 iz-
gled 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 biblio-
teci 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 pri-
lino 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 po-
sveenim 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 me-
tode 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 izu-
zetku biva veom a korisna pri traenju greaka, pa u bloku catch m oete staviti barem na-
redbu 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(); 1
}
} 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 iz-
gleda. 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 adre-
si 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 doda-
tku ovog poglavlja na adresi w w w .M indV iew .net. Potpisani apleti su m on i i m ogu da za-
m 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 pro-
blem 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 lini-

Ovaj 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 Interne-
ta 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 potpisa-
nim 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 re-
sursima korisnikovog raunara i kada su u nepotpisanim JAR datotekam a. Tokom izvra-
vanja programa, od korisnika se trai da odobri te zahteve.
JNLP je specifikacija protokola, a ne njegova realizacija. Zato je za korienje neop-
h 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; najjednostav-
nije 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 ap-
likacije. U koliko ne potpiete JAR arhivu, morate da koristite usluge koje interfejs za pro-
gramiranje 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 resur-
sim 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 pri-
stupanja resursima preko, recim o, objekta tipa File ili FileReader, izazvao bi bacanje izu-
zetka 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 JnlpBiranjeDa-
toteka.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 dato-
teka 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. Atri-
but 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 da-
toteku 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 (ulaz-
na 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 pret-


h 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 hipertekstu-
alnu 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 kon-


zole. Ako Java Web Start koristite u W indow su, prilikom druge upotrebe m oi ete da na-
pravite 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 clip-
board. 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() Swin-
govog 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 zabora-
vite 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 obra-
titi panju prilikom rada u Swingu.
Poglavlje 22: Grafika korisnika okruenja 1111

Dugotrajni zadaci
Jedna od osnovnih greaka pri programiranju grafikih korisnikih okruenja jeste upo-
treba 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 rasporei-
vaa, 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, do-
biete 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 efi-
nisaem 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 o-
datne 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;
}
1 ///: -

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 zada-
tak postoji zato to sm o ga snimili.
MenadzerZadataka je stavljen u paket net.m indview.util da bi bio dostupan kao uslu-
na 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 zadat-
kom. Na taj nain, ako ita bude trebalo da se radi s tim zadatkom, imaete referencu na nje-
ga. 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 isporuu-
ju u standardnoj distribuciji Jave). To su SwingWorker (nai ete je na Sunovoj Web lo-
kaciji) 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 oba-
vestiti 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 argu-
m 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 od-
redbu catch gde se m onitor gasi m etod om cIo se( ).
Ostatak koda je uglavnom isti kao pre, sem to se pravljenje objekta tipa ProgressMo-
nitor 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 sva-
kom prosleuje broj pauza. U m etodi m a in ( ) vidite da pauza i brojcelija imaju podrazu-
m 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 Runna-
ble, 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 tre-
ba 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 izgle-
da da pravim o m nogo niti i da se sve m oraju prebrojiti. M oda vam izgleda da tim e kri-
m 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 Swin-
govim 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 ta-
ke (,,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 ka-
rakteristika (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 programira-
nja, ali kada piete aplikaciju, najee se trudite da kom ponente rade tano on o to vi ho-
ete. 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 ova-
kvog 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 progra-
m erskim alatkama kom ponente su predstavljene vizuelno, to ima sm isla poto one obi-
no 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 o-
nente, a taj kod prouzrokuje pravljenje kom ponente u izvrnom programu.
Sam o prevlaenje kom ponente u obrazac obino nije dovoljno za kom pletiranje pro-
grama. 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 doga-
ajim 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 na-
piete 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 korisni-
ka 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 predsta-
vljen u obliku klase. Kljuno pitanjeje m ogunost alatke za pravljenje aplikacija da otkri-
va 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 o-
m ou zrna (engl. Java BcansJ, 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 argu-
m 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 jav-
ne.
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 removeBounce-
Listener(BounceListener) obrauje se dogaaj BounceEvent. Najee e ugrae-
ni 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, svoj-
stvo jum per pokazuje da im e svojstva nije uslov za korienje tano odreenog identifi-
katora 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 e-
toda cro a k ( ) i dalje deo zrna sam o zato to je javna, a ne zato to potuje pravilo dodelji-
vanja 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 iz-
vorn om kodu, izdvoji sve neop h od n e inform acije kako bi napravila spisak svojstava i blo-
kove 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 ko-
rienja 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 po-
dravanje 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 pra-
vljenje 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 e-
utim , 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 " + metodaUpisivanja);
print("====================");
1
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 11 + 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 svoj-
stvim 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 o-
vezan 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 pri-
jem 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 nezavi-
sni. 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 e-
toda 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 dogaa-
jem , 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 pri-
kazuje kada se pritisne taster mia. Objekat BangBean takode ima sopstvene m etode ad-
dActionListener( ) 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() { return fontSize; }
public void setFontSize(int newSize) {
fontSize = newSize;
}
public Color getTextColor() { return tColor; }
public void setTextColor(Color newColor) {
tColor = 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 alat-
ka za pravljenje aplikacija m oe da uita sve inform acije o zrnu p om ou serijalizacije, na-
kon 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 pro-
jektovali.
U potpisu m etode addA ctionListener( ) vidite da ona m oe da generie izuzetak To-
oManyListenersException. To znai da je dogaaj jcdnosm eran, tj. da se sam o jedan pri-
jem 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 java.awt.*;
import java.awt.event.*;
import java.uti1 .*;
import 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, po-
vezujui 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 naj-
vei 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 gor-
njem 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 vre-
m 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 ora-
te 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 pri-
m 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 java.awt.*;
import java.awt.event.*;
import java.util.*;
import java.io.*;
import 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 eu-
tim , uoite da se u m etodam a addActionListener( ) i rem oveActionListener( ) prijem-
nici 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 e-
toda prolazi kroz listu prijemnika. Da bi se taj problem ublaio, spisak je unutar sinhro-
nizovanog 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 original-
nim spiskom prijemnika, a da se pri tom ne utie na tnetodu notifyListeners( ).
N i m etoda paintComponent( ) nije sinhronizovana. Odiuiti da li treba sinhro-
nizovati 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 oba-
vlja 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 sinhroni-
zovati. U ovom sluaju, m eutim , m oete da se zapitate: ta se najgore m oe d o-
goditi 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 po-
nent( ) 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 (cSi-
ze) koje m odifikuje sinhronizovana m etoda, a njem u pristupa m etoda paintCom-
p 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 os-
novnoj klasi, ona nepostajc autom atski sinhronizovana u redefinisanoj verziji izve-
dene 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 BangBe-
anTest, 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 tek-
stualna 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 pa-
nije Sun, iznosi 1.0. Drugi red (prazni redovi se zanemaruju) navodi datoteku Bang-
Bean.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 dato-
teke m ora da sadri informacije o paketu. O sim toga, m anifest datoteku m orate da sm e-
stite 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 ma-


nifest datoteka zove BangBean.mf.
M oda se pitate ta se deava sa svim drugim klasama koje se generiu prilikom pre-
voenja programa BangBean.java. O n e zavravaju u poddirektorijum u bangbean, a pri-
m 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 ko-
dom programa BangBean.java; vi ete m oda odluiti da izvorni koti ne pakujete zajed-
no 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 sop-
stvenu 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 dato-
teku, 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 napra-
vili 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 pod-
direktorijume.
Kada pravilno sm estite zrno u arhivu, m oete da ga unesete u alatku za izradu progra-
ma koja podrava zrna. Nain na koji se to radi razlikuje se od alatke do alatke, ali kom-
panija 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 okru-
enje 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 de-
talja saznaete na lokaciji java.sun.com .
Svojstva zrna m ogu da se poboljaju. U prethodnim prim erim a prikazana su sam o p o-
jedinana svojstva, ali je m ogue predstaviti i vie svojstava u obliku niza. To se zove indek-
sirano svojstvo. Vi obezbeujete sam o odgovarajue m etode (naravno, potujui pravila za
dodeljivanje im ena m etodam a), a objekat klase Introspector prepoznaje indeksirano svoj-
stvo 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 po-
tom 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 Pro-
pertyVetoException 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, auto-
m 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 Feature-
D 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 Stan-
dard 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 obeava-
lo, 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 kan-
didat 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 uo-
biajenih 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 iz-
gledati i ponaati se jednako na svim sistem im a i p latform am a.10
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 je-
zik za pisanje skriptova, slini program skim m odelim a HTML i JavaScript, i robusne bi-
blioteke kom ponenata. Za deklarisanje kontrolnih objekata i upravljanja rasporedom
kom ponenata koristi se sintaksa MXML, a dinam iki skriptovi se upotrebljavaju za do-
davanje 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 Fla-
shovog 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/mxml1111
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 kodi-
ranja; 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 raspo-
redom kontrola u kontejneru. U najjednostavnijem sluaju, kao u gornjem prim eru, Ap-
plication 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 o-
drava 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>
///: -

11 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 une-
sene 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 Text-
Input vidite da je atribut change povezan s funkcijom updateO utput( ) - dakle, ta funk-
cija 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 datote-
kom 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 po-
kree s kom andne linije.
Prva opcija, prevoenje na W ebu u vrem e izvravanja, sem Flexa zahteva i neki konte-
jner 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 do-
data 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 i-
taem . Flex e prevesti aplikaciju nakon prvog takvog zahteva, slino kao u m odelu Javi-
nih 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 prevo-
dioca m x m lc na kom andnoj liniji. Njih m oete upotrebiti kako god elite. Izvrna dato-
teka 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 e-
stiti uz HTM L na neki HTTP server. (N akon uitavanja Flasha u ita Weba, esto je do-
voljno 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 funk-


cije 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 posto-
ji 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 po-
trebite MXML. Dakle, MXML nije neophodan, ali je pogodan. MXML se ob in o upotre-
bljava 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 Ac-
tionScript klasama. Postojee MXML kontejnere i kontrole m oete da kom binujete u no-
vom 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 raspore-
dom i niz kontrola koje idu 11 te kontejnere. M eu kontejnerim a su panoi, vertikalni i ho-
rizontalni 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, kli-
zai, kalenari, tabele podataka itd.
Sada em o napraviti Flex aplikaciju koja prikazuje i ureuje listu audio datoteka. Vi-
d 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 sofistici-
ranije 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, 'Greka')">
<mx:method name="dajPesme"/>
</mx:RemoteObject>
</mx:Application>
///:-

Kontejner tipa DataGrid sadri ugneene oznake za svoj niz kolona. Svaki atribut ili
ugneeni elem ent na kontroli odgovara nekom svojstvu, dogadaju ili kapsuliranom ob-
jektu pripadne ActionScript klase. Taj DataGrid ima atribut id ija je vrednost tabelaPe-
sama, 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 dato-
teka. 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 ito-
vanja u realnom vrem enu na zahtev.
Flash plejer sadri ugraene kodeke za reprodukciju i em itovanje audio i video dato-
teka 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 iz-
raajne 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 di-
nam 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 odulira-


jui alfa kanali. Pored ugraenih efekata, Flex podrava i Flashov API za crtanje zaista in-
ovativnih 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 stan-
dardne stilove. Ako MXML datoteci p ridruite CSS datoteku, Fiex kontrole e se pridra-
vati 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 upotre-
bljava 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 na-
jrazliitijih kontrola koje podravaju m notvo dogaaja za sve aspekte kretanja mia i ko-
rienja 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 e-
OsveziPesme 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 re-
ferencira RemoteObject (udaljeni objekat) koji odgovara toj Java metodi.

Povezivanje s Javom
Oznaka RemoteObject na kraju MXML datoteke uspostavlja vezu sa spoljnom Java kla-
som, 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 RemoteOb-
ject im a atribut source koji naznauje Java klasu oznake RemoteObject, i specificira Ac-
tionScript 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 dostup-
n om ostatku Flex aplikacije.
U Flexu se svi pozivi usluga vraaju asinhrono, preko dogaaja koji p rouzrokuju (iza-
zivaju) 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 Usluga-
Pesama:

//: 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.rezul-
tat) 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 dekla-
raciji 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 po-
vezuje 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 referen-
cira, bez ikakve intervencije program era. Infrastruktura Flexa tano odgovara na dogaa-
je 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 vitia-
stih 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 auto-
matski se aurira. Na taj nain, program er ne m ora da obrauje dogaaje prom ena vred-
nosti elem enta Slider da bi aurirao tekstualno polje.
Ima i sofisticiranijih kontrola, kao to su Tree (stablo) i DataGrid u aplikaciji biblio-
teke pesama. Te kontrole imaju svojstvo dataprovider koje olakava povezivanje s kolek-
cijama podataka. A ctionScript funkcija on P esm e() prikazuje kako je m etoda
UslugaPesam a.dajPesm ef) povezana sa svojstvom dataprovider Flexovog elementa Da-
taG 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 pro-
gram 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 po-
troaa podataka, im e se olakava korienje obrasca model-prikaz-kontroler (Model-
View-Controller, MVC). U veim, sofisticiranijim aplikacijama, poetno poveanje sloe-
nosti zbog um etanja m odela esto se isplati je r se dobija MVC aplikacija s jasno razdvo-
jenim 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. Pri-
stupanje 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 an-
dnoj 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 i-
tau 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 al-
bum 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 u-
nicirate 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 ko-
m andne linije:

jrun -start default

Proverite da li je server uspeno p o k ren ut tako to ete u itau Weba otvoriti http://lo-
calhost: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 posredst-
vom 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 adre-
se 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 pake-
tom . Ako koristite server JRun, datoteke treba sm estiti u direktorijum e jrun4/servers/de-
fauIt/flex/W EB-INF/cIasses/gui/flex/Pesma.class i jrun4/servers/default/flex/W EB-
INF/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 ap-
likacije 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 tre-
ba da se nalazi u direktorijum u jrun4/servers/default/flex/W EB-INF/flex/flex-con-
fig.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 znako-
ve koji od donjeg elem enta prave komentar.
Izriito preporuujem o da u kodu koji se isporuuje ne dozvolite pristupanje svim iz-
vornim 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 poda-
taka.
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 ap-
likaciju.
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 ne-
ma. D obija se aplikacija koja korisniku lii na m atinu, a esto je i znatno bra od ekvi-
valentnog Swing program a. Pored toga, SWT-ov program ski m odel je jednostavniji od
Swingovog, to je poeljno u veini prim en a.13
Poto SWT upoljava m atini OS koliko god je to mogue, on autom atski moe da is-
koristi 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 pri-
kazuju 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 vi-
dite 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 doku-
mentaciji 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 Eclip-
se. 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. Da-
toteku 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 biblio-
teku 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 si-
stemska prom enljiva PATH, ali stvarnu vrednost java.Iibrary.path moete saznati po-
m 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;
}
1 ///:-
Ukoliko preuzm ete izvorni ko ove knjige, videete da kom entarska direktiva ,,Requi-
res zavrava u A nt datoteci build.xm l kao preduslov p o trebno je pravljenje poddirekto-
rijum 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 pre-
tvoren 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 pri-
liku da um etnete kod za ienje. Ali to znai da je glavna nit m a in () istovrem eno i n it ko-
risnikog 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 ko-
risniko 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 po-
zvati za objekat tipa Display: asyncExec(Runnable), syncExec(Runnable) i timer-
Exec(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 re-
sursi 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 nared-
nom 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 kon-
struktoru 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 argu-
m ent pom ou kojega moete zadati proizvoljan broj direktiva u vezi sa stilom , u zavisno-
sti 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 vide-
tovog konstruktora uvek m orate proveriti koji su dozvoljeni indikatori. Neki konstrukto-
ri 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 na-
pravite 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 ap-
likaja 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 SW-
TAplikacija 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() + 11: " +
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 upotre-
bljava 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", 11\\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 nat-
klasa 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 po-
punjava 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 na-
praviti odgovor m e n ija -ili ete za svaki m eni napraviti zaseban prijem nik (to jebezbed-
nije 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 raz-
liite 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 directorvDia-
lo 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() pona-
vlja Swing prim er iz prethodnog dela poglavlja u kojem se kliza vee s trakom za prika-
zivanje 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 Crtaj-
Sinusoidu gotovo identina. U SWT-u se grafiki kontekst grafKontekst dobija iz objek-
ta 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, ina-
e 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 is-
crta kom ponentu. Taj prijem nik tipa P aintL istener prijavljuje se (registruje) u konstruk-
toru 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.in-
vokeLater( ). 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 na-
poredo 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 sloe-
no, 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 poja-
vili, 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 o-
gunosti 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 bi-
blioteci AWT Jave 1.0). M eutim , tek kada su se pojavile klase IFC/S\ving, posao je okon-
an. 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 po-
bolja 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 pri-
stup. D a bi te alatke iko uzeo u obzir, one m oraju da podravaju Javina zrna. To podra-
zum 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 po-
veate 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 re-
zultate 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 kori-
snikih okruenja. M eutim , Swing, SWT i Flash/Flex m ogu i m nogo vie od toga, jer sa-
dre 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 ob-
janjavaju i Swing i SWT.
Reenja odabranih vcbi data su u elektronskom dokumentu The Thinking in Java Annotated Solu-
tion 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. Prezen-
taciju je smislio C huck Allison, a razvila kom panija MindVievv. Radi se o m ultim edijal-
nom 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 lo-
kaciji w w w .M in dV iew .net pronai ete inform acije o rasporedu i m estu odravanja, sve-
doenja 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 ate-
rijala 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 etoda-
ma, 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 teh-
nike projektovanja, m oem o upotrebiti va projekat kao p rim er i do kraja sedmice napra-
viti 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 odra-
vanja, 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 Ja-
vi. 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 napi-
sali 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 ide-
jam a i tehnikam a za reavanje problem a.
Nastala je kao poslednje poglavlje prvog izdanja knjige M isliti na javi. Kako su se raz-
vijale 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 prezen-
tacijam 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 po-
delili na dva: ovaj i prethodno spom enuti D esigning Objects & System s ( P rojektovanje ob-
jekata 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 to-
kom 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 vre-
menskoj 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 biblioteka-
m 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 ot-
por 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 pot-
puniji 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, na-
jee 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 rau-
nare. Ovaj program za ureivanje teksta im a m nogo softverskih dodataka, od kojih su na-
jvei 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. Plat-
form 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. Pre-
poruujem 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 (Addi-
son-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 ni-
sam 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 rigi-
nalna 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 obra-
zaca 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 ko-
renim 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. Je-
dina 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 gleda-
jui ih i sprem ni su da ih izbace. (Prim etiete da se na knjizi ne nalazi peat UML.) Od-
luivao 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 za-
miljate 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 jezi-
kom 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 po-
znajete sam o nekoliko alatki za crtanje dijagram a, a Fowlerov cilj je da dobije dobar pro-
gram , 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, prijat-
no 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 ve-
ruju 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 do-
bro.) 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 eksperimen-
tisanja (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 izra-
avaju 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 pro-
gram eri i m enaderi trebalo bi da proitaju ovu knjigu pre nego to se zapute na klizav te-
ren 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: ko-
liko projekata ne sam o da je propalo, ve je propalo spektakularno. U stanovio sam da ve-
ina 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 od-
uvek 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 za-
dovoljni 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 Con-
sulting 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 razli-
itim 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 fi-
zike, 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 ma-
tem 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 opona-
ati na sve m ogue naine. (Knjiga dokum entuje, na prim er, pojavljivanje genetskih al-
goritam 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 (inter-
pretator 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 e-
sto 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 Develop-
m 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 asters Collection, Bruce Eckel, urednik (M&T Books, 1994).
Rasprodata. Zbirka radova raznih strunjaka za C + + zasnovana na njihovim prezentaci-
jam 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 naj-
bolju knjigu po izboru urednika asopisa Java D evelopers Journal i nagradu za najbolju
knjigu po izboru italaca asopisa JavaWorld. Moe se preuzeti s lokacije w w w .M ind-
View.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 a-
sopisa 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 atinotation processing isp itn i blok try block
tool, apt isp re tu ra n o shufflcd
alijas, p seu d o n im alias izg rad n ja p ro g ram a , build
a n aliza to r p ro g ram sk ih scanner a u to m a tiz o v an o
jezika prev o en je i pakovanje
au to m atizo v an o prevoenje build izuzetak exception
i pakovanje, izgradnja izuzetak to k o m izvravanja runtime exception
pro g ram a izvrilac executor
au to m a tsk o pakovanje autoboxing jed in ica za p revoenje compilation unit,
bacan je izuzetka, throvving an exception translation unit
gen erisan je izuzetka je d in i n o te stiranje unit testing
b it in d ik a to r bit flag je d n o k ra tn o single dispatching
exception handler
b lok za o b ra d u izuzetaka o tk riv an je tip a
b lo k za pozive invocation handler k asno vezivanje late binding
brava lock kliza slider
cev pipe k lju za h eiranje hash code
cu re n je m em o rije niemory leak k o m p le t alatki Abstract Window
ita klasa class browser za a p stra k tn e p rozore Toolkit, A \V T
u v an a oblast guarded rcgion k o n s tru k to r constructor
daljinsko pozivanje m etoda Rcmotc Mcthod k o n s tru k to r no-arg constructor
Invocation, R M l bez arg u m e n a ta
d a to te k a preslikana memory-tnappcd file konverzija h vatanjem capturc cotiversion
u m e m o riju k o o p e ra tiv n o v ien itn o cooperative
deljen a brava shared lock p ro g ram ira n je multithrcading
din am ik a oblast m em orije hcap k o stu r p ro g ram a , application frame\vork
d in a m i k o vezivanje dynamic binding stru k tu ra p ro g ram a
dn ev n ik , zap isnik log lak o bjekat light-weight object
dod eljiv assignablc laka tra jn o st lightweight persistence
d o k u m en tacio n a oznaka doc tag lani objekat mock object
d v o stra n i red za ekanje doublc-cndcd ijueue. leksem a token
deque lenja pro cen a lazy evaluation
d v re d n o st rvaluc literal klase class literal
ek sp licitn a konverzija casting lo kalna m e to d a tiative method
elem en t, spravica widgct m e h a n iza m brzog fail-fast
fan tom sk a referenca phantom refcrence otkazivan ja
g en erisan je izuzetka, throvving an cxception m eka referenca soft reference
b acanje izuzetka m iksin mixin
granica bound n a b ro ja n i tip enumerated type
h eiran je, tra n sfo rm isa n je hashing najd avn ije korienje least-recently-used, LRU
kljua nasledivanje inheritance
identifikacija tipa u v rem e run-tim c type nastavljanje resumption
izvravanja idcntification, RTTI natklasa superclass
id en tifik ato r hatidle n ep ro vereni izuzetak unchecked exception
im ensk i p ro sto r namcspacc nit thread
in lin inaciici l ipo\ ima run-tim c typc nit izvravanja thrcad oj cxccution
p rilik o m izvravanja information, RTTI nit za o tp re m u d ogaaja event dispatch thread
in te g risa n o razv ojn o Intcgratcd Dcvelopment o b jek at lan metnbcr object
o k ru e n je Environmcnt, IDE o b last vaenja scopc
iskljuiva brava exclusivc lock o b ra d a izuzetaka exception handling
ispis steka stack trace od re d ite targct
1176 Misliti na Javi

odseak slice skladite pool


o k ru e n je za brzo Rapid Application sk u plja s k o p ira n je m copy collector
razv ijan je aplikacija Development, RAD slaba referenca weak referetice
o p e ra to r p o m e ra n ja shift operator sp ecifik ator p ris tu p a access specifier
opseg range sp o ljn o n adovezivanje external chaining
oznak a label sp o re d an efekat side effect
o zn ak a kraja end sentinel spravica, elem ent widget
o zn ak a tip a type tag stablo tree
p a ra le ln o st, p a ra le ln i rad concurrency stan je o k o n an ja termination condition
p o istiti clean up sta n d a rd n a biblio teka Standard Template
p o d o b je k a t subobject ab lo n a Library, STL
pok aziva steka stack pointer statu s p re k in u to sti interrupted status
poslati submit s tru k tu ra p ro g ram a , application framework
p o sre d n ik i p ro g ra m middleware k o s tu r p ro g ram a
p o sre d n ik proxy sudar, su k ob collision
p o v ezan o zakljuavanje hand-over-hand locking, svojstvo property
lock coupling sv oen je nanie downcasting
poveziva linker sv oen je nanie koje uva type-safe downcast
p o v ra tn i poziv callback tip
p o v rn o ko p iran je shallow copy svo enje navie upcasting
poziv u g ra e n d ire k tn o inline call ab lon template
u kod to k p o d a ta k a stream
p ra z n o fin aln o polje blank final to rk a tuple
p re d u p re d n o preemptive tra jn i objekat persistent object
p rek id an je termination traka n ap redo v an ja progress bar
p rekJapanje o p e ra to ra operator overloading tra n sfo rm isa n je kljua, hashing
p rijem n ik listener heiran je
prilag o dljiv izgled pluggable look & feel uitava klasa class loader
i p o n a a n je ugaen terminated
p riru n i savet tool tip u lan av an je unazad backward chaining
p ro d u a v an je zero extension u n u tra n ja klasa inner class
uz d o d av an je n ula u p o red iv comparable
p ro d u av an je sign extension uslov za trk u race condition
uz o u v anje znaka uzajam na blokada deadlock
profajler profiler u z ajam n o iskljuiva brava m utex
p ro g ram za u itav anje loader u z ajam n o iskljuivanje m utual exclusion
p ro g ra m ira n je client-side programming uzro k cause
s klijentske stra n e v a n re d n o stan je exceptional condition
p ro g ra m ira n je server-side programming vezivanje binding
sa serverske stra n e vezivanje p rilik om runtimc binding
p s e u d o n im , alijas alias izvravanja
p u ta n ja klasa classpath vezivna funkcija stub
ra n o vczivanje early binding v isoko s p re g n u t highly coupled
raspo rediv a layout manager v iek ratn o o tk riv an je tipa m u Itiple dispa tclii ilg
red za ekanje queue vieprogram ski multitasking
red za ekanie d o g a aja eventqueue v rem en sk a o zn ak a time stamp
r c d e f in is a n je overriding v ren ien .sk o o d la g a n je timcom
rukovalac d o g ad ajem event handler zaklj uak closure
saku plja sm ea garbage collector zapisn ik, d n ev n ik log
serv isn a nit daemon thread zatitn a b arijera firewaU
sistem k o jim up ravljaju event-driven system zau zeto st ekanjem busy waiting
dogadaji zrn a Jave Java Beans

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 @TestObjectCreate, >
za @Unit, 869
!>75 >, 73
@throws, 61
!=, 73 >=, 73
@Unit, 864
,8 0
upotreba, 864
= ,8 1
& @version, 60
&, 79
&&, 75 A
&=, 80 f abecedno uredivanje, 325
[], operator indeksiranja,
abstract (apstraktna)
145
klasa, 239
nasleivanje od
.NET, 39 A apstraktnih klasa, 240
.new, sintaksa, 270
A, 80 rezervisana re, 240
.this, sintaksa, 270 u odnosu na interfejs, 253
^ = ,8 0
AbstractButton, 1069
@ AbstractSequentialList, 684
I AbstractSet, 629
@, simbol za anotacije, 845
@author, 60 I,79 ActionEvent, 1091, 1130
l= , 80 ActionListener, 1055
@Deprecated, anotacija, 845
@deprecated, Javadoc II,75 ActionScript, za Adobe Flex,
oznaka, 62 1139
@docRoot, 60 Adapter, projektni obrazac,
+
250,257, 338,495,582,
@inheritDoc, 60 +,71
@interface, i rezervisana re 585,631
konverzija u tip String adapteri prijemnika, 1065
extends, 854 operatorom +, 67, 85,
@link, 60 adapterska metoda, 338
393 a d d ( ), m etoda klase
@Override, 845
@param, 61 ArrayList, 303
< addActionListener( ), 1128,
@Retention, 846
@return, 61 1134
< ,73
addChangeListener, 1094
@see, 60 <=, 73
@since, 61 addListener, 1060
, 80
Adler32, 777
@SuppressWarnings, 845 = , 81
@Target, 846 Adobe Flex, 1138
<''l'est, 8 16
Adobe, proizvoa sistema
Flex, 1138
@Test, za @Unit, 864
@TestObjectCleanup, ==,73 agentski orijentisano
program iranje, 1042
oznaka za alatku @Unit,
872 agregacija, 21
1178 Misliti na Javi

aktivni objekti, argum enti promenljive biblioteka


u paralelnom duine, 150, 578 autor u odnosu na
izvravanju, 1039 i generike metode, 499 program era klijenta,
alatka za prikazivanje, Arnold, Ken, 1046 159
za Swing, 1050 ArrayBlockingQueue, 971 projekat, 159
Allison, Chuck, 3,11, 1166, ArrayList, lista, 311, 644 upotreba, 159
1174 a d d ( ), metoda, 303 binarni
allocate(), 755, 797-798 get( ), m etoda, 303 brojevi, 78
allocateD irect(), 755 size( ), metoda, 303 brojevi, ispisivanje, 84
angaovanje, Teorija Arrays operatori, 80
rastueg, 915 asL ist(), 307, 340, 648 binarySearch( ), 624, 706
anonim na unutranja klasa, binarySearch( ), 624 BitSet, 716
275,721, 1053 usluna m etoda za Bloch, Joshua, 130,805,914,
generika, 508 pretvaranje sekvence 929
i kod upravljan tabelom, ili niza u kontejner, BlockingQueue, 971, 988
684 616 blok za obradu izuzetka, 348
anotacija, 845 asC harB uffer(), 757 blok za pozive, 466
apt, alatka za obradu, 857 asocijativan niz, 302, 306 blokiranje i metoda
dozvoljeni tipovi drugo ime za mapu, 661 available(), 740
elemenata, 849 aspektno orijentisano u program im a za
elementi, 847 program iranje (AOP), paralelno izvravanje,
marker anotacija, 847 565 887
podrazumevana vrednost, assert, i @Unit, 867 Booch, Grady, 1172
853 Atomiclnteger, 932 boolean, 98
podrazum evanevrednosti AtomicLong, 932 algebra, 79
elemenata, 847-848, AtomicReference, 932 i eksplicitna konverzija
850 atomska operacija, 926 tipova, 87
procesor, 848 autom atska konverzija operatori koji ne rade
procesor koji radi na tipova, 181 s logikim
osnovu refleksije, 854 autom atsko pakovanje, 326, argum entim a, 73
apstrakcija, 15 495 u odnosu na C i C++, 75
apt, alatka za obradu i generiki tipovi, 497, Borlandov Delphi, 1120
anotacija, 857 549 BoxLayout, 1059
argum ent available( ), 740 Brajanovo pravilo
finalni, 204, 722 sinhronizacije, 922
konstruktor, 116 brave
kovarijantni tipovi
B eksplicitne, u paralelnom
argumenata, 559 bafer, nio, 754 izvravanju, 923
liste promenljivih BASIC, Microsoftov Visual optimistiko
parametara BASIC, 1120 zakljuavanje, 1034
( n e p o z n a t b r o j i tip
BasicArro\vButton, 1070 takmienje za, u
argum enata), 150 Beanlnfo, namenska klasa, paralelnom
zakljuivanje o tipovima 1137 izvravanju, 1019
generikih Beck, Kent, 1171 u paralelnom izvravanju,
argumenata, 497 bezuslovni skok, 106 922
Indeks 1179

break, rezervisana re, 107 cev, 731, 745 ClassCastException, 237,445


brisanje, 551 i ulazno/izlazne operacije, ClassNotFoundException,
u generikom kodu, 512 976 450
brojanje referenci, Chain of Responsibility, classpath, sistemska
sakupljanje smea, 133 projektni obrazac, 826 promenljiva, 163
brojevi, binarni, 78 CharArrayReader, 735 clea r(), nio, 756
Budd, Timothy, 16 CharArrayWriter, 735 clo se(), 738
BufferedlnputStream, 733 CharBuffer, 757 collection, 332, 705
BufferedOutputStream, 734 CharSequence, interfejs, 414 Collections, klasa
BufferedReader, 377, 736, Charset, 758 en u m eratio n (), 714
738 checkedCollection(), 562 fill(), 629
BufferedVVriter, 736, 740 CheckedlnputStream, 775 m etoda addAll( ), 307
ButtonGroup, 1070, 1079 checkedList(), 562 unmodifiableList( ), 648
ByteArrayInputStream, 731 checkedM ap(), 562 Com mand, projektni
ByteArrayOutputStream, CheckedOutputStream, 775 obrazac, 295,473, 822,
732 checkedSet( ), 562 893
ByteBuffer, 754 checkedSortedM ap(), 562 Comparable, interfejs, 619,
checkedSortedSet(), 562 653, 658
c Checksum, klase, 777
Chiba, Shigeru, Dr., 881,883
Comparator, interfejs, 621
compareTo( ), u paketu
C#, programski jezik, 39 Class, 1071 java.lang.Comparable,
C++, 73 forName( ),436, 1063 619,655
obrada izuzetaka, 384 generike reference klasa, ConcurrentHashM ap, 664,
Standardna biblioteka 441 1027, 1032
ablona (Standard getCanonicalNamef ),438 ConcurrentI.inkedQueue,
Template Library, getClass( ), 357 1027
STL), 718 getConstructors( ), 464 ConcurrentM odification-
abloni, 485, 513 getlnterfaces( ), 438 Exception, 709
CachedThreadPool, 894 getMethods( ), 464 izbegavanje pom ou klase
Callable, paralelno getSimpleName( ), 438 CopyOnWriteArray-
izvravanje, 896 getSuperclass(), 438 List, 1027, 1041
case, naredba, 112 isAssignableFrom( ),454 Condition, klasa,
CASF._INSENSITIVE_ islnstance(), 453 u paralelnom
ORDER, String islnterface( ), 438 izvravanju, 968
Com parator, 705, 720 newlnstance( ),438 continue, rezervisana re,
cast( ),444 objekat tipa Class, 434, 107
catch 795, 922 Coplien, Jim
hvatanje izuzetka, 348 postupak pravljenja ablonski obrazac koji se
hvatanje bilo kog izuzetka, objekata, 142 neobino ponavlja,
356 reference, i dokeri, 442 556
iv/'iTvisanu re, 348 reterence, i ogranienja, CopyOnWriteArrayList,
cepanje rei, u paralelnom 442 1003, 1027
izvravanju, 926 RTTI pomou objekta CopyOnWriteArraySet,
cepanje, cepanje rei, 926 tipa Class, 434 1027
1180 Misliti na Javi

CountDownLatch, za gubitak podataka izlazne prom ena ponaanja


paralelno izvravanje, datoteke, greke i pom ou kompozicije,
983 pranjenje bafera, 741 234
CRC32, 777 JARarhiva, 161 provera tipova u Javi, 647
crtanje na panou u Swingu, obeleja, 728 sintaksa za skupnu
1093 preslikana u mem oriju, inicijalizaciju nizova,
crtanje u Swingu, 1092 769 597
CSS (kaskadni opisi stilova), zakljuavanje, 772 vezivanje, 211,214
i Adobe Flex, 1144 datoteka klase, analiza, 879 direktorijum
CyclicBarrier, za paralelno decode( ), ema kodiranja, i paketi, 168
izvravanje, 986 759 listanje, 719
Decorator, projektni pravljenje direktorijuma
obrazac, 568 i putanja, 728
default, rezervisana re disjunkcija, 86
ista supstitucija, 25, 235 u naredbi switch, 112 (II), 75
ienje defauItR eadO bject(), 792 dispose( ), 1096
i skuplja smea, 191 defaultW riteObject( ), 792 dodela objekata, 67
izvravanje, 131 D eflaterOutputStream , 775 dodela vrednosti, 67
provera stanja okonanja Delayed, 990 dogaaj
metodom finalize(), DelayQueue, za paralelno dogaaji i prijemnici,
131 izvravanje, 988 1059-1060
uz finally, 368 delegiranje, 188, 568 dogaaji i zrna Jave, 1121,
ita klasa, 175 Delphi, kompanije Borland, 1130
itanje standardnog ulaznog 1120 model, Swing, 1059
toka, 749 deljenje, 69 odgovor na Svving
lan DeMarco, Tom, 1173 dogaaj, 1052
funkcija lanica, 19 deque, dvostrani red program iranje upravljano
inicijalizatori, 225 za ekanje, 319, 660 dogadajima, 1052
objekat, 21 destruktor, 129, 131,368 sistem upravljan
uvana oblast, u obradi Java ga nem a, 191 dogaajima, 290
izuzetaka, 348 dijagram dokumentacija, 11
diiagrami nasledivanja kom entari i ugradena
D klasa, 199 dokumentacija, 57
nasleivanje, 28 dostavlja poruka, idiom,
DatagramChannel, 773
dijalog 487, 632, 684
D atalnput, 737
okvir za, 1095 dostini objekti i skupljanje
D atalnputStream, 733, 736,
okno s jezicima, 1083 smea, 710
739
za rad s datotekam a, 1099 double
D ataOutput, 737
Dijkstra, F.dsger, 978 i vienitni rad, 926
DataOutputStream, 734, 737
dim, m arker (d ili D ' literala,
datoteka
dinamika 78
dijalozi, 1099
bezbednost tipova do-whi!e, 101
File, klasa, 719, 731,737
i kontejneri, 562 drugi komplement, 84
File.list( ), 719
posrednik, 466
Indeks 1181

dugme EnumSet, 505, 718 FIFO (prvi izlazi onaj koji je


pravljenje sopstvenog, um esto indikatora, 819 prvi uao), 329
1066 equals( ), 73 FileChannel, 754
radio-dugm e, 1078 i hashCode( ), 653,679 FileDescriptor, 731
Swing, 1051, 1069 i strukture podataka FilelnputReader, 738
dvokratno otkrivanje tipa, s transform isanim FilelnputStream, 731
836 kljuevima, 671 FileLock, 773
pom ou mape EnumMap, redefinisanje za HashMap, FilenameFilter, 719
842 670 FileNotFoundException,
dvostrani red za ekanje uslovi za pravilno 378
(deque), 319 definisanje, 670 FileOutputStream, 732
dvrednost, 67 Erlang, programski jezik, FileReader, 377, 735
888 FileWriter, 735, 740
EventSetDescriptor, 1126 fillInStackTrace( ), 359
D
Exchanger, klasa, filozofi, koji veeraju,
dokeri u paralelnom prim er uzajamne
i Class reference, 442. izvravanju, 1001 blokade u paralelnom
nadtipova, 539 Executor (izvrilac), izvravanju, 978
neogranieni, 541 paralelno izvravanje, FilterlnputStream, 731
u generikim tipovima, 893 FilterOutputStream, 732
534 ExecutorService, 893 FilterReader, 736
extends, rezervisana re, FilterWriter, 736
E 172, 185,235 final, 488
East, BorderLayout, 1056 i @interface, 854 final (finalno), 243
efikasnost i interfejs, 254 argument, 204, 722
i nizovi, 593 rezervisana re, 183 i efikasnost, 207
i finalnost, 207 Externalizable, 785 i privatnost, 205
alternativa za, 791 i reference objekata, 201
eksplicitno zadavanje tipov'a
Extreme Programm ing i statinost, 201
argum enata generikih
(XP), 1171 klase, 206
metoda, 309, 499
eksponencijalni zapis, 78 metoda, 215
else, rezervisana re, 99 metode, 204, 232
podaci, 200
encode( ), ema kodiranja, Fa<;ade, 452
759 prazna finalna polja, 203
Factory Method, projektni rezervisana re, 200
endian obrazac, 262
big endian, 763 statini prosti tipovi, 202
faktor optereenja, objekata finalize( ), 129, 194, 379
little endian, 763 tipa HashMap ili
entrySet( ), metoda direktno pozivanje, 131
HashSet, 701 i nasleivanje, 226
interfejsa Map, 673 false, 75 finallv, 191, 194
enum. I 'idcli nabrojani FeatureDescriptor, 1137
tipovi i konstruktori, 377
Fibonacci, 494 i return, 371
Enum eration, 713 Field, klasa za refleksiju, 462 mana, 372
EnumMap, 821
1 182 Misliti na Javi

ne izvrava se u sluaju funkcijski objekat, 585 kontravarijansa, 539


servisnih niti, 905 funkcijski programski jezici, metode, 496, 631
rezervisana re, 367 888 najjednostavnija definicija
FixedThreadPool, 894 Future, 897 klase, 321
Flex neogranien dokerski
OpenLaszlo, alternativa argum ent, 541
za Flex, 1138
G
niz generikih objekata,
sistem kompanije Adobe, Gecov test, za izbegavanje 677
1138 sinhronizacije, 926 osnovni uvod, 303
flip( ), nio, 755 Generator, 218, 492, 500, oznaka tipa, 522
float 508, 550, 580, 606, 621, preklapanje, 553
istina i neistina u 630,813 prim er strukture, 1028
poreenju brojeva opte nam ene, 501 samoogranieni tipovi,
s pokretnim zarezom, popunjavanje kontejnera 555
76 (podtipova klase testiranje sa @Unit, 873
marker (F) literala, 78 Collection), 500 unutranje klase, 508
FlowLayout, 1057 generiki tipovi generisanje izuzetka, 347
Flyweight, projektni anonim ne unutranje get( ), m etoda klase
obrazac, 635, 1043 klase, 508 ArrayList, 303
for, rezervisana re, 101 argum enti promenljive HashMap, realizacija klase
foreach, 103, 107, 150-151, duine i generike Map, 326
167,291,305,328, 334, metode, 499 u interfejsu Collection
426,494-495, 549, 805, brisanje, 512, 551 nema m etode get( ),
827 Class reference, 441 644
i Adapterska metoda, 338 dokerski argum enti, 534 getBeanInfo( ), 1124
i interfejs Iterable, 336 dokerski argum enti getBytes( ), 740
i Iterable, 336 nadtipova, 539 getCanonicalName( ), 438
format eksplicitna konverzija, 551 getChannei( ), 755
preciznost, 402 eksplicitna konverzija getClass( ), 357, 436
prikazivanja znakovnih putem generike g etC o n stru cto r(), 1071
nizova, 400 klase, 553 getConstructors( ),464
specifikatori, 402 eksplicitno zadavanje getenv( ), 337
format( ), 400 tipova argum enata getenv( ), metoda, 337
Formatter, 401 generikih metoda, getEventSetDescriptors( ),
forName( ),436, 1063 309,499 1126
FORTRAN, programski granice, 514, 531 getlnterfaces( ), 438
jezik, 79 i kontejneri za bezbedan getM ethodDescriptors( ),
Fowler, Martin, 159, 386, rad s tipovima, 303 1126
1172 instanceof, naredba, 522, getMethods( ), 464
funkcija 551 getName( ), I 126
funkciju lanica, 19 islnstancef ^22 getPropert) 1)e.scriptoi's( j,
redefinisanje, 24 izuzeci, 563 1126
funkcija za transformisanje koji se neobino getPropertyType( ), 1126
kljueva (he funkcija), ponavljaju, 556 getReadMethod( ), 1126
675 konkretizacija, 516 getSelecteValues( ), 1081
Indeks 1183

getSim pleN am e(), 438 H sukobljavanje prilikom


getState( ), 1090 kombinovanja
Harold, Elliotte Rusty, 1137,
getSuperclass(), 438 interfejsa, 255
1171
getW riteM ethod(), 1126 sukobi, 165
XOM, XML biblioteka,
Glass, Robert, 1172 im e klase, otkrivanje u
799
Goetz, Brian, 922,926,1019, datoteci klase, 879
h ash C od e(), 663, 668, 670,
1044 imenski prostori, 160
675
goto, nepostojanje te implements, rezervisana re,
recept za pisanje pristojne
naredbe u Javi, 108 243
metode, 679
grafiko korisniko im port, rezervisana re, 160
hash C od e()
okruenje (GUI), 290, indeksiranje, operator [], 145
eq u als(), 653
1045 indeksirano svojstvo, 1137
i strukture podataka
grafika, 1098 indexO f(), metoda klase
s transform isanim
Graphics, klasa, 1093 String, 464
kljuevima, 671
granice indikator, korienje skupa
na ta obratiti panju
i Class reference, 442 EnumSet umesto, 819
prilikom pisanja
natklasa i Class reference, InflaterlnputStream, 775
sopstvene metode,
444 inicijalizacija
678
samoogranieni generiki lana klase, 181
HashMap, 664, 700, 1032,
tipovi, 555 i nasledivanje, 208
1068
u generikim tipovima, i uitavanje klasa, 208
HashSet, realizacija skupa
514, 531 inicijalizacija instance,
Set, 322,653, 696
greka 144,277
Hashtable, 700, 714
obrada pom ou izuzetaka, inicijalizacija
hasN ex t(), m etoda klase
345 konstruktorim a
Iterator, 316
oporavak, 345 tokom nasleivanja
heksadecimalno, 78
prijavljivanje, 385 i kompozicije, 189
Holub, Allen, 1039
prijavljivanje greaka inicijalizacija nestatinih
HTML u Swing
u knjizi, 14 instanci, 144
kom ponentam a, 1101
standardni izlazni tok inicijalizacija niza, 145
za greke, 350 inicijalizacija pomou
GridBagLayout, 1058 I konstruktora, 115
GridLayout, 1058, 1119 IdentityHashM ap, 664, 700 inijalizatori lanova, 225
Grindstaff, Chris, 1150 if-else naredba, 84, 99 klase, 440
grupa niti, 914 ikonice i klasa Icon, 1071 lenja, 182
grupa objekata, 997 IlIegalAccessException, 449 osnovna klasa, 185
grupe, regularnog izraza, 417 IllegalMonitorState- polja klase, 136
GUI Exception, 958 promenljivih u
grafiki korisniki ima, relacija, 21 metodama, 136
interfejs, 290, 1045 relacija kompozicije, 197 redosled inicijalizacije,
razvojna okruenja za Imagelcon, 1071 139,232
GUI, 1046 ime statinih, 209
GZIPInputStream, 775 pravljenje jedinstvenih inicijalizovanje izvedene
GZIPOutputStream , 775 iinena paketa, 163 klase, 185
puno, 438 InputStream, 730
1184 Misliti na Javi

InputStreamReader, 735 ugneivanje interfejsa FileNotFoundException,


instanca unutar klasa i drugih 378
inicijalizacija instance, interfejsa, 260 fillInStackTrace( ), 359
277 za objekat, 17 finally, 367
inicijalizacija nestatinih zajedniki interfejs, 239 generiki kod, 563
instanci, 144 internacionalizacija, u U/I generisanje izuzetka, 346,
klase, 16 biblioteci, 735 347
instanceof, 451 interrupt( ) gubljenje izuzetka, mana,
dinamiko ispitivanje tipa paralelno izvravanje, 946 372
objekta m etodom vienitni rad, 912 hijerarhije klasa, 382
isln stan ce() umesto Introspector, 1124 hvatanje bilo kog izuzetka,
sa instanceof, 453 inenjering bajtkoda, 879 356
i generiki tipovi, 551 Javassist, 881 hvatanje izuzetka, 348
rezervisana re, 445 isAssignableFrom( ), i konstruktori, 376
Integer m etoda klase Class, 454 i konzola, 387
parselnt( ), 1099 iscrpljenje memorije, i nasleivanje, 374, 382
omotaka klasa, 148 reenje pom ou i paralelnost, 924
interfejs referenci, 710 konstruktori, 377
i enum , 815 isDaemon( ), 903 menjanje mesta nastanka
i generiki kod, 484 islnstance(), 453 izuzetka, 360
i nasleivanje, 253 i generiki tipovi, 522 neprovereni, 366
i razdvajanje od islnterface( ),438 NullPointerException, 366
realizacije, 20,174, Iterable obrada, 33
1060 i foreach, 336 obrada izuzetaka, 345
inicijalizacija polja u Iterable, interfejs, 494, 632 ogranienja, 374
interfejsima, 259 i foreach sintaksa, 336 ponovno generisanje
interfejs osnovne klase, i niz, 337 izuzetka, 359
218 Iterator, 332 pravljenje sopstvenih, 349
klase ugnedene unutar, Iterator, klasa, 315, 318, 332 prekidanje ili nastavljanje,
283 hasNext( ), metoda, 316 349
privatni, kao ugneeni next( ), metoda, 316 pretvaranje proverenih
interfejsi, 261 Iterator, projektni obrazac, u neproverene
rezervisana re interface, 269 izuzetke, 388
242 izgled i ponaanje, prijavljivanje izuzetaka
sukobljavanje imena promenljiv, 1103 putem zapisnika, 353
prilikom izuzetak printStackTrace( ), 359
kombinovanja blok try, 348 problemi pri
interfejsa, 255 blok za obradu, 346 projektovanju, 379
svoenje navie na blok za obradu izuzetka, pronalaenje slinih
interfejs, 245 348 izuzetak.i, 382
u odnosu na apstraktnu uvana oblast, 348 proveren, 356, 384
klasu, 253 Error, klasa, 365 RuntimeException, 366
u odnosu na realizaciju, Exception, klasa, 365 specifikacija, 355, 385
196 Throwable, klasa, 356
Indeks 1185

tipine upotrebe javac, 57 JMenuBar, 1085,1091


izuzetaka, 390 javadoc, 58 JMenuItem, 1071,1085,
try, 368 javap, prevodilac unazad, 1090,1092
ulanani izuzeci, 388 393,479, 520 JNLP, Java Netvvork Launch
ulanavanje, 362 Javassist, 881 Protocol, 1105
vanredno stanje, 346 Javina standardna biblioteka, jo in ( ), vienitni rad, 912
zapisivanje, 352 i bezbedno vienitno JOptionPane, 1083
izvedena klasa, 214 izvravanje, 985 Joy, Bill, 73
izvedeni tipovi, 22 JButton, 1071 JPanel, 1070,1093,1119
izvorni kod, 12 Swing, 1051 JPopupMenu, 1091
napom ena o zatiti JCheckBox, 1071,1077 JProgressBar, 1103
autorskih prava, 12 JCheckBoxMenuItem, 1086, JRadioButton, 1071,1079
izvravanje Java programa, 1090 JScrollPane, 1055,1082
56 JComboBox, 1080 JSlider, 1103
izvravanje program a JComponent, 1073, 1093 JTabbedPane, 1083
operativnog sistema IDialog, 1096 JTextArea, 1054
iz Jave, 752 meniji, 1085 JTextField, 1052, 1073
JDK 1.1 U/I tokovi, 734 JTextPane, 1076
JDK, preuzimanje IToggleButton, 1070
J
i instaliranje, 56 JUnit, problem i sa, 864
Jacobsen, Ivar, 1172 je, relacija, 235 JVM (Javina virtuelna
JApplet, 1056 i svoenje navie, 198 maina), 434
meniji, 1085 relacija nasleivanja, 197
JAR, 1135 u odnosu na relacije
arhiva, 161 je-kao, 25
K
jar arhive i promenljiva je-kao, relacija, 235 kanal, nio, 754
classpath, 164 jedinica za prevodenje, 161 kapacitet, objekata tipa
usluni program, 779 jednakost HashMap ili HashSet,
Java 700
==, 73
AWT, 1045 jednakost objekata, 73 kapsuliranje, 174
bajtkod, 394 jednokratno otkrivanje tipa, upotreba refleksije za
i aparati za prikazivanje 836 ponitavanje, 476
Interneta na TV-u, 79, jednosm erni (dogaaj), kaskadni opisi stilova (CSS),
394 1130 i Adobe Flex, 1144
Java Web Start, 1105 JFC, Javine osnovne klase kasno vezivanje, 27, 211, 214
Javina virtuelna maina (Swing), 1045 keySet( ), 700
(JVM), 434 JFileChooser, 1099 kidanje veze, putem
Javine osnovne klase JFrame, 1056 polimorfizma, 27, 211
(JFC/Svving), 1045 meniji, 1085 klasa, 17
iavni seminari o Javi, 9 iri', prevodioci anonim na unutrania
prevoenje i izvravanje ba-kad-treba, 135 klasa, 275, 721, 1053
program a, 56 JLabel, 1075 apstraktna klasa, 239
JavaBeans, videti zrna Jave, JList, 1081 autori, 20
1120
JMenu, 1085,1090 ita, 175
1186 Misliti na Javi

dijagrami nasleivanja, ugnedivanje unutar kod upravljan tabelom, 823


199 interfejsa, 283 i anonim ne unutranje
ekvivalentnost, unutranja klasa, 266 klase, 684
i instanceof/ unutranja klasa i prava kolekcija, 29, 306, 332
isln stan ce(), 459 pristupa, 268 klase, 302
finalne klase, 206 unutranja klasa popunjavanje pomou
hijerarhije klasa i obrada i svoenje navie, 271 Generatora, 500
izuzetaka, 382 unutranja klasa i Svving, spisak metoda za, 643
inicijalizacija, 440 1060 uslune metode, 701
inicijalizacija lanova, 181 unutranja klasa, kolekcije, parametar, 565,
inicijalizacija i uitavanje i redefinisanje, 297 589
klasa, 208 unutranja klasa, kom anda akcije, 1090
inicijalizacija polja, 136 i rezervisana re kombinovane liste, 1080
inicijalizovanje izvedene super, 297 komentari i ugraena
klase, 185 unutranja klasa, dokumentacija, 57
inicijalizovanje osnovne identifikatori i .class kompatibilnost
kJase, 185 datoteke, 300 vertikalna, 516
izvedena klasa, 214 unutranja klasa, migracijska, 516
javna klasa i jedinice u metodama komplet alatki za apstraktne
za prevoenje, 161 i oblastima vaenja, prozore (Abstract
literal klase, 439, 451 273 WindowToolkit,AWT),
metode, 53 unutranja klasa, 1045
nasleivanje od ugneivanje unutar komponenta, i zrna Jave,
apstraktnih klasa, 240 proizvoljne oblasti 1121
nasledivanje unutranje vaenja, 274 kompozicija, 21, 180
klase, 296 upuivanje na objekat i dinamika promena
osnovna klasa, 172, 183, spoljne klase iz ponaanja, 234
214 unutranje klase, 270 i projektovanje, 233
podaci, 53 viestruko ugnedena, 284 kombinovanje
podobjekat, 185 klasa, uitava, 434 kompozicije i
povezivanje, 440 klijent, programer, 20 nasleivanja, 189
primerak, 16 kliza, 1102 u odnosu na nasleivanje,
pristup, 175 konjunkcija 196,200, 660,714
privatna unutranja klasa, nad bitovima, 86 komprimovanje, biblioteka
291 logika (&&), 75 za, 775
redosled inicijalizacije, kod komunicirajui
139 izvorni, 12 sekvencijalni procesi
rezervisana re class, 22 podela i smetanje, 168 (CSP), 1042
statine unutranje klase, ponovna upotreba, 180 konferencija, za razvoj
282 standardi programiranja, softvera, 9
stil pruvljenja klasa, 174 13 konkretizacija i generiki
uitavanje, 209,440 stil program iranja, 63 tipovi, 516
ugnedena klasa (statina kod bez zakljuavanja, konsalting i obuka koju
unutranja klasa), 282 u paralelnom prua MindVievv, Inc.,
izvravanju, 926 1166
Indeks 1187

konstanta statika odredba kostur upravljanja i


grupe konstantnih konstruktora, 143 unutranje klase, 290
vrednosti, 258 statine metode, 142 kovarijantni, 441
posredne konstante i kontejner, 29 nizovi, 535
String, 393 ispitivanje performansi, povratni tipovi, 232, 559
pri prevoenju, 200 684 tipovi argumenata, 559
uklapanje konstante, 201 klasa, 302 kritian odeljak
konstruktor, 115 poreenje s nizom, 593 i sinhronizovani blok,
argum enti, 116 kontejneri 933
bez argum enata, 116, 124 bez zakljuavanja, 1027 kvantifikator
Constructor, klasa za koji brzo otkazuju, 708 pohlepno, 413
refleksiju, 462 osnovno ponaanje, 309 posesivno, 413
i anonim ne unutranje za bezbean rad regularnog izraza, 413
klase, 275 s tipovima i generiki rezervisano, 413
i finally, 377 tipovi, 303
i obrada izuzetaka, kontravarijansa i generiki
376-377 kod, 539
L
i paralelno izvravanje, 907 kontrola pristupa, 20, 178 lak
i polim orfizam, 224 konverzija objekat, 315
i preklapanje, 117 automatska, 181 trajnost, 781
inicijalizacija instance, proirujua, 87 latentni tipovi, 572, 582
277 suavajua, 87 lani objekat, 476
inicijalizacija tokoni konverzija tipova, 28 leksikografsko ureivanje,
nasleivanja i asSubc!ass( ), 445 325
kompozicije, 189 i generiki tipovi, 551 u odnosu na abecedno
konstruktor osnovne i prosti tipovi, 98 ureivanje, 623
klase, 225 operatori, 87 length
nivo pristupa automatski putem generike klase, lan niza, 147
napravljenog 553 za nizove, 595
podrazum evanog konverzija vremenskih lenja inicijalizacija, 182
konstruktora, 464 jedinica, 990 lepak, rasporeivaa
podrazum evani, 124 konzola BoxLayout, 1059
ponaanje polim orfnih slanje izuzetaka na, 387 LIFO (poslednji koji ue,
metoda unutar Svving alatka za prvi izlazi), 320
konstruktora, 230 prikazivanje LineNumberlnputStream,
povratna vrednost, 1 17 u net.mindview.util. 733
pozivanje iz drugih SwingKonzola, 1050 LineNumberReader, 736
konstruktora, 127 kopiranje niza, 617 linija napredovanja, 1102
pozivanje konstruktora korisniki interfejs LinkedBlockingQueue, 971
osnovne klase sa grafiki korisniki interfeis LinkedHashMap, 664, 667,
argum entim a, 186 (GUI), 290, 1045 700
redosled poziva koje brzo reaguje, I.inkedHashSet, realizacija
konstruktora uz realizacija pomou skupa Set, 323, 653,
nasleivanje, 224 niti, 913 696-697
1188 Misliti na Javi

LinkedList, ulanana lista, Map, klasa, 302, 306, 326 pojava pseudonim a pri
311,319, 329,649 EnumM ap, 821 pozivanju metoda, 68
List, klasa, 302, 306, 311, iscrpno istraivanje, 661 polim orfni pozivi metoda,
649,1081 poreenje performansi, 211
poreenje performansi, 698 ponaanje polim orfnih
688 Map.Entry, 673 m etoda unutar
ureivanje i pretraivanje, MappedByteBuffer, 769 konstruktora, 230
705 m a rk (), 737 preklapanje, 117
lista m arker anotacija, 847 prim ena m etode na
grafike liste, 1081 maine stanja i nabrojani sekvencu, 577
padajua lista, 1080 tipovi, 830 privatna, 232
Lister, Timothy, 1173 matcher( ), m etoda za razlikovanje preklopljenih
Listlterator, 649 pronalaenje metoda, 119
literal regularnog izraza, 414 redefinisanje privatnih,
double, 78 matches( ), m etoda klase 221
float, 78 String, 409 rekurzivna, 398
klase, 439,451 matematiki operatori, 69, statina, 129, 215
long, 78 773 ugraivanje poziva
vrednosti, 77 M ath.random ( ), metoda metoda direktno
little endian, 763 opseg rezultata, 695 u kod, 204
logaritmi, prirodni, 79 M ath.random ( ), metoda unutranje klase
logika za generisanje sluajnih u m etodam a
konjunkcija, 86 brojeva, 326 i oblastima vaenja,
disjunkcija, 86 mehanizam za raspodelu 273
operator i nepotpuno procesorskog vremena vezivanje poziva metoda,
izraunavanje, 76 nitim a, 891 214
operatori, 75 meni zatiene metode, 198
lokalna JDialog, JApplet, JFrame, Meyer, Jeremy, 845, 879,
unutranja klasa, 274 1085 1105
promenljiva, 50 JPopupM enu, 1091 Meyers, Scott, 20
long metaanotacije, 848 Microsoftov Visual BASIC,
i vienitni rad, 926 Metapodaci, 845 1120
marker (L) literala, 78 Method, 1126 migracijska kompatibilnost,
LRU, algoritam najdavnijeg klasa za refleksiju, 462 516
korienja, 667 M ethodDescriptor, 1126 m ikroporeenje
Ivrednost, 67 metoda perform ansi, 694
alatka za pronalaenje, miksin, 565
1061 mkdirs( ), 730
M dodavanje metoda mnemonici (preice
main( ), 184 program u, 179 s tastatiiff), ! 090
manitest datoteka, za JAR finalna, 204,215,232 mnoenje, 69
datoteke, 779, 1135 generika, 496 modulo, 69
manje ili jednako (<=), 73 inicijalizacijapromenljivih m ogunost ponovnog
manje od (<), 73 u metodam a, 136 korienja, 21
Indeks 1189

monitor, za paralelno operator konjunkcije (8c), unutranjih klasa, 296


izvravanje, 922 79 viestruko nasleivanje
Mono, 39 operatori, 79 u jezicima C++ i Java,
mrea objekata, 782 nadtipovi, dokerski 251
mreni ulaz/izlaz, 753 argum enti, 539 nasleivanje viestruke
MXML, ulazni format najdavnije korieno realizacije, 287
Adobe Flexa, 1138 (algoritam, LRU), 667 nastavljanje, prekidanje ili
mxmlc, prevodilac za Adobe napom ena o zatiti nastavljanje, obrada
Flex, 1140 autorskih prava, izvorni izuzetaka, 349
kod, 12 nasum ian izbor i nabrojani
nasleivanje, 22,172,180, tipovi, 814
N
183,211 natklasa, 185
nabrojani tipovi, 155 dijagram, 28 ogranienje, 444
dodavanje metoda, 807 dijagrami nasleivanja negacija, logika (!), 75
grupe konstantnih klasa, 199 neobino ponavljanje
vrednosti u jezicima i finalize( ), 226 generiki tipovi, 556
C i C++, 258 i finalnost, 206 ablonski obrazac u jeziku
i interfejsi, 815 i generiki kod, 484 C++, 556
i maine stanja, 830 i nabrojani tipovi, 813 neogranien dokerski
i naredba svvitch, 809 i rezervisana re argum ent u generikom
i nasleivanje, 813 synchronized, 1135 kodu, 541
i nasumian izbor, 814 inicijalizacija i nepodrane metode, u Java
i projektni obrazac Chain nasleivanje, 208 kontejnerim a, 646
of Responsibility, 826 kombinovanje nepotpuno izraunavanje
i uvoz statinih lanova, kompozicije i logiki operatori, 76
806 i nasleivanja, 189 nepravilan niz, 600
i viekratno otkrivanje od apstraktnih klasa, 240 neprekidnost, u paralelnom
tipa, 836 poreenjc potpunog izvravanju, 918
metode koje se menjaju nasleivanja i nepromenljivost, 470
u zavisnosti od kon- proirivanja, 235 nepromenljivost, kako
stante (nabrojanog preklapanje m etoda kolekciju ili m apu
tipa), 823, 840 u poreenju uiniti nepromenljivom,
rezervisana re enum, 155, redefinisanjem, 194 706
805 projektovanje pomou neprovereni izuzeci, 366
values( ), 805, 810 nasleivanja, 233 pretvaranje proverenih u,
nain pisanja koda, 13 proirivanje interfejsa 388
nad bitovima pom ou nasleivanja, net.mindview.util.Swing-
disjunkcija, 86 253 Konzola, 1050
iskljuiva disjunkcija XOR proirivanje klase tokom, Neville, Sean, 1138
( a ). 80 23 new, operator, 129
konjunkcija, 86 specijalizacija, 197 i niz prostih tipova, 147
negacija ~, 80 u odnosu na kompoziciju, newlnstance( ), 1071
operator disjunkcije (I), 196, 200, 660,714 refleksija, 438
79
1 190 Misliti na Javi

next( ),metoda klase poreenje s kontejnerom, mrea objekata, 782


Iterator, 316 593 nizovi su prvorazredni
nio, 753 prostih tipova, 595 objekti, 595
bafer, 754 provera granica, 147 objektno orijentisano
i prekid, 950 sintaksa dinamike program iranje, 432
kanal, 754 agregatne pojava pseudonim a, 68
perform anse, 770 inicijalizacije, 597 postupak pravljenja, 142
nit viedimenzionalni, 599 pravljenje, 116
bezbednost, Javina vraanje, 597 serijalizovanje, 780
standardna N orth, BorderLayout, 1056 tipa Class, 434, 795, 922
biblioteka, 985 notifyA ll(), 957 w a it() i notifyAll( ), 958
grupa, 914 notifyListeners(), 1134 objekat za prenos podataka,
in te rru p t(), 946 nove U/I klase, 753 487, 632
isD aem o n (), 903 n-torka, 487, 502, 509 objekat za prenos podataka
lokalno skladite niti, 940 null, 46 (idiom Prenosilac), 684
mehanizam za raspodelu Null iterator, projektni oblast vaenja
procesorskog obrazac, 469 ugneivanje unutranje
vremena, 891 Null objekat, projektni klase unutar
notifyAIl( ), 957 obrazac, 469 proizvoljne oblasti
prioritet, 899 NullPointerException, 366 vaenja, 274
resu m e () i uzajamna unutranje klase
blokada, 946
stanja, 945
o u m etodam a
i oblastima vaenja,
s to p ( ) i uzajamna obavezno proveravanje 273
blokada, 946 statikih tipova, 384 oblik
su sp e n d () i uzajamna O bjectO utputStream , 781 primer, 22, 215
blokada, 946 objekat, 16 primer, i prepoznavanje
u odnosu na zadatak, brava, za paralelno tipa u vreme
terminologija, 911 izvravanje, 922 izvravanja, 432
wait( ), 957 lan, 21 odeljak, kritian oeljak
niz dodela objekata i sinhronizovani blok,
asojativan niz, 306 kopiranjem referenci, 933
generikih objekata, 677 67 oduzimanje, 69
inicijalizacija, 145 equals( ), 73 ograniena svojstva, 1137
kao prvorazredni objekti, finalni, 201 okno s jezicima, 1083
595 getClass( ), 436 okruenja koja brzo reaguju,
kopiranje niza, 617 hashCode( ), metoda 913
kovarijansa, 535 korenske klase Object, oktalno, 78
length, lan, 147, 595 663 okviri za poruke, u Swingu,
nepravilan, 600 interfejs za, 17 1083
nije iterabilan, 337 jenakost, 73
OOP
objekata, 595 jednakost u odnosu osnovna obeleja, 16
poreenje elemenata, 619 na jednakost osnovni koncepti, 15
poreenje nizova, 618 referenci, 73 protokol, 243
Indeks 1191

Simula-67, programski unarni, 71, 80 podrazumevani, 161, 170


jezik, 17 za eksplicitnu konverziju pravljenje jedinstvenih
zamenljivost, 17 tipova, 87 im ena paketa, 163
opcione metode, u Java zarez kao operator, 103 pakovanje, 326, 495
kontejnerima, 646 ordinal( ), za nabrojane i generiki tipovi, 497, 549
OpenLaszlo, alternativa tipove, 806 paraleino izvravanje
za Flex, 1138 OSIzvrenje, 752 aktivni objekti, 918,1039
operacija, atomska, 926 osnova, 8,16, 78 ArrayBlockingQueue, 971
operativni sistem, osnovna klasa, 172, 183,214 BlockingQueue, 971, 988
izvravanje programa apstraktna osnovna klasa, Brajanovo pravilo
iz Jave, 752 239 sinhronizacije, 922
operator oznaenog inicijalizacija, 185 cepanje rei, 926
pom eranja udesno interfejs, 218 Condition, klasa, 968
( ) , 80 konstruktor, 225 CountDownLatch, 983
operator pomeranja osnovni koncepti objektno CyclicBarrier, 986
u le v o ( ) , 80 orijentisanog DelayQueue, 988
operator prvog program iranja (OOP), eksplicitne brave, 923
komplementa, 80 15 Exchanger, 1001
operator umanjenja, 72 osnovni tipovi, 22 Executor (izvrilac), 893
operator uslovljavanja, 84 otkrivanje tipa gaenje zadataka, 942
operator uveanja, 72 dvokratno, 836 Gecov test za izbegavanje
operatori, 66 viekratno nabrojani sinhronizacije, 926
+, za String, 393 tipovi, 836 i izuzeci, 924
binarni, 80 O utputStream, 730, 732 i kontejneri, 708
este greke, 86 OutputStreamVVriter, 735 i Swing, 1110
konverzija u tip String oznaen interfejs Callable, 896
operatorom +, 67, 85 break, 108 kod bez zakljuavanja, 926
logiki, 75 continue, 108 konstruktori, 907
logiki operatori i oznaka, 108 LinkedBlockingQueue,
nepotpuno 971
izraunavanje, 76 lokalno skladite niti, 940
nad bitovima, 79
P neprekidnost, 918
operator ineksiranja [j, padajua lista, 1080 nit u odnosu na zadatak,
145 paintCom ponent( ), 1093, terminologija, 911
pomeranja, 80 1098 operacije na prostim
poreenja, 73 paket, 160 tipovima long
preklapanje, 85 i struktura direktorijuma, i double nisu
preklapanje + i += 168 neprekidne, 926
za String, 184 imena, pisanje velikim optimizacija performansi,
preklapanje operatora slovima, 52 1018
z.a String, 393 paketni i prijateljski prioritet, 899
prioriteti, 66 pristup, 169 PriorityBlockingQueue,
prvog komplementa, 80 paketni pristup i zatieni 991
ternarni, 84 lanovi, 197 proizvoa potroa, 965
1192 Misliti na Javi

proputeni signali, 961 podela i smetanje koda, 168 posrednik


ReadWriteLock, 1036 podobjekat, 185,196 i klasa java.lang.ref.Refe-
ScheduledExecutor, 994 podrazum evani rence, 648
semafor, 997 konstruktor, 124 za nepromenljive metode
servisne niti, 901 dobija isti nivo pristupa klase Collections, 648,
sle ep (), 897 kao klasa, 464 710
SynchronousQueue, 1008 pravljenje, 186 potpis, metode, 50
takmienje, za brave, 1019 podrazum evani paket, 161, povezano svojstvo, 1137
ulazno/izlazne operacije 170 povezivanje, klase, 440
izmedu zadataka, podupirai, rasporedivaa povratni poziv, 720, 1052
realizovane pom ou BoxLayout, 1059 i unutranje klase, 288
cevi, 976 pohlepni kvantifikatori, 413 prazna finalna polja, 203
UncaughtException- pokaziva, nem a ga u Javi, pranjenje bafera izlaznih
Handler, 916 288 datoteka, 741
uslov za trku, 919 polim orfizam, 26, 211, 238, prebacivanje, konteksta
parametar kolekcije, 565,589 432,482 u paralelnom
parametrizovani tipovi, 484 i konstruktori, 224 izvravanju, 887
perform anse i viekratno otkrivanje preice, s tastature, 1090
i finalnost, 207 tipa, 836 preduslovi za razumevanje
nio, 770 ponaanje polim orfnih knjige, 15
optimizacija, za paralelno m etoda unutar Preferences, API, 802
izvravanje, 1018 konstruktora, 230 prefiksno umanjenje, 72
PhantomReference, 709 poloaj, apsolutan, prilikom prefiksno uveanje, 72
PipelnputStream , 731 rasporeivanja Svving prekidanje ili nastavljanje,
PipedOutputStream, kom ponenata, 1059 obrada izuzetaka, 349
731-732 polja, inicijalizacija u preklapanje
PipedReader, 735, 976 interfejsima, 259 generikih metoda, 553
PipedWriter, 735, 976 polje za potvrdu, 1077 i konstruktori, 117
pisanje imenaa paketa pom eranje sadraja izostanak sakrivanja
velikim slovima, 52 u Swingu, 1055 imena tokom
pisanje imena slinih ponovna podela na proste nasleivanja, 194
grbama kamile, 63 faktore, 159 metoda, 117
Plauger, P.J., 1172 ponovna upotreba operatora + i += za String,
poetni kapacitet, objekata koda, 180 184,393
tipa HashMap ili ponovno generisanje povratnih vrednosti, 123
HashSet, 700 izuzetka, 359 razlikovanje preklopljenih
podaci ponovno transformisanje metoda, 119
finalni, 200 kljueva (heiranje), u odnosu na redefinisanje,
inicijalizacija statinih, 701 194
140 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,
prosti tipovi podataka poreenje perform ansi, 98
i kako se 1019 prenosivost u jezicima C,
upotrebljavaju poruka, slanje, 17 C ++ i Java, 89
sa operatorim a, 89 posesivni kvantifikatori, 4 1 3
Indeks 1193

prepoznavanje tipa u vreme PrintStream, 734 program


izvravanja (RTTI), 237 PrintWriter, 736, 740-742 builder, 1121
ClassCastException, 445 prigodni konstruktor kostur, 290
Constructor, klasa za u Javi SE5, 746 program za ureivanje
refleksiju, 462 prioritet, paralelno teksta, pravljenje
Fiel, klasa, 462 izvravanje, 899 pom ou Swing klase
getConstructor( ), 1071 PriorityBlockingQueue, JTextPane, 1076
instanceof, rezervisana za paralelno izvravanje, program er klijent, 20
re, 445 991 programer, klijent
islnstance( ), 453 PriorityQueue, 330, 657 u odnosu na autora
M ethod, klasa, 462 PriorityQueue, prioritetni biblioteke, 159
newlnstance( ), 1071 red za ekanje, 330, 657 programiranje
objekat tipa Class, 434, prirodni logaritmi, 79 Extreme Programm ing
1071 priruni saveti, 1073 (XP), 1171
prim er obiika, 432 pristup m ultistandarno, 16
raziika izmeu refleksije i, k'asi, 175 objektno orijentisano,
462 kontrola, 159, 178 432
refleksija, 461 krenje kontrole pristupa osnovni koncepti OOP-a,
svoenje nanie koje uva pomou refleksije, 15
tip, 445 476 programi voeni
zloupotreba, 482 paketni i prijateljski dogaajima, 1052
pretraivanje pristup, 169 proizvoa potroa, u
niza, 624 specifikatori, 20, 159, 168 paralelnom izvravanju,
ureivanje i pretraivanje unutar direktorijuma, 965
Listi, 705 preko podrazume- projektni obrazac
preusmeravanje vanog paketa, 171 Adapter, 250, 257, 495,
standardnog unutranja klasa i prava 582,585,631
ulaza/izlaza, 751 pristupa, 268 adapterska metoda, 338
prevodilac unazad, javap, private, rezervisana re, 20, Chain of Responsibility,
393, 479, 520 159, 168, 171, 197, 922 826
prevoenje Java programa, iluzija preklapanja Com mand, 295, 473, 822,
56 privatnih metoda, 893
prijavljivanje greaka u 205 Decorator, 568
knjizi, 14 interfejsi, kada su Fa^ade, 452
prijem nik ugneeni, 261 Factory Method, 262
adapteri, 1065 metode, 232 Flyweight, 635,1043
i dogaaji, 1060 redefinisanje metoda, 221 Iterator, 269, 315
interfejsi, 1064 unutranje klase, 291 Null iterator, 469
prim ena m etode na proces, paralelan, 887 Null objekat, 469
sekvcncu, 377 ProcessBuilder, 732 objekat za prenos
prim ordijalni uitava klasa, ProcessFiles, 878 podataka (idiom
434 produavanje Dostavlja poruka),
printff ), 400 uz ouvanje znaka, 80 487, 632, 684
printStackTrace( ), 357, 359 uz dodavanje nula, 80 Proxy, 465
1 194 Misliti na Javi

Singleton, 177 prostor RandomAccess, interfejs


Strategy, 247, 256, 585, imenski prostori, 160 za kontejnere, 344
606,619, 621,720, prostor problema, 15 RandomAccessFile, 737,
726, 826, 990 prostor reenja, 15 744,755
Template Method, 290, proiriv program, 218 rano vezivanje, 27,214
449, 684, 772, 937, proirivanje klase tokom raspored, kontrolisanje
1025,1030 nasleivanja, 23 pom ou rasporedivaa,
Visitor, 861 proirivanje, u odnosu na 1056
projektovanje, 235 potpuno nasleivanje, rastue angaovanje, teorija,
dodavanje metoda 235 915
projektu, 179 proirujua konverzija, 87 razdvajanje interfejsa
i kompozicija, 233 protected, rezervisana re, i realizacije, 20,174,
i nasleivanje, 233 20, 159,168, 172, 197 1060
projekat biblioteke, 159 i paketni pristup, 197 razliito (!=), 73
promenljiv izgled takoe daje paketni read( ), 730
i ponaanje, 1103 pristup, 174 nio, 755
promenljive protokol, 243 readDouble( ), 743
definisanje, 102 provera granica niza, 147 Reader, 730, 734-735
inicijalizacija u provereni izuzeci, 356, 384 readExternal( ), 785
metodama, 136 pretvaranje u readLine( ), 379, 736, 741,
liste promenljivih neproverene, 388 750
param etara Proxy, projektni obrazac, readObject( ), 781
(nepoznat broj i tip 465 dodavanje metode
argum enata), 150 public, 159, 168-169 interfejsu Serializable,
lokalne, 50 i rezervisana re interface, 791
promene, vektor, 292 243 ReadWriteLock, 1036
PropertyChangeEvent, 1137 klasa i jedinice za realizacija, 18
PropertyDescriptor, 1126 prevoenje, 161 i interfejs, 196, 243
ProptertyVetoException, public, rezervisana re, 20 i razdvajanje od interfejsa,
1137 puno ime, 438 20, 174, 1060
proputeni signali, PushbacklnputStream , 733 sakrivanje, 159, 174,271
u paralelnom PushbackReader, 736 renik, 306
izvravanju, 961 Python, 1 ,3,6, 36,41,572, red za ekanje, 302,319,329,
srosti tipovi 626,888, 1173 657
finalni, 201 performanse, 688
finalni statini prosti sinhronizovan,
tipovi, 202
R u paralelnom
inicijalizacija polja klase, RAD (okruenje za brzo izvravanju, 971
136 razvijanje aplikacija), redefinisanje
poreenje, 73 461 funkcije, 24
tipovi, 44 radio-dugme, 1078 i uiuitrasnjc klase, 297
tipovi podataka, i kako se random ( ), metoda za privatne metode, 221
upotrebljavaju sa generisanje sluajnih u odnosu na preklapanje,
operatorim a, 89 brojeva, 326 194
Indeks 1195

redosled removeActionListener(), Serializable, 781, 785, 789,


inicijalizacije, 139,208, 1128,1134 798, 1130
232 removeXXXListener(), readObject( ), 791
pozivanja konstruktora 1060 w riteO bject(), 791
s nasleivanjem, 224 renameTo( ), 730 serijalizovanje
ReentrantLock, 925, 953 reset( ), 737 defaultReadO bject(), 792
reference resu m e () i uzajamna defaultW riteO bject(),
dodela objekata blokada, 946 792
kopiranjem referenci, re w in d (), 759 i rezervisana re transient,
67 rezervisani kvantifikatori, 789
jednakost referenci 413 i skladitenje objekata, 793
u odnosu na RoShamBo, 836 upravljanje postupkom
jednakost objekata, 73 Rumbaugh, James, 1172 serijalizovanja, 785
null, 46 RuntimeException, 366, zadavanje verzije, 793
otkrivanje tanog tipa 388 servisne niti, 901
na koji upuuje Set, 302, 306, 322,652
referenca na osnovnu
klasu, 434
s matematiki odnosi, 504
poreenje performansi,
Reference, klasa paketa sabiranje, 69 696
java.lang.ref, 709 sakrivanje realizacije, 174 setActionCommand( ),
referenciranje unapred, 138 sakupljanje smea, 129, 131 1090
refleksija, 461, 1061, 1123 dostini objekti, 709 setBorder( ), 1075
i neobavezno i ienje, 191 setErr(PrintStream ), 751
proveravanje tipova, kako radi skuplja, 133 setIcon( ), 1073
387 redosled ienja objekata, setln(InputStream ), 751
i zrna Jave, 1121 194 setLayout( ), 1056
latentni tipovi i generiki samoogranieni tipovi setMnemonic( ), 1090
kod, 576 u generikom kodu, 555 setO ut(PrintStream ), 751
ponitavanje kapsuliranja savrena funkcija za setToolTipText( ), 1073
pom ou, 476 transformisanje shuffle( ), 706
primer, 1070 kljueva, 675 signali, proputeni,
procesor anotacija, 848, Schedu!edExecutor, u paralelnom
854 za paralelno izvravanje, izvravanju, 961
razlika izm edu RTTI 994 Simula-67, programski
i refleksije, 462 seek( ), 737, 744 jezik, 17
regex, paket, 411 sekvenca, prim ena metode simulacija alterskog
registrovane proizvodne na sekvencu, 577 slubenika, 1003
metode, varijanta semafor, 997 SingleThreadExecutor, 895
projektnog obrasca seminari singularni projektni
Factorv Method, 456 javni o Javi, 9 obrazac, 177
regularni izrazi, 408 obuka koju prua sinusoida, 1092
rekurzija, nenam erna, koju MindVievv, Inc., 1166 sirov tip, 513
prouzrokuje metoda SequenceInputStream, 731, siz e(), metoda klase
toString( ), 397 737 ArrayList, 303
1 196 Misliti na Javi

sizeof(), operator rezervisana re, 53, 129 preklapanje operatora +


za odreivanje veliine sinhronizovane statike i +=, 184
koji Java nema, 89 metode, 922 s p lit(), metoda, 247
skok, bezuslovni, 106 statika odredba ureivanje,
skupna inicijalizacija nizova, konstruktora, 143 CASE_INSENSITIVE
146 statiki blok, 143 _ORDER, 720
slanje poruke, 17 u odnosu na dinamiku form at( ), 406
sleep (), u paralelnom proveru tipova, 647 StringBuffer, 731
izvravanju, 897 unutranje klase, 282 StringBufferlnputStream,
Smalltalk, 16 uvoz i nabrojani tipovi, 731
SocketChannel, 773 806 StringBuilder, poredenje
SoftReference, 709 stek, 319,320, 714 s klasom String
softver, konferencija generiki s kojeg se prvo i m etodom to S trin g (),
za razvoj, 9 uzima ono to je 394
SortedMap, 666 na njega poslednje StringReader, 735, 739
SortedSet, 656 stavljeno, 490 StringVVriter, 735
South, BorderLayout, 1056 stil Stroustrup, Bjarne, 158
specifikacija, specifikacija stil program iranja, 63 strukturni tipovi, 572
izuzetaka, 355, 385 pravljenja klasa, 174 sufiksno, 72
specifikator pristupa, 20, STL, C ht, 718 sufiksno umanjenje, 72
159, 168 stop( ) i uzajamna blokada, sufiksno uveanje, 72
specijalizacija, 197 946 sukob
sp lit(), metoda klase String, Strategy, projektni obrazac, tokom heiranja, 675
247, 409 247, 256, 585,606,619, ime, 165
sporedan efekat, 66, 72, 123 621,720, 726, 826, 990 super, 186
sprintf( ), 406 StreamTokenizer, 736 i unutranje klase, 297
SQL kod generisan putem String rezervisana re, 185
anotacija, 850 CASE_INSENSITIVE_ supstitucija
stanje okonanja i ORDER Com parator, nasledivanje u odnosu na
finalize( ), 131 705 proirivanje, 235
standardni ulazni tok, format( ), 406 princip, 25
itanje, 749 indexOf( ), 464 suspend( ) i uzajamna
stateChanged( ), 1095 konverzija pom ou blokada, 946
static, 243 operatora +, 67, 85 suavajua konverzija, 87
finalni statini prosti leksikografsko u odnosu svite, @Unit u odnosu na
tipovi, 202 na abecedno lUnit, 874
i finalnost, 201 uredivanje, 623 svodenje nanie, 200, 236
inicijalizacija, 209,436 m etoda toString( ), 181 koje uva tip, 445
inicijalizacija statinih metode, 398 svoenje navie, 28, 198, 211
podataka, 140 nadovezivanje i interfejs, 245
metoda, 129, 215 operatorom + = ,85 i prepoznavanje tipa
obavezno proveravanje nepromenljivost, 392 u vreme izvravanja,
tipova, 384 podrka za regularne 434
provera tipova, 483 izraze u klasi, 409 i unutranje klase, 271
Indeks 1197

System.out, omotavanje ekvivalencija tipa


svojstvo, 1121
toka u PrintW riter, 750 podataka i klase, 17
indeksirano, 1137
systemNodeForPackage(), generiki tipovi i
nam enska klasa za
API Preferences, 803 kontejneri za
ureivanje svojstava,
bezbedan rad
1137
s tipovima, 303
nam enski spisak svojstava,
izveden, 22
1137
abloni, C++, 485, 513 latentni tipovi, 572,582
ograniena svojstva, 1137
osnovni, 22
povezana svojstva, 1137
otkrivanje tanog tipa na
SWF, Flashov bajtkod
koji upuuje referenca
format, 1138 tabela baze podataka, na osnovnu klasu, 434
Swing, 1045 generisana SQL kodom oznaka tipa u generikom
i paralelno izvravanje, putem anotacija, 850
kodu, 522
1110 takmienje, za brave, parametrizovani, 484
kom ponente, korienje u paralelnom
prosti, 44
HTML-a sa, 1101 izvravanju, 1019 prosti tipovi podataka
model dogaaja, 1059 tastatura i kako se koriste
primeri kom ponenata, navigacija, i Swing, 1046 sa operatorim a, 89
1068 preice, 1090 provera tipova i nizovi, 593
switch Template M ethod, projektni statika provera, 384,483
i enum , 809 obrazac, 290,449, 684, strukturni tipovi, 572
rezervisana re, 112 772,845,850,937,1025, svoenje nanie koje uva
synchronized, rezervisana 1030 tip, 445
re, 921 Teorija rastueg zakljuivanje o tipovima
blok i kritian odeijak, angaovanja, 915
generikih
934 ternarni operator, 84 argumenata, 497
Brajanovo pravilo testiranje To, 646
sinhronizaeije, 922 jedinica na osnovu toArray( ),700
i m etode wait( ) i anotacija @Unit, 864
tok, U /1,730
notifyAll( ),957 jedinice, 184 TooManyListenersExcep-
i nasleivanje, 1135 tehnike, 284 tion, 1130
odluivanje koje metode this, rezervisana re, 125
toString( ), 181
sinhronizovati, 1134 ThreadFactory, namenska smernice za upotrebu klase
red za ekanje, 971 realizacija interfejsa, 902 StringBuilder, 396
sinhronizovani throw, rezervisana re, 347
trajnost, 793
kontejneri, 708 Throvvable, natklasa od laka trajnost, 781
statiko, 922 Exception, 356 traka napredovanja, 1102
SynchronousQueue, Timer, objekat tipa, 964 transferFrom( ), 756
za paralelno TimeUnit, 898, 990 transferTo( ),756
izvravanje, 1008 up transformisanje kljueva
System.arraycopy( ), 617 bezbednost tipova u Javi, (heiranje), 672, 674
System.err, 350, 749 86 i kljuevi za heiranje, 668
System.in, 749 dinamika bezbednost savrena funkcija za, 675
System.out, 749 tipova i kontejneri, spoljno nadovezivanje, 675
562
1198 Misliti na Javi

transient, rezervisana re, D atalnputStream, 733, m a rk (), 737


789 736, 739 m k d irs(), 730
traenje .class datoteka D ataO utput, 737 mreni ulaz/izlaz, 753
tokom uitavanja, 163 DataOutputStream , 734, nove U/I klase (nio), 753
TreeMap, 664, 666, 700 737 O bjectOutputStream, 781
TreeSet, realizacija skupa DeflaterOutputStream, O utputStream , 730, 732
Set, 323, 653, 655-656, 775 OutputStrearhVVriter, 735
696 direktorijum, pravljenje PipedlnputStream , 731
true, 75 direktorijuma i PipedOutputStream,
try, rezervisana re, 194, 368 putanja, 728 731-732
blok try u izuzecima, 348 Externalizable, 785 PipedReader, 735
tryLock( ), zakljuavanje File, klasa, 719, 731,737 PipedWriter, 735
datoteke, 773 File.list(), 719 preusmeravanje
TYPE polje, za literale FileDescriptor, 731 standardnog
prostih klasa, 439 FilelnputReader, 738 ulaza/izlaza, 751
FilelnputStream, 731 primeri jednostavne
FilenameFilter, 719 upotrebe, 737
u FileOutputStream, 732 PrintStream, 734
u/i FileReader, 377, 735 PrintVVriter, 736, 740-742
available( ), 740 FileWriter, 735, 740 PushbacklnputStream,
biblioteka, 719 FilterlnputStream, 731 733
biblioteka za FilterOutputStream, 732 PushbackReader, 736
komprimovanje, 775 FilterReader, 736 RandomAccessFile, 737,
blokiranje i metoda FilterVVriter, 736 744
available( ), 740 GZIPInputStream, 775 read( ), 730
BufferedlnputStream, 733 GZIPOutputStream, 775 readDoublef ), 743
BufferedOutputStream, InflaterlnputStream, 775 Reader, 730, 734-735
734 InputStream, 730 readExternal(), 785
BufferedReader, 377, 736, InputStreamReader, 735 readLine( ), 379, 736, 741,
738 internacionalizacija, 735 750
BufferedVVriter, 736, 740 izlaz, 730 readObject( ), 781
ByteArrayInputStream, izmedu zadataka, renameTo( ), 730
731 realizovane pomou reset( ), 737
ByteArrayOutputStream, cevi, 976 seek( ), 737, 744
732 karakteristike datoteka, SequenceInputStream,
cev, 731 728 731, 737
cevovodi, 745 koje se mogu prekidati, Serializable, 785
CharArrayReader, 735 950 setErr(PrintStream ), 751
CharArrayWriter, 735 laka trajnost, 781 setln(InputStream ), 751
CheckedlnputStream, 775 LineNumberlnputStream, setO ut(PrintStream ), 751
CheckedOutputStream, 733 standardnog ulaznog
775 LineNumberReader, 736 toka, 749
close( ), 738 listanje direktorijuma, StreamTokenizer, 736
D atalnput, 737 719 StringBuffer, 731
Indeks 1 199

StringBufferlnputStream, unapreenje tipova, u int, u m etodam a i oblastima


731 89, 98 vaenja, 273
StringReader, 735, 739 unarni ugneivanje unutar
String\Vriter, 735 minus (-), 71 proizvoljne oblasti
System.err, 749 operator, 80 vaenja, 274
System.in, 749 operatori, 71 upuivanje na objekat
System.out, 749 plus (+), 71 spoljne klase, 270
tipine U/I konfiguracije, UncaughtExceptionHandler, zakljuak, 287
737 klase Thread, 916 upravljanje procesima, 752
transient, rezervisana re, Unicode, 735 uredivanje, 619
789 Unified Modeling Language abecedno, 325
ulaz, 730 (UML, unifikovani jezik i pretraivanje listi, 705
Unicode, 735 za modelovanje), 19,21, leksikografsko, 325
upravljanje postupkom 1172 userNodeForPackage( ),API
serijalizovanja, 785 unmodifiableList( ),m etoda Preferences, 803
w rite (), 730 klase Collections, 648 uslov za trku, u paralelnom
w riteD ouble(), 743 U nsupportedO peration- izvravanju, 919
writeExternal( ), 785 Exception, 647 uslovno prevodenje, 168
writeObject( ), 781 unutranja klasa, 266 uslune metode, klase
Writer, 730, 734-735 anonim na, 721, 1053 java.util.Collections, 701
ZipEntry, 778 generika, 508 uveanje, operator, 72
ZipInputStream, 775 i kod upravljan tabelom, i paralelno izvravanje,
ZipO utputStream , 775 684 920
uitava klasa, 434 i kosturi upravljanja, 290 uzajamna blokada uprkos
uitavanje i niti, 907 izvravanja, 1043
datoteka .class, 163 i redefinisanje, 297 uzajamna blokada,
inicijalizacija i uitavanje i rezervisana re super, u paralelnom
klasa, 208 297 izvravanju, 978
klase, 209, 440 i svodenje navie, 271 uzajamno iskljuivanje
ugneena klasa (statina i Swing, 1060 (mutex), u paralelnom
unutranja klasa), 282 identifikatori i .class izvravanju, 921
ugneivanje interfejsa, 260 datoteke, 300 uzorak, regularnog izraza,
ugradivanje poziva metoda lokalna, 274 412
direktno u kod, 204 motivacija, 285
uklapanje konstante, 201 nasleivanje, 296
V
ulanani izuzeci, 362, 388 povratni poziv, 288
ulazno/izlazneoperacijekoje prava pristupa, 268 values( ), za nabrojane
se mogu prekidati, 950 privatna, 291 tipove, 805, 810
umanjenje, operator, 72 skrivena referenca objekta Varga, Ervin, 5, 951
UMI okruujue klase, Vector, 694, 713
jezik za modelovanje, 19, 269 veera filozola, prim er
21, 1172 statine unutranje klase, uzajamne blokade
pokazivanje kompozicije, 282 u paralelnom
21 izvravanju, 978
12 0 0 Misliti na Javi

vee ili jednako (>=), 73 vraanje rezultata Y


vee od (>), 73 i finally, 371
yPos, 797-798
vektor prom ene, 292 kovarijantni povratni
veliina, objekata tipa tipovi, 232, 559
HashM ap ili HashSet, povratna vrednost z
700 konstruktora, 117 zadavanje verzije,
Venners, Bill, 131 preklapanje povratnih serijalizovanje, 793
vertikalna kom patibilnost, vrednosti, 123 zadravanje pokazivaa mia
516 u obliku niza, 597 iznad dugmeta bez
vezivanje vraanje vie objekata, pritiskanja, 1073
dinamiko, 214 487 zahtev, u OOP, 17
dinamiko, kasno, ili vrednost, spreavanje zajedniki interfejs, 239
vezivanje prilikom izmena u vreme zakljuak, i unutranje klase,
izvravanja, 211 izvravanja, 200 288
kasno, 27, 214 zakljuavanje, datoteke,
poziva metoda, 214
prilikom izvravanja, 214
w 772-773
zakljuivanje, o tipu
rano, 27 w a it(), 957
generikog argumenta,
vezivna funkcija, 476 Waldrop, M. Mitchell, 1173
497
Visitor (projektni obrazac) WeakHashMap, 664, 712
zamenljivost, u OOP, 17
i anotacije, API mirror, VVeakReference, 709
zapisivanje, ugradnja
861 Web Start, Java, 1105
u izuzetke, 352
Visual BASIC, Microsoftov, West, BorderLayout, 1056
zarez kao operator, 103
1120
while, 100 zauzetost ekanjem,
viedimenzionalni nizovi, w indow C losing(), 1096
u paralelnom
599 write( ), 730
izvravanju, 957
viekratno otkrivanje tipa nio, 756 ZipEntry, 778
i nabrojani tipovi, 836 writeDouble( ), 743
ZipInputStream, 775
pom ou m ape EnumMap, writeExternal( ), 785
ZipOutputStream, 775
842 writeObject( ), 781
zrna Jave
vieprogramski rad, 887 dodavanje m etode alatka za pravljenje
viesmerni, 1130 interfejsu Serializable,
aplikacija, 1121
dogaaj, i zrna Jave, 1130 791
dogadaji, 1121
viestruko nasleivanje, Writer, 730, 734-735 EventSetDescriptor, 1126
u jezicima C ++ i Java, FeatureDescriptor, 1137
251 X getBeanInfo( ), 1124
viestruko ugneena klasa, getEventSetDescriptors( ),
XDoclet, 845
284 1126
XML, 799
vizuelno programiranje, XOM, XML biblioteka, 799 getM ethodDescriptors( ),
11 20 I 126
XOR uskljuiva
okruenja, 1046 disjunkcija), 80 getName( ), 1126
volatile, modifikator, 918, getPropertyD escriptors(),
xPos, 797-798
926, 930 1126
Indeks 1201

getPropertyType( ), 1126 manifest datoteka, 1135 pravilo za imenovanje,


getReadMethod( ), 1126 Method, 1126 1122
getW riteM ethod( ), 1126 MethodDescriptor, 1126 PropertyChangeEvent,
i Borlandov Delphi, 1120 namenska klasa Beanlnfo, 1137
i Microsoftov Visual 1137 PropertyDescriptor, 1126
BASIC, 1120 namenska klasa ProptertyVetoException,
indeksirano svojstvo, 1137 za ureivanje 1137
Introspector, 1124 svojstava, 1137 refleksija, 1121, 1123
JAR datoteke za namenski spisak svojstva, Serializable, 1130
arhiviranje, 1135 1137 svojstva, 1121
kom ponenta, 1121 ograniena svojstva, 1137 vizuelno program iranje,
1120

You might also like